123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236(* bidirectional ordered chained lists (mutable) *)letdebug=!B_utils.debug;;exceptionMax_insert;;type'aelement={id:int;(* identifies the connected component *)mutablevalue:'a;mutabledepth:int;(* depth is a redundant information, in order to get
faster comparison between chains. The rule is that
the .next element must have higher depth. A
consequence is that the number of elements cannot
exceed max_int - 2 (here = 4611686018427387901) *)mutableprev:('aelement)option;mutablenext:('aelement)option}letnew_id=B_utils.fresh_int();;type'at='aelementoption(* None = empty chain *)(* The only non trivial (fun) part of the implementation is to decide what
"depth" should be attributed to an element when adding or inserting a new
element in a chain. In our implementation, the two directions "prev" and
"next" are not symmetric. In a symmetric implementation, each insertion would
cut the depth interval in two equal parts, hence, since max_int = 2^62, in
the (very) worst case, we can roughly only insert 62 elements to a chain
before we need to reattribute depths. Here we decide that "insert_after" is
more common than "insert_before" -- we will use the Chains for graphic
layers, and it's more usual to add a layer on top of the previous one rather
than inserting a layer "below" an existing one.
So, when appending a new element, we simply add a constant value to the
depth: sqrt(max_int). Therefore we may append sqrt(max_int) elements in a
row. *)letdx=ifdebugthen10elseint_of_float(sqrt(floatmax_int));;(* Since max_int = 4611686018427387903 on a 64bits machine, dx = 2147483648 *)letsingletonvalue=Some{id=new_id();value;depth=dx;prev=None;next=None}letdo_optionof=matchowith|Somex->fx|None->();;letnext=function|None->None|Somet->t.next;;letprev=function|None->None|Somet->t.prev;;letvalue=function|None->raiseNot_found|Somea->a.value;;letdepth=function|None->raiseNot_found|Somea->a.depth;;letrecfirst=function|None->raiseNot_found|Somea->matcha.prevwith|None->Somea|b->firstb;;letreclast=function|None->raiseNot_found|Somea->matcha.nextwith|None->Somea|b->lastb;;letsame_componentt1t2=matcht1,t2with|None,None|None,Some_|Some_,None->true|Somex1,Somex2->x1.id=x2.id;;letcomp(x:int)(y:int)=Stdlib.comparexy;;letcomparet1t2=matcht1,t2with|None,None->(* print_endline "None to compare"; *)0|Some_,None|None,Some_->raiseNot_found|Somex1,Somex2->(ifx1.id<>x2.idthenfailwith"Cannot compare chains in different \
components"(* print_endline (Printf.sprintf "depths=%d,%d" x1.depth x2.depth); *)elsecompx1.depthx2.depth);;let(==)t1t2=comparet1t2=0;;(* t1 > t2 if t1.depth > t2.depth. So ">" means "deeper than". *)let(>)t1t2=comparet1t2>0;;letsizet=letrecloopti=matchtwith|None->i|Somet->loopt.next(i+1)inloop(firstt)0;;(* redistribute depth values *)letevenizet=letdx=max_int/(sizet+2)inifdx=0thenfailwith"Chain too large"(* in principe this cannot happen *)elseletrecloopdt=matchtwith|None->()|Somea->a.depth<-d;loop(d+dx)a.nextinloopdx(firstt);;(* the return value points to the inserted element *)letinsert_aftertvalue=letn=nexttinletid,depth=matchtwith|None->new_id(),dx|Somex->matchnwith|None->x.id,x.depth+dx|Somex'->letd=x'.depth-x.depthinifd<2thenraiseMax_insert(* TODO: en fait on peut encore décaler le suivant ! *)elsex.id,x.depth+d/2inlett'=Some{id;value;depth;prev=t;next=n}inB_utils.(printddebug_memory"New layer created with depth: %u\n"depth);do_optiont(funx->x.next<-t');do_optionn(funx->x.prev<-t');t';;letinsert_aftertvalue=tryinsert_aftertvaluewith|Max_insert->B_utils.(printddebug_memory"Need to evenize chain...");evenizet;insert_aftertvalue|e->raisee;;letinsert_beforetvalue=letp=prevtinletid,depth=matchtwith|None->new_id(),dx|Somex->letd'=matchpwith|None->0|Somex'->x'.depthinletd=x.depth-d'inifd<2thenraiseMax_insert(* TODO: en fait on peut encore décaler le suivant ! *)elsex.id,x.depth-d/2inlett'=Some{id;value;depth;prev=p;next=t}inPrintf.printf"New layer created with depth: %u\n"depth;do_optiont(funx->x.prev<-t');do_optionp(funx->x.next<-t');t';;letinsert_beforetvalue=tryinsert_beforetvaluewith|Max_insert->B_utils.(printddebug_memory"Need to evenize chain...");evenizet;insert_beforetvalue|e->raisee;;letreplacetvalue=matchtwith|None->raiseNot_found|Somea->a.value<-value;;letto_listt=letrecloopxlist=matchxwith|None->list|Somea->loopa.prev(a.value::list)inloop(lastt)[];;(* the return value points to the last element of the list *)letof_listlist:'at=letid=new_id()inlett,_=List.fold_left(fun(t,depth)value->lett'=Some{id;value;depth;next=None;prev=t}indo_optiont(funb->b.next<-t');(t',depth+dx))(None,dx)listint;;(* iter on values (not elements) starting from the given position *)letreciter_downf=function|None->()|Somea->fa.value;iter_downfa.next;;(* iter on values (not elements) of the whole chain *)letiterft=iter_downf(firstt);;letreciter_upf=function|None->()|Somea->fa.value;iter_upfa.prev;;letiter_upft=iter_upf(lastt);;(* iter on 'real elements' (no option) *)letreciter_elements_downf=function|None->()|Somea->fa;iter_elements_downfa.next;;letiter_elementsft=iter_elements_downf(firstt);;letfilltvalue=iter_elements(funt->t.value<-value)t;;letinsert_chain_before~dstt=iter(funv->ignore(insert_beforedstv))t;;(* of course this could be done more efficiently = in constant time if we didn't
have to compute depth, or if the depths of the subchain were strictly included
between t and (next t) *)letinsert_chain_after~dstt=iter_up(funv->ignore(insert_afterdstv))t;;letprint_depthst=iter_elements(funa->Printf.printf"depth=%d\n"a.depth)t;;