123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609(* This file is free software, part of containers. See file "license" for more details. *)(** {1 Weight-Balanced Tree}
Most of this comes from "implementing sets efficiently in a functional language",
Stephen Adams.
The coefficients 5/2, 3/2 for balancing come from "balancing weight-balanced trees"
*)(*$inject
module M = Make(CCInt)
type op =
| Add of int * int
| Remove of int
| Remove_min
let gen_op = CCRandom.(choose_exn
[ return Remove_min
; map (fun x->Remove x) small_int
; pure (fun x y->Add (x,y)) <*> small_int <*> small_int])
and pp_op =let open Printf in
function Add (x,y) -> sprintf "Add %d %d" x y
| Remove x -> sprintf "Remove %d" x | Remove_min -> "Remove_min"
let apply_ops l m = List.fold_left
(fun m -> function
| Add (i,b) -> M.add i b m
| Remove i -> M.remove i m
| Remove_min ->
try let _, _, m' = M.extract_min m in m' with Not_found -> m
) m l
let op = Q.make ~print:pp_op gen_op
let _list_uniq = CCList.sort_uniq ~cmp:(CCFun.compose_binop fst Stdlib.compare)
*)(*$Q & ~count:200
Q.(list op) (fun l -> let m = apply_ops l M.empty in M.balanced m)
*)type'asequence=('a->unit)->unittype'agen=unit->'aoptiontype'aprinter=Format.formatter->'a->unitmoduletypeORD=sigtypetvalcompare:t->t->intendmoduletypeKEY=sigincludeORDvalweight:t->intend(** {2 Signature} *)moduletypeS=sigtypekeytype+'atvalempty:'atvalis_empty:_t->boolvalsingleton:key->'a->'atvalmem:key->_t->boolvalget:key->'at->'aoptionvalget_exn:key->'at->'a(** @raise Not_found if the key is not present *)valnth:int->'at->(key*'a)option(** [nth i m] returns the [i]-th [key, value] in the ascending
order. Complexity is [O(log (cardinal m))] *)valnth_exn:int->'at->key*'a(** @raise Not_found if the index is invalid *)valget_rank:key->'at->[`Atofint|`Afterofint|`First](** [get_rank k m] looks for the rank of [k] in [m], i.e. the index
of [k] in the sorted list of bindings of [m].
[let (`At n) = get_rank k m in nth_exn n m = get m k] should hold.
@since 1.4 *)valadd:key->'a->'at->'atvalremove:key->'at->'atvalupdate:key->('aoption->'aoption)->'at->'at(** [update k f m] calls [f (Some v)] if [get k m = Some v], [f None]
otherwise. Then, if [f] returns [Some v'] it binds [k] to [v'],
if [f] returns [None] it removes [k] *)valcardinal:_t->intvalweight:_t->intvalfold:f:('b->key->'a->'b)->x:'b->'at->'bvalmapi:f:(key->'a->'b)->'at->'bt(** Map values, giving both key and value. Will use {!WORD.of_list} to rebuild keys.
@since 0.17
*)valmap:f:('a->'b)->'at->'bt(** Map values, giving only the value.
@since 0.17
*)valiter:f:(key->'a->unit)->'at->unitvalsplit:key->'at->'at*'aoption*'at(** [split k t] returns [l, o, r] where [l] is the part of the map
with keys smaller than [k], [r] has keys bigger than [k],
and [o = Some v] if [k, v] belonged to the map *)valmerge:f:(key->'aoption->'boption->'coption)->'at->'bt->'ct(** Like {!Map.S.merge} *)valextract_min:'at->key*'a*'at(** [extract_min m] returns [k, v, m'] where [k,v] is the pair with the
smallest key in [m], and [m'] does not contain [k].
@raise Not_found if the map is empty *)valextract_max:'at->key*'a*'at(** [extract_max m] returns [k, v, m'] where [k,v] is the pair with the
highest key in [m], and [m'] does not contain [k].
@raise Not_found if the map is empty *)valchoose:'at->(key*'a)optionvalchoose_exn:'at->key*'a(** @raise Not_found if the tree is empty *)valrandom_choose:Random.State.t->'at->key*'a(** Randomly choose a (key,value) pair within the tree, using weights
as probability weights
@raise Not_found if the tree is empty *)valadd_list:'at->(key*'a)list->'atvalof_list:(key*'a)list->'atvalto_list:'at->(key*'a)listvaladd_seq:'at->(key*'a)sequence->'atvalof_seq:(key*'a)sequence->'atvalto_seq:'at->(key*'a)sequencevaladd_gen:'at->(key*'a)gen->'atvalof_gen:(key*'a)gen->'atvalto_gen:'at->(key*'a)genvalpp:keyprinter->'aprinter->'atprinter(**/**)valnode_:key->'a->'at->'at->'atvalbalanced:_t->bool(**/**)endmoduleMakeFull(K:KEY):Swithtypekey=K.t=structtypekey=K.ttypeweight=inttype+'at=|E|Nofkey*'a*'at*'at*weightletempty=Eletis_empty=function|E->true|N_->falseletrecget_exnkm=matchmwith|E->raiseNot_found|N(k',v,l,r,_)->matchK.comparekk'with|0->v|nwhenn<0->get_exnkl|_->get_exnkrletgetkm=trySome(get_exnkm)withNot_found->Noneletmemkm=tryignore(get_exnkm);truewithNot_found->falseletsingletonkv=N(k,v,E,E,K.weightk)letweight=function|E->0|N(_,_,_,_,w)->w(* balancing parameters.
We take the parameters from "Balancing weight-balanced trees", as they
are rational and efficient. *)(* delta=5/2
delta × (weight l + 1) ≥ weight r + 1
*)letis_balancedlr=5*(weightl+1)>=2*(weightr+1)(* gamma = 3/2
weight l + 1 < gamma × (weight r + 1) *)letis_singlelr=2*(weightl+1)<3*(weightr+1)(* debug function *)letrecbalanced=function|E->true|N(_,_,l,r,_)->is_balancedlr&&is_balancedrl&&balancedl&&balancedr(* smart constructor *)letmk_node_kvlr=N(k,v,l,r,weightl+weightr+K.weightk)letsingle_lk1v1t1t2=matcht2with|E->assertfalse|N(k2,v2,t2,t3,_)->mk_node_k2v2(mk_node_k1v1t1t2)t3letdouble_lk1v1t1t2=matcht2with|N(k2,v2,N(k3,v3,t2,t3,_),t4,_)->mk_node_k3v3(mk_node_k1v1t1t2)(mk_node_k2v2t3t4)|_->assertfalseletrotate_lkvlr=matchrwith|E->assertfalse|N(_,_,rl,rr,_)->ifis_singlerlrrthensingle_lkvlrelsedouble_lkvlr(* balance towards left *)letbalance_lkvlr=ifis_balancedlrthenmk_node_kvlrelserotate_lkvlrletsingle_rk1v1t1t2=matcht1with|E->assertfalse|N(k2,v2,t11,t12,_)->mk_node_k2v2t11(mk_node_k1v1t12t2)letdouble_rk1v1t1t2=matcht1with|N(k2,v2,t11,N(k3,v3,t121,t122,_),_)->mk_node_k3v3(mk_node_k2v2t11t121)(mk_node_k1v1t122t2)|_->assertfalseletrotate_rkvlr=matchlwith|E->assertfalse|N(_,_,ll,lr,_)->ifis_singlelrllthensingle_rkvlrelsedouble_rkvlr(* balance toward right *)letbalance_rkvlr=ifis_balancedrlthenmk_node_kvlrelserotate_rkvlrletrecaddkvm=matchmwith|E->singletonkv|N(k',v',l,r,_)->matchK.comparekk'with|0->mk_node_kvlr|nwhenn<0->balance_rk'v'(addkvl)r|_->balance_lk'v'l(addkvr)(*$Q
Q.(list (pair small_int bool)) (fun l -> \
let m = M.of_list l in \
M.balanced m)
Q.(list (pair small_int small_int)) (fun l -> \
let l = _list_uniq l in let m = M.of_list l in \
List.for_all (fun (k,v) -> M.get_exn k m = v) l)
Q.(list (pair small_int small_int)) (fun l -> \
let l = _list_uniq l in let m = M.of_list l in \
M.cardinal m = List.length l)
*)(* extract min binding of the tree *)letrecextract_minm=matchmwith|E->raiseNot_found|N(k,v,E,r,_)->k,v,r|N(k,v,l,r,_)->letk',v',l'=extract_minlink',v',balance_lkvl'r(* extract max binding of the tree *)letrecextract_maxm=matchmwith|E->raiseNot_found|N(k,v,l,E,_)->k,v,l|N(k,v,l,r,_)->letk',v',r'=extract_maxrink',v',balance_rkvlr'letrecremovekm=matchmwith|E->E|N(k',v',l,r,_)->matchK.comparekk'with|0->beginmatchl,rwith|E,E->E|E,o|o,E->o|_,_->ifweightl>weightrthen(* remove max element of [l] and put it at the root,
then rebalance towards the left if needed *)letk',v',l'=extract_maxlinbalance_lk'v'l'relse(* remove min element of [r] and rebalance *)letk',v',r'=extract_minrinbalance_rk'v'lr'end|nwhenn<0->balance_lk'v'(removekl)r|_->balance_rk'v'l(removekr)(*$Q
Q.(list_of_size Gen.(0 -- 30) (pair small_int small_int)) (fun l -> \
let m = M.of_list l in \
List.for_all (fun (k,_) -> \
M.mem k m && (let m' = M.remove k m in not (M.mem k m'))) l)
Q.(list_of_size Gen.(0 -- 30) (pair small_int small_int)) (fun l -> \
let m = M.of_list l in \
List.for_all (fun (k,_) -> let m' = M.remove k m in M.balanced m') l)
*)letupdatekfm=letmaybe_v=getkminmatchmaybe_v,fmaybe_vwith|None,None->m|Some_,None->removekm|_,Somev->addkvmletrecnth_exnim=matchmwith|E->raiseNot_found|N(k,v,l,r,w)->letc=i-weightlinmatchcwith|0->k,v|nwhenn<0->nth_exnil(* search left *)|_->(* means c< K.weight k *)ifi<w-weightrthenk,velsenth_exn(i+weightr-w)rletnthim=trySome(nth_exnim)withNot_found->None(*$T
let m = CCList.(0 -- 1000 |> map (fun i->i,i) |> M.of_list) in \
List.for_all (fun i -> M.nth_exn i m = (i,i)) CCList.(0--1000)
*)letget_rankkm=letrecauxikm=matchmwith|E->ifi=0then`Firstelse`Afteri|N(k',_,l,r,_)->matchK.comparekk'with|0->`At(i+weightl)|nwhenn<0->auxikl|_->aux(1+weightl+i)krinaux0km(*$QR & ~count:1_000
Q.(list_of_size Gen.(0 -- 30) (pair small_int small_int)) (fun l ->
let l = CCList.sort_uniq ~cmp:(CCFun.compose_binop fst compare) l in
let m = M.of_list l in
List.for_all
(fun (k,v) -> match M.get_rank k m with
| `First | `After _ -> true
| `At n -> (k,v) = M.nth_exn n m)
l)
*)letrecfold~f~x:accm=matchmwith|E->acc|N(k,v,l,r,_)->letacc=fold~f~x:acclinletacc=facckvinfold~f~x:accrletrecmapi~f=function|E->E|N(k,v,l,r,w)->N(k,fkv,mapi~fl,mapi~fr,w)letrecmap~f=function|E->E|N(k,v,l,r,w)->N(k,fv,map~fl,map~fr,w)letreciter~fm=matchmwith|E->()|N(k,v,l,r,_)->iter~fl;fkv;iter~frletchoose_exn=function|E->raiseNot_found|N(k,v,_,_,_)->k,vletchoose=function|E->None|N(k,v,_,_,_)->Some(k,v)(* pick an index within [0.. weight m-1] and get the element with
this index *)letrandom_choosestm=letw=weightminifw=0thenraiseNot_found;nth_exn(Random.State.intstw)m(* make a node (k,v,l,r) but balances on whichever side requires it *)letnode_shallow_kvlr=ifis_balancedlrthenifis_balancedrlthenmk_node_kvlrelsebalance_rkvlrelsebalance_lkvlr(* assume keys of [l] are smaller than [k] and [k] smaller than keys of [r],
but do not assume anything about weights.
returns a tree with l, r, and (k,v) *)letrecnode_kvlr=matchl,rwith|E,E->singletonkv|E,o|o,E->addkvo|N(kl,vl,ll,lr,_),N(kr,vr,rl,rr,_)->letleft=is_balancedlrinifleft&&is_balancedrlthenmk_node_kvlrelseifnotleftthennode_shallow_krvr(node_kvlrl)rrelsenode_shallow_klvlll(node_kvlrr)(* join two trees, assuming all keys of [l] are smaller than keys of [r] *)letjoin_lr=matchl,rwith|E,E->E|E,o|o,E->o|N_,N_->ifweightl<=weightrthenletk,v,r'=extract_minrinnode_kvlr'elseletk,v,l'=extract_maxlinnode_kvl'r(* if [o_v = Some v], behave like [mk_node k v l r]
else behave like [join_ l r] *)letmk_node_or_join_ko_vlr=matcho_vwith|None->join_lr|Somev->node_kvlrletrecsplitkm=matchmwith|E->E,None,E|N(k',v',l,r,_)->matchK.comparekk'with|0->l,Somev',r|nwhenn<0->letll,o,lr=splitklinll,o,node_k'v'lrr|_->letrl,o,rr=splitkrinnode_k'v'lrl,o,rr(*$QR & ~count:20
Q.(list_of_size Gen.(1 -- 100) (pair small_int small_int)) ( fun lst ->
let lst = _list_uniq lst in
let m = M.of_list lst in
List.for_all (fun (k,v) ->
let l, v', r = M.split k m in
v' = Some v
&& (M.to_seq l |> Iter.for_all (fun (k',_) -> k' < k))
&& (M.to_seq r |> Iter.for_all (fun (k',_) -> k' > k))
&& M.balanced m
&& M.cardinal l + M.cardinal r + 1 = List.length lst
) lst)
*)letrecmerge~fab=matcha,bwith|E,E->E|E,N(k,v,l,r,_)->letv'=fkNone(Somev)inmk_node_or_join_kv'(merge~fEl)(merge~fEr)|N(k,v,l,r,_),E->letv'=fk(Somev)Noneinmk_node_or_join_kv'(merge~flE)(merge~frE)|N(k1,v1,l1,r1,w1),N(k2,v2,l2,r2,w2)->ifK.comparek1k2=0then(* easy case *)mk_node_or_join_k1(fk1(Somev1)(Somev2))(merge~fl1l2)(merge~fr1r2)elseifw1<=w2then(* split left tree *)letl1',v1',r1'=splitk2ainmk_node_or_join_k2(fk2v1'(Somev2))(merge~fl1'l2)(merge~fr1'r2)else(* split right tree *)letl2',v2',r2'=splitk1binmk_node_or_join_k1(fk1(Somev1)v2')(merge~fl1l2')(merge~fr1r2')(*$R
let m1 = M.of_list [1, 1; 2, 2; 4, 4] in
let m2 = M.of_list [1, 1; 3, 3; 4, 4; 7, 7] in
let m = M.merge (fun k -> CCOpt.map2 (+)) m1 m2 in
assert_bool "balanced" (M.balanced m);
assert_equal
~cmp:(CCList.equal (CCPair.equal CCInt.equal CCInt.equal))
~printer:CCFormat.(to_string (list (pair int int)))
[1, 2; 4, 8]
(M.to_list m |> List.sort Stdlib.compare)
*)(*$QR
Q.(let p = list (pair small_int small_int) in pair p p) (fun (l1, l2) ->
let l1 = _list_uniq l1 and l2 = _list_uniq l2 in
let m1 = M.of_list l1 and m2 = M.of_list l2 in
let m = M.merge (fun _ v1 v2 -> match v1 with
| None -> v2 | Some _ as r -> r) m1 m2 in
List.for_all (fun (k,v) -> M.get_exn k m = v) l1 &&
List.for_all (fun (k,v) -> M.mem k m1 || M.get_exn k m = v) l2)
*)letcardinalm=fold~f:(funacc__->acc+1)~x:0mletadd_listml=List.fold_left(funacc(k,v)->addkvacc)mlletof_listl=add_listemptylletto_listm=fold~f:(funacckv->(k,v)::acc)~x:[]mletadd_seqmseq=letm=refminseq(fun(k,v)->m:=addkv!m);!mletof_seqs=add_seqemptysletto_seqmyield=iter~f:(funkv->yield(k,v))mletrecadd_genmg=matchg()with|None->m|Some(k,v)->add_gen(addkvm)gletof_geng=add_genemptygletto_genm=letst=Stack.create()inStack.pushmst;letrecnext()=ifStack.is_emptystthenNoneelsematchStack.popstwith|E->next()|N(k,v,l,r,_)->Stack.pushrst;Stack.pushlst;Some(k,v)innextletpppp_kpp_vfmtm=letstart="["andstop="]"andarrow="->"andsep=","inFormat.pp_print_stringfmtstart;letfirst=reftrueiniterm~f:(funkv->if!firstthenfirst:=falseelseFormat.pp_print_stringfmtsep;pp_kfmtk;Format.pp_print_stringfmtarrow;pp_vfmtv;Format.pp_print_cutfmt());Format.pp_print_stringfmtstopendmoduleMake(X:ORD)=MakeFull(structincludeXletweight_=1end)