123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139type'at=|Leaf|Nodeofint*'at*'a*'atletsize=function|Node(s,_,_,_)->s|Leaf->0(** {1 Balance criteria}
Functions are not symmetric.
The first argument should always be of the same power of two or smaller
(guaranteed by construction). *)(** [smaller_ell smin smax] iff
- [smin] is less than [smax]
- [smin] and [smax] differs by less than two magnitude orders, i.e
msbs(smin) >= msbs(smax) - 1
where msbs is the index of the most significant bit set *)letsmaller_ellsminsmax=(smin<smax)&&((sminlandsmax)lsl1<smax)(** [disbalanced smin smax] check if two sub-trees of size [smin] and [smax],
are disbalanced. That is, msbs(smin) < msbs(smax) - 1 *)letdisbalancedsminsmax=smaller_ellsmin(smaxlsr1)(** {1 Smart but not too much constructors} *)(** Construct node and check balance
let node_ l x r =
let sl = size l and sr = size r in
if sl < sr then
assert (not (disbalanced sl sr))
else
assert (not (disbalanced sr sl));
Node (sl + 1 + sr, l, x, r)
*)(** Construct Node *)letnode_sllxsrr=Node(sl+1+sr,l,x,r)(** Rotations *)letrot_leftlxrk=matchrwith|Node(_,rl,y,rr)->k(klxrl)yrr|_->assertfalseletrot_rightlyrk=matchlwith|Node(_,ll,x,lr)->kllx(klryr)|_->assertfalse(** Balancing *)letinc_leftlxrk=letr=matchrwith|Node(_,rl,y,rr)whensmaller_ell(sizerr)(sizerl)->rot_rightrlyrrk|_->rinrot_leftlxrkletinc_rightlyrk=letl=matchlwith|Node(_,ll,x,lr)whensmaller_ell(sizell)(sizelr)->rot_leftllxlrk|_->linrot_rightlyrk(** Balance a node in both directions: a rotation that fixes one side can
leave the other side too heavy, so the nodes it builds are balanced the
same way. *)letrecnode_balancelxr=letsl=sizelandsr=sizerin(* Fast path: [disbalanced a b] requires [a < b / 2]. *)ifsr<=2*sl+1&&sl<=2*sr+1thennode_sllxsrrelseifdisbalancedslsrtheninc_leftlxrnode_balanceelseifdisbalancedsrsltheninc_rightlxrnode_balanceelsenode_sllxsrr(** [link l x r] joins balanced trees [l] and [r] of any sizes around [x],
descending the spine of the larger one until the sizes are compatible.
Cost is O(log (max sl sr / min sl sr)). *)letreclinklxr=letsl=sizelandsr=sizerin(* Fast path: [disbalanced a b] requires [a < b / 2]. *)ifsr<=2*sl+1&&sl<=2*sr+1thennode_sllxsrrelseifdisbalancedslsrthenmatchrwith|Node(_,rl,y,rr)->node_balance(linklxrl)yrr|Leaf->assertfalseelseifdisbalancedsrslthenmatchlwith|Node(_,ll,y,lr)->node_balancelly(linklrxr)|Leaf->assertfalseelsenode_sllxsrr(** Remove the last element, returned with the remaining tree. *)letrecpop_max=function|Node(_,l,x,Leaf)->l,(x)|Node(_,l,x,r)->letr,m=pop_maxrinnode_balancelxr,m|Leaf->assertfalse(** Remove the first element, returned with the remaining tree. *)letrecpop_min=function|Node(_,Leaf,x,r)->(x),r|Node(_,l,x,r)->letm,l=pop_minlinm,node_balancelxr|Leaf->assertfalse(** Public interface *)letleaf=Leafletnodelxr=linklxr(* The root of the result is the last element of [l] or the first of [r],
taken from the smaller tree. *)letjoinlr=matchl,rwith|Leaf,t|t,Leaf->t|Node(sl,_,_,_),Node(sr,_,_,_)->ifsl<=srthenletl,(x)=pop_maxlinlinklxrelselet(x),r=pop_minrinlinklxrletrecrankn=function|Leaf->raiseNot_found|Node(_,l,x,r)->letsl=sizelinifn=slthenxelseifn<slthenranknlelserank(n-1-sl)r