123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140type(+'a,+'b)t=|Leaf|Nodeofint*('a,'b)t*'a*'b*('a,'b)tletsize=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 x0 x1 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, x0, x1, r)
*)(** Construct Node *)letnode_sllx0x1srr=Node(sl+1+sr,l,x0,x1,r)(** Rotations *)letrot_leftlx0x1rk=matchrwith|Node(_,rl,y0,y1,rr)->k(klx0x1rl)y0y1rr|_->assertfalseletrot_rightly0y1rk=matchlwith|Node(_,ll,x0,x1,lr)->kllx0x1(klry0y1r)|_->assertfalse(** Balancing *)letinc_leftlx0x1rk=letr=matchrwith|Node(_,rl,y0,y1,rr)whensmaller_ell(sizerr)(sizerl)->rot_rightrly0y1rrk|_->rinrot_leftlx0x1rkletinc_rightly0y1rk=letl=matchlwith|Node(_,ll,x0,x1,lr)whensmaller_ell(sizell)(sizelr)->rot_leftllx0x1lrk|_->linrot_rightly0y1rk(** 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_balancelx0x1r=letsl=sizelandsr=sizerin(* Fast path: [disbalanced a b] requires [a < b / 2]. *)ifsr<=2*sl+1&&sl<=2*sr+1thennode_sllx0x1srrelseifdisbalancedslsrtheninc_leftlx0x1rnode_balanceelseifdisbalancedsrsltheninc_rightlx0x1rnode_balanceelsenode_sllx0x1srr(** [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)). *)letreclinklx0x1r=letsl=sizelandsr=sizerin(* Fast path: [disbalanced a b] requires [a < b / 2]. *)ifsr<=2*sl+1&&sl<=2*sr+1thennode_sllx0x1srrelseifdisbalancedslsrthenmatchrwith|Node(_,rl,y0,y1,rr)->node_balance(linklx0x1rl)y0y1rr|Leaf->assertfalseelseifdisbalancedsrslthenmatchlwith|Node(_,ll,y0,y1,lr)->node_balancelly0y1(linklrx0x1r)|Leaf->assertfalseelsenode_sllx0x1srr(** Remove the last element, returned with the remaining tree. *)letrecpop_max=function|Node(_,l,x0,x1,Leaf)->l,(x0,x1)|Node(_,l,x0,x1,r)->letr,m=pop_maxrinnode_balancelx0x1r,m|Leaf->assertfalse(** Remove the first element, returned with the remaining tree. *)letrecpop_min=function|Node(_,Leaf,x0,x1,r)->(x0,x1),r|Node(_,l,x0,x1,r)->letm,l=pop_minlinm,node_balancelx0x1r|Leaf->assertfalse(** Public interface *)letleaf=Leafletnodelx0x1r=linklx0x1r(* 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,(x0,x1)=pop_maxlinlinklx0x1relselet(x0,x1),r=pop_minrinlinklx0x1rletrecrankn=function|Leaf->raiseNot_found|Node(_,l,x0,x1,r)->letsl=sizelinifn=slthenx0,x1elseifn<slthenranknlelserank(n-1-sl)r