123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145type'acell={content:'a;weight:int;mutableprev:'acell;mutablenext:'acell}typestats={mutabletotal_cap:int;mutablepromote_count:int;mutableadd_count:int;mutablediscard_count:int;mutableadd_size:int;mutablediscarded_size:int}type'adbll=|Nilofint|Listof{first:'acell;last:'acell;size:int;cap:int}type'at={mutabledbll:'adbll;stats:stats}exceptionAction_on_empty_listofstringletpp_statst=letsize=matcht.dbllwith|Nil_->0|Listl->l.sizeinPrintf.eprintf"total_cap \t\t: %d\n\
size \t\t: %d\n\
promote_count \t: %d\n\
add_count \t\t: %d\n\
discard_count \t: %d\n\
add_size \t\t: %d\n\
discard_size \t: %d\n\
volume_conservation \t: %d = %d + %d : %b\n\
%!"t.stats.total_capsizet.stats.promote_countt.stats.add_countt.stats.discard_countt.stats.add_sizet.stats.discarded_sizet.stats.add_sizet.stats.discarded_sizesize(t.stats.add_size=t.stats.discarded_size+size)letcreatecap=letstats={total_cap=cap;promote_count=0;add_count=0;discard_count=0;add_size=0;discarded_size=0}in{dbll=Nilcap;stats}letadd_frontt(v,w)=t.stats.add_count<-t.stats.add_count+1;t.stats.add_size<-t.stats.add_size+w;matcht.dbllwith|Nilcap->letrecc={content=v;weight=w;prev=c;next=c}int.dbll<-List{first=c;last=c;size=w;cap};c|Listl->letrecnew_first={content=v;weight=w;prev=new_first;next=l.first}inl.first.prev<-new_first;t.dbll<-List{first=new_first;last=l.last;size=l.size+w;cap=l.cap};new_firstletdiscardt=t.stats.discard_count<-t.stats.discard_count+1;matcht.dbllwith|Nil_->raise(Action_on_empty_list"Unable to discard the last element, the doubly linked list is empty.")|Listl->ifl.first==l.lastthen(t.dbll<-Nill.cap;t.stats.discarded_size<-t.stats.discarded_size+l.last.weight;l.last.content)elseletdiscarded_value=l.last.contentinletdiscarded_weight=l.last.weightint.stats.discarded_size<-t.stats.discarded_size+discarded_weight;letnew_last=l.last.previn(* TODO Should we explicitely disconnect last's pointers ? *)new_last.next<-new_last;(* TODO Int.max 0 (l.size - discarded_weight) does seems useless.
We could use an assert to check it. *)(* Unlinking the discaded cell is not strictly necessary but not doing it
could lead to memory leaks if the user of the cache keeps a reference
to the cell. *)l.last.next<-l.last;l.last.prev<-l.last;t.dbll<-List{first=l.first;last=new_last;size=Int.max0(l.size-discarded_weight);cap=l.cap};discarded_valueletdiscard_sizets=(* this is fold not iter *)letreciteracct=matcht.dbllwith|Nil_->acc|Listl->ifl.size+s<=l.capthenaccelseiter(discardt::acc)tiniter[]tletpromotetc=t.stats.promote_count<-t.stats.promote_count+1;matcht.dbllwith|Nil_->raise(Action_on_empty_list"Unable to promote a cell, the doubly linked list is empty.")|Listl->ifl.first==cthen()elseifl.last==cthen(letnew_last=l.last.previnnew_last.next<-new_last;letnew_first=cinnew_first.next<-l.first;new_first.prev<-new_first;l.first.prev<-new_first;t.dbll<-List{first=new_first;last=new_last;size=l.size;cap=l.cap})elseletvoisin_prev=c.previnletvoisin_next=c.nextinvoisin_prev.next<-voisin_next;voisin_next.prev<-voisin_prev;letnew_first=cinnew_first.prev<-new_first;new_first.next<-l.first;l.first.prev<-new_first;t.dbll<-List{first=new_first;last=l.last;size=l.size;cap=l.cap}letgetc=c.content