123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189(* Copyright 2003 Yamagata Yoriyuki. distributed with LGPL *)(* Modified by Edgar Friendly <thelema314@gmail.com> *)type'atree=|Empty|Nodeof'atree*'a*'atree*int(* height *)letempty=Emptyletis_empty=function|Empty->true|Node_->falseletsingleton_treex=Node(Empty,x,Empty,1)letleft_branch=function|Empty->raiseNot_found|Node(l,_,_,_)->lletright_branch=function|Empty->raiseNot_found|Node(_,_,r,_)->rletroot=function|Empty->raiseNot_found|Node(_,v,_,_)->vletheight=function|Empty->0|Node(_,_,_,h)->hletcreatelvr=leth'=1+BatInt.max(heightl)(heightr)inassert(abs(heightl-heightr)<2);Node(l,v,r,h')(* Assume |hl- hr| < 3 *)letballvr=lethl=heightlinlethr=heightrinifhl>= hr+2thenmatchlwith|Empty->assertfalse|Node(ll,lv,lr,_)->ifheightll>=heightlrthencreatelllv(createlrvr)elsematchlrwith|Empty->assertfalse|Node(lrl,lrv,lrr,_)->create(createlllvlrl)lrv(createlrrvr)elseifhr>=hl+2thenmatchrwith|Empty->assertfalse|Node(rl,rv,rr,_)->ifheightrr>=heightrlthencreate(createlvrl)rvrrelsematch rlwith|Empty->assertfalse|Node(rll,rlv,rlr,_)->create(createlvrll)rlv(createrlrrvrr)elsecreatelvrletrecadd_leftv=function|Empty->Node(Empty,v,Empty,1)|Node(l,v',r,_)->bal(add_leftvl)v'rletrecadd_rightv=function|Empty->Node(Empty,v,Empty,1)|Node(l,v',r,_)->ballv'(add_rightvr)(*No assumption ofheight of l and r. *)letrecmake_treelvr=matchl,rwith|Empty,_->add_leftvr|_,Empty->add_rightvl|Node(ll,lv,lr,lh),Node(rl,rv,rr,rh)->iflh>rh+1thenballllv(make_treelrvr)elseifrh>lh+1thenbal(make_treelvrl)rvrrelsecreatelvr(* Generate pseudo-random trees in an imbalanced fashion using function [f].
The trees generated are determined solely by the input list. *)(*${*)letrecof_list_for_testf=function|[]->empty|h::t->letlen=BatList.lengthtinlet(l,r)=BatList.split_at(abs(hmod(len+1)))tinf(of_list_for_test fl)h(of_list_for_testfr)(*$}*)(* This teststhree aspects of [make_tree] and the rebalancing algorithm:
- The height value in a node is accurate.
- The height of two subnodes differs at most by one (main AVL tree invariant).
- All elements put into a tree stay in a tree even if it is rebalanced.
*)(*$Q make_tree & ~small:List.length
(Q.list Q.small_int) (fun l -> \
let t = of_list_for_test make_tree l in \
check_height_cache t && check_height_balance t \
)
(Q.list Q.small_int) (fun l -> \
let t = of_list_for_test make_tree l in \
(enum t |> List.of_enum |> List.sort compare) = List.sort compare l \
)
*)(* Utilities *)letrecsplit_leftmost=function|Empty->raiseNot_found|Node(Empty,v,r,_)->(v,r)|Node(l,v,r,_)->letv0,l'=split_leftmostlin(v0,make_treel'vr)letrecsplit_rightmost=function|Empty->raiseNot_found|Node(l,v,Empty,_)->(v,l)|Node(l,v,r,_)->letv0,r'=split_rightmostrin(v0,make_treelvr')letrecconcatt1t2=matcht1,t2with|Empty,_-> t2|_,Empty->t1|Node(l1,v1,r1,h1),Node(l2,v2,r2,h2)->ifh1<h2thenmake_tree(concat t1l2)v2r2elsemake_tree l1v1(concatr1t2)letreciterproc=function|Empty ->()|Node(l,v,r,_)->iterprocl;procv;iterprocrletrecfoldftinit=matchtwith|Empty ->init|Node(l,v,r,_)->letx=foldflinitinletx=fvxinfoldfrx(*FIXME: this isnlog n because of the left nesting of appends *)letrecenum=function|Empty->BatEnum.empty()|Node(l,v,r,_)->BatEnum.append(enuml)(BatEnum.delay(fun()->BatEnum.append (BatEnum.singletonv)(enumr)))(* Helpers for testing *)(* Check that the height value in a node is correct. *)letcheck_height_cachet=letrecgo=function|Empty->Some0|Node(l,_,r,h)->letopenBatOption.Monadinbind(gol)(fun lh->bind (gor)(funrh->ifmaxlhrh+1=hthenSomehelseNone))inBatOption.is_some(got)(* Check that the difference of the height of the left and right subnode is 0
or 1 based on the height value in the nodes. *)letcheck_height_balancet=letbalancedlr=match(l,r)with|(Node(_,_,_,hl),Node(_,_,_,hr))-> abs(hl-hr)<2|_-> trueinletrecgo=function|Empty->true|Node(l,_,r,_)->gol&&gor&&balancedlringot(* Sanitychecks *)letcheckt=check_height_cachet&&check_height_balancet