Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file avltree.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539(* A few small things copied from other parts of Base because they depend on us, so we
can't use them. *)open!Importletraise_s=Error.raise_smoduleInt=structtypet=intletmax(x:t)y=ifx>ythenxelseyend(* Its important that Empty have no args. It's tempting to make this type a record
(e.g. to hold the compare function), but a lot of memory is saved by Empty being an
immediate, since all unused buckets in the hashtbl don't use any memory (besides the
array cell) *)type('k,'v)t=|Empty|Nodeof{mutableleft:('k,'v)t;key:'k;mutablevalue:'v;mutableheight:int;mutableright:('k,'v)t}|Leafof{key:'k;mutablevalue:'v}letempty=Emptyletis_empty=function|Empty->true|Leaf_|Node_->false;;letheight=function|Empty->0|Leaf_->1|Node{left=_;key=_;value=_;height;right=_}->height;;letinvariantcompare=letlegal_left_keykey=function|Empty->()|Leaf{key=left_key;value=_}|Node{left=_;key=left_key;value=_;height=_;right=_}->assert(compareleft_keykey<0)inletlegal_right_keykey=function|Empty->()|Leaf{key=right_key;value=_}|Node{left=_;key=right_key;value=_;height=_;right=_}->assert(compareright_keykey>0)inletrecinv=function|Empty|Leaf_->()|Node{left;key=k;value=_;height=h;right}->lethl,hr=heightleft,heightrightininvleft;invright;legal_left_keykleft;legal_right_keykright;assert(h=Int.maxhlhr+1);assert(abs(hl-hr)<=2)ininv;;letinvariantt~compare=invariantcomparet(* In the following comments,
't is balanced' means that 'invariant t' does not
raise an exception. This implies of course that each node's height field is
correct.
't is balanceable' means that height of the left and right subtrees of t
differ by at most 3. *)(* @pre: left and right subtrees have correct heights
@post: output has the correct height *)letupdate_height=function|Node({left;key=_;value=_;height=old_height;right}asx)->letnew_height=Int.max(heightleft)(heightright)+1inifnew_height<>old_heightthenx.height<-new_height|Empty|Leaf_->assertfalse;;(* @pre: left and right subtrees are balanced
@pre: tree is balanceable
@post: output is balanced (in particular, height is correct) *)letbalancetree=matchtreewith|Empty|Leaf_->tree|Node({left;key=_;value=_;height=_;right}asroot_node)->lethl=heightleftandhr=heightrightin(* + 2 is critically important, lowering it to 1 will break the Leaf
assumptions in the code below, and will force us to promote leaf nodes in
the balance routine. It's also faster, since it will balance less often.
Note that the following code is delicate. The update_height calls must
occur in the correct order, since update_height assumes its children have
the correct heights. *)ifhl>hr+2then(matchleftwith(* It cannot be a leaf, because even if right is empty, a leaf
is only height 1 *)|Empty|Leaf_->assertfalse|Node({left=left_node_left;key=_;value=_;height=_;right=left_node_right}asleft_node)->ifheightleft_node_left>=heightleft_node_rightthen(root_node.left<-left_node_right;left_node.right<-tree;update_heighttree;update_heightleft;left)else((* if right is a leaf, then left must be empty. That means
height is 2. Even if hr is empty we still can't get here. *)matchleft_node_rightwith|Empty|Leaf_->assertfalse|Node({left=lr_left;key=_;value=_;height=_;right=lr_right}aslr_node)->left_node.right<-lr_left;root_node.left<-lr_right;lr_node.right<-tree;lr_node.left<-left;update_heightleft;update_heighttree;update_heightleft_node_right;left_node_right))elseifhr>hl+2then((* see above for an explanation of why right cannot be a leaf *)matchrightwith|Empty|Leaf_->assertfalse|Node({left=right_node_left;key=_;value=_;height=_;right=right_node_right}asright_node)->ifheightright_node_right>=heightright_node_leftthen(root_node.right<-right_node_left;right_node.left<-tree;update_heighttree;update_heightright;right)else((* see above for an explanation of why this cannot be a leaf *)matchright_node_leftwith|Empty|Leaf_->assertfalse|Node({left=rl_left;key=_;value=_;height=_;right=rl_right}asrl_node)->right_node.left<-rl_right;root_node.right<-rl_left;rl_node.left<-tree;rl_node.right<-right;update_heightright;update_heighttree;update_heightright_node_left;right_node_left))else(update_heighttree;tree);;(* @pre: tree is balanceable
@pre: abs (height (right node) - height (balance tree)) <= 3
@post: result is balanceable *)(* @pre: tree is balanceable
@pre: abs (height (right node) - height (balance tree)) <= 3
@post: result is balanceable *)letset_leftnodetree=lettree=balancetreeinmatchnodewith|Node({left;key=_;value=_;height=_;right=_}asr)->ifphys_equallefttreethen()elser.left<-tree;update_heightnode|_->assertfalse;;(* @pre: tree is balanceable
@pre: abs (height (left node) - height (balance tree)) <= 3
@post: result is balanceable *)letset_rightnodetree=lettree=balancetreeinmatchnodewith|Node({left=_;key=_;value=_;height=_;right}asr)->ifphys_equalrighttreethen()elser.right<-tree;update_heightnode|_->assertfalse;;(* @pre: t is balanced.
@post: result is balanced, with new node inserted
@post: !added = true iff the shape of the input tree changed. *)letadd=letrecaddtreplaceaddedcomparekv=matchtwith|Empty->added:=true;Leaf{key=k;value=v}|Leaf({key=k';value=_}asr)->letc=comparek'kin(* This compare is reversed on purpose, we are pretending
that the leaf was just inserted instead of the other way
round, that way we only allocate one node. *)ifc=0then(added:=false;ifreplacethenr.value<-v;t)else(added:=true;ifc<0thenNode{left=t;key=k;value=v;height=2;right=Empty}elseNode{left=Empty;key=k;value=v;height=2;right=t})|Node({left;key=k';value=_;height=_;right}asr)->letc=comparekk'inifc=0then(added:=false;ifreplacethenr.value<-v)elseifc<0thenset_leftt(addleftreplaceaddedcomparekv)elseset_rightt(addrightreplaceaddedcomparekv);tinfunt~replace~compare~added~key~data->lett=addtreplaceaddedcomparekeydatainif!addedthenbalancetelset;;letrecfirstt=matchtwith|Empty->None|Leaf{key=k;value=v}|Node{left=Empty;key=k;value=v;height=_;right=_}->Some(k,v)|Node{left=l;key=_;value=_;height=_;right=_}->firstl;;letreclastt=matchtwith|Empty->None|Leaf{key=k;value=v}|Node{left=_;key=k;value=v;height=_;right=Empty}->Some(k,v)|Node{left=_;key=_;value=_;height=_;right=r}->lastr;;let[@inlinealways]recfindi_and_call_implt~comparekarg1arg2~call_if_found~call_if_not_found~if_found~if_not_found=matchtwith|Empty->call_if_not_found~if_not_foundkarg1arg2|Leaf{key=k';value=v}->ifcomparekk'=0thencall_if_found~if_found~key:k'~data:varg1arg2elsecall_if_not_found~if_not_foundkarg1arg2|Node{left;key=k';value=v;height=_;right}->letc=comparekk'inifc=0thencall_if_found~if_found~key:k'~data:varg1arg2elsefindi_and_call_impl(ifc<0thenleftelseright)~comparekarg1arg2~call_if_found~call_if_not_found~if_found~if_not_found;;letfind_and_call=letcall_if_found~if_found~key:_~data()()=if_founddatainletcall_if_not_found~if_not_foundkey()()=if_not_foundkeyinfunt~comparek~if_found~if_not_found->findi_and_call_implt~comparek()()~call_if_found~call_if_not_found~if_found~if_not_found;;letfindi_and_call=letcall_if_found~if_found~key~data()()=if_found~key~datainletcall_if_not_found~if_not_foundkey()()=if_not_foundkeyinfunt~comparek~if_found~if_not_found->findi_and_call_implt~comparek()()~call_if_found~call_if_not_found~if_found~if_not_found;;letfind_and_call1=letcall_if_found~if_found~key:_~dataarg()=if_founddataarginletcall_if_not_found~if_not_foundkeyarg()=if_not_foundkeyarginfunt~comparek~a~if_found~if_not_found->findi_and_call_implt~compareka()~call_if_found~call_if_not_found~if_found~if_not_found;;letfindi_and_call1=letcall_if_found~if_found~key~dataarg()=if_found~key~dataarginletcall_if_not_found~if_not_foundkeyarg()=if_not_foundkeyarginfunt~comparek~a~if_found~if_not_found->findi_and_call_implt~compareka()~call_if_found~call_if_not_found~if_found~if_not_found;;letfind_and_call2=letcall_if_found~if_found~key:_~dataarg1arg2=if_founddataarg1arg2inletcall_if_not_found~if_not_foundkeyarg1arg2=if_not_foundkeyarg1arg2infunt~comparek~a~b~if_found~if_not_found->findi_and_call_implt~comparekab~call_if_found~call_if_not_found~if_found~if_not_found;;letfindi_and_call2=letcall_if_found~if_found~key~dataarg1arg2=if_found~key~dataarg1arg2inletcall_if_not_found~if_not_foundkeyarg1arg2=if_not_foundkeyarg1arg2infunt~comparek~a~b~if_found~if_not_found->findi_and_call_implt~comparekab~call_if_found~call_if_not_found~if_found~if_not_found;;letfind=letif_foundv=Somevinletif_not_found_=Noneinfunt~comparek->find_and_callt~comparek~if_found~if_not_found;;letmem=letif_found_=trueinletif_not_found_=falseinfunt~comparek->find_and_callt~comparek~if_found~if_not_found;;letremove=letrecmin_elttree=matchtreewith|Empty->Empty|Leaf_->tree|Node{left=Empty;key=_;value=_;height=_;right=_}->tree|Node{left;key=_;value=_;height=_;right=_}->min_eltleftinletrecremove_min_elttree=matchtreewith|Empty->assertfalse|Leaf_->Empty(* This must be the root *)|Node{left=Empty;key=_;value=_;height=_;right}->right|Node{left=Leaf_;key=k;value=v;height=_;right=Empty}->Leaf{key=k;value=v}|Node{left=Leaf_;key=_;value=_;height=_;right=_}asnode->set_leftnodeEmpty;tree|Node{left;key=_;value=_;height=_;right=_}asnode->set_leftnode(remove_min_eltleft);treeinletmerget1t2=matcht1,t2with|Empty,t->t|t,Empty->t|_,_->lettree=min_eltt2in(matchtreewith|Empty->assertfalse|Leaf{key=k;value=v}->lett2=balance(remove_min_eltt2)inNode{left=t1;key=k;value=v;height=Int.max(heightt1)(heightt2)+1;right=t2}|Node_asnode->set_rightnode(remove_min_eltt2);set_leftnodet1;node)inletrecremovetremovedcomparek=matchtwith|Empty->removed:=false;Empty|Leaf{key=k';value=_}->ifcomparekk'=0then(removed:=true;Empty)else(removed:=false;t)|Node{left;key=k';value=_;height=_;right}->letc=comparekk'inifc=0then(removed:=true;mergeleftright)elseifc<0then(set_leftt(removeleftremovedcomparek);t)else(set_rightt(removerightremovedcomparek);t)infunt~removed~comparek->balance(removetremovedcomparek);;letrecfoldt~init~f=matchtwith|Empty->init|Leaf{key;value=data}->f~key~datainit|Node{left=Leaf{key=lkey;value=ldata};key;value=data;height=_;right=Leaf{key=rkey;value=rdata}}->f~key:rkey~data:rdata(f~key~data(f~key:lkey~data:ldatainit))|Node{left=Leaf{key=lkey;value=ldata};key;value=data;height=_;right=Empty}->f~key~data(f~key:lkey~data:ldatainit)|Node{left=Empty;key;value=data;height=_;right=Leaf{key=rkey;value=rdata}}->f~key:rkey~data:rdata(f~key~datainit)|Node{left;key;value=data;height=_;right=Leaf{key=rkey;value=rdata}}->f~key:rkey~data:rdata(f~key~data(foldleft~init~f))|Node{left=Leaf{key=lkey;value=ldata};key;value=data;height=_;right}->foldright~init:(f~key~data(f~key:lkey~data:ldatainit))~f|Node{left;key;value=data;height=_;right}->foldright~init:(f~key~data(foldleft~init~f))~f;;letrecitert~f=matchtwith|Empty->()|Leaf{key;value=data}->f~key~data|Node{left;key;value=data;height=_;right}->iterleft~f;f~key~data;iterright~f;;letrecmapi_inplacet~f=matchtwith|Empty->()|Leaf({key;value}ast)->t.value<-f~key~data:value|Node({left;key;value;height=_;right}ast)->mapi_inplace~fleft;t.value<-f~key~data:value;mapi_inplace~fright;;letchoose_exn=function|Empty->raise_s(Sexp.message"[Avltree.choose_exn] of empty hashtbl"[])|Leaf{key;value;_}|Node{key;value;_}->key,value;;