1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801(*
* BatMap - Additional map operations
* Copyright (C) 1996 Xavier Leroy
* 1996-2003 Nicolas Cannasse, Markus Mottl
* 2009-2011 David Rajchenbach-Teller, Edgar Friendly, Gabriel Scherer
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)##V>=5##modulePervasives=Stdlib(*$inject
##V>=5##module Pervasives = Stdlib
*)(* A concrete implementation for the direct balanced maps structure,
without carrying the ordering information with the data.
This implementation directly expose the map structure, and should
be the basis of both functorized Map and polymorphic PMap
operations (both providing their own way to access the ordering
information, and to possibly pass it along with the result).
I tried to keep the interface minimal with respect to ordering
information : function that do not need the ordering (they do not
need to find the position of a specific key in the map) do not have
a 'cmp' parameter.
Most of those implementations are derived from Extlib's PMap
module.
Please keep in mind that our Map module currently relies on the
fact that the (('k, 'v) Concrete.map) implementation is physically
equal to stdlib's ('a Map.S.t). Changing Concrete.map is not a good
idea.
*)moduleConcrete=structtype('k,'v)map=|Empty|Nodeof('k,'v)map*'k*'v*('k,'v)map*intletheight=function|Node(_,_,_,_,h)->h|Empty->0letempty=Emptyletis_emptym=m=Empty(* The create and bal functions are from stdlib's map.ml (3.12)
differences from the old (extlib) implementation :
1. create use direct integer comparison instead of calling
polymorphic 'max'
2. the two calls of 'height' for hl and hr in the beginning of 'bal'
(hot path) are inlined
The difference in performances is important for bal-heavy worflows,
such as "adding a lot of elements". On a test system, we go from
1800 op/s to 2500 op/s.
*)letcreatelxdr=lethl=height landhr=height rinNode(l,x,d,r,(ifhl>=hrthenhl+1elsehr+1))letballxdr=lethl=match lwithEmpty->0|Node(_,_,_,_,h)->hinlethr=match rwithEmpty->0|Node(_,_,_,_,h)->hinifhl>hr+2thenbeginmatchlwithEmpty->invalid_arg"Map.bal"|Node(ll,lv,ld,lr,_)->ifheightll>=heightlrthencreatelllvld(createlrxdr)elsebeginmatchlrwithEmpty->invalid_arg"Map.bal"|Node(lrl,lrv,lrd,lrr,_)->create(createlllvldlrl)lrv lrd(create lrrxdr)endendelseifhr>hl+2thenbeginmatchrwithEmpty->invalid_arg"Map.bal"|Node(rl,rv,rd,rr,_)->ifheightrr>=heightrlthencreate(createlxdrl)rvrdrrelsebeginmatchrlwithEmpty->invalid_arg"Map.bal"|Node(rll,rlv,rld,rlr,_)->create(createlxdrll)rlv rld(create rlrrvrdrr)endendelseNode(l,x,d,r,(ifhl>=hrthenhl+1elsehr+1))letrecmin_binding=function|Node(Empty,k,v,_,_)->k,v|Node(l,_,_,_,_)->min_bindingl|Empty->raiseNot_foundletrecmin_binding_opt=function|Node(Empty,k,v,_,_)->Some(k,v)|Node(l,_,_,_,_)->min_binding_optl|Empty->Noneletget_root=function|Empty->raiseNot_found|Node(_,k,v,_,_)->k,vletpop_min_bindings=letmini=ref (get_root s)inletrecloop=function|Empty->assert(false)(* get_root already raises Not_found on empty map *)|Node(Empty,k,v,r,_)->mini:=(k,v);r|Node(l,k,v,r,_)->bal(loopl)kvrinletothers=loopsin(!mini,others)letrecmax_binding=function|Node(_,k,v,Empty,_)->k,v|Node(_,_,_,r,_)->max_bindingr|Empty->raiseNot_foundletrecmax_binding_opt=function|Node(_,k,v,Empty,_)->Some(k,v)|Node(_,_,_,r,_)->max_binding_optr|Empty->Noneletpop_max_bindings=letmaxi=ref (get_root s)inletrecloop=function|Empty->assert(false)(* get_root already raises Not_found on empty map *)|Node(l,k,v,Empty,_)->maxi:=(k,v);l|Node(l,k,v,r,_)->ballkv(loopr)inletothers=loopsin(!maxi,others)letrecremove_min_binding=function|Node(Empty,_,_,r,_)->r|Node(l,k,v,r,_)->bal(remove_min_bindingl)kvr|Empty->raiseNot_foundletmerget1t2=matcht1,t2with|Empty,_->t2|_,Empty->t1|_->letk,v=min_binding t2inbalt1kv(remove_min_bindingt2)letaddxdcmpmap=letrecloop=function|Node(l,k,v,r,h)asnode->letc=cmpxkinifc=0thenifd==vthennodeelseNode(l,x,d,r,h)elseifc<0thenletnl=looplinifnl==lthennodeelsebalnlkvrelseletnr=looprinifnr==rthennodeelseballkvnr|Empty->Node(Empty,x,d,Empty,1)inloopmapletfindxcmpmap=letrecloop=function|Node(l,k,v,r,_)->letc=cmpxkinifc<0thenlooplelseifc>0thenlooprelsev|Empty->raiseNot_foundinloopmapletrecfind_first_helper_foundk0v0f=function|Empty->(k0,v0)|Node(l,k,v,r,_)->iffkthenfind_first_helper_foundkvflelsefind_first_helper_foundk0v0frletrecfind_firstfm=matchmwith|Empty->raiseNot_found|Node(l,k,v,r,_)->iffkthenfind_first_helper_foundkvflelsefind_firstfrletrecfind_first_optfm=matchmwith|Empty->None|Node(l,k,v,r,_)->iffkthenSome(find_first_helper_foundkvfl)elsefind_first_optfrletrecfind_last_helper_foundk0v0f=function|Empty->(k0,v0)|Node(l,k,v,r,_)->iffkthenfind_last_helper_foundkvfrelsefind_last_helper_foundk0v0flletrecfind_lastfm=matchmwith|Empty->raiseNot_found|Node(l,k,v,r,_)->iffkthenfind_last_helper_foundkvfrelsefind_lastflletrecfind_last_optfm=matchmwith|Empty->None|Node(l,k,v,r,_)->iffkthenSome(find_last_helper_foundkvfr)elsefind_last_optfl(*$T find_first
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first (fun x -> x >= 0)) = ((1, 11))
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first (fun x -> x >= 1)) = ((1, 11))
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first (fun x -> x >= 2)) = ((2, 12))
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first (fun x -> x >= 3)) = ((3, 13))
try ignore(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first (fun x -> x >= 4)); false with Not_found -> true
try ignore(empty |> find_first (fun x -> x >= 3)); false with Not_found -> true
*)(*$T find_first_opt
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first_opt (fun x -> x >= 0)) = (Some (1, 11))
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first_opt (fun x -> x >= 1)) = (Some (1, 11))
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first_opt (fun x -> x >= 2)) = (Some (2, 12))
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first_opt (fun x -> x >= 3)) = (Some (3, 13))
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_first_opt (fun x -> x >= 4)) = (None)
(empty |> find_first_opt (fun x -> x >= 3)) = (None)
*)(*$T find_last
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last (fun x -> x <= 1)) = (1, 11)
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last (fun x -> x <= 2)) = (2, 12)
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last (fun x -> x <= 3)) = (3, 13)
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last (fun x -> x <= 4)) = (3, 13)
try ignore(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last (fun x -> x <= 0)); false with Not_found -> true
try ignore(empty |> find_last (fun x -> x <= 3)); false with Not_found -> true
*)(*$T find_last_opt
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last_opt (fun x -> x <= 0)) = None
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last_opt (fun x -> x <= 1)) = Some (1, 11)
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last_opt (fun x -> x <= 2)) = Some (2, 12)
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last_opt (fun x -> x <= 3)) = Some (3, 13)
(empty |> add 1 11 |> add 2 12 |> add 3 13 |> find_last_opt (fun x -> x <= 4)) = Some (3, 13)
(empty |> find_last_opt (fun x -> x <= 3)) = None
*)letfind_optionxcmpmap=trySome(findxcmpmap)withNot_found->Noneletfind_defaultdefxcmpmap=tryfindxcmpmapwithNot_found->defletremovexcmpmap=letrecloop=function|Node(l,k,v,r,_)asnode->letc=cmpxkinifc=0thenmergelrelseifc<0thenletnl=looplinifnl==lthennodeelsebalnlkvrelseletnr=looprinifnr==rthennodeelseballkvnr|Empty->Emptyinloopmap(* A variant of [remove] that throws [Not_found] on failure *)letremove_exnxcmpmap=letrecloop=function|Empty->raiseNot_found|Node(l,k,v,r,_)->letc=cmpxkinifc=0thenmergelrelseifc<0thenbal(loopl)kvrelseballkv(loopr)inloopmapletupdatek1k2v2cmpmap=ifcmpk1k2<>0thenaddk2v2cmp(remove_exn k1cmpmap)elseletrecloop=function|Empty->raiseNot_found|Node(l,k,v,r,h)asnode->letc=cmpk1kinifc=0thenifv==v2&&k==k2thennodeelseNode(l,k2,v2,r,h)elseifc<0thenletnl=looplinifnl==lthennodeelseNode(nl,k,v,r,h)elseletnr=looprinifnr==rthennodeelseNode(l,k,v,nr,h)inloopmapletrecupdate_stdlibxfcmp=function|Empty->beginmatchfNonewith|None->Empty|Somedata->Node(Empty,x,data,Empty,1)end|Node(l,v,d,r,h)asm->letc=cmpxvinifc=0thenbeginmatchf(Somed)with|None->mergelr|Somedata->ifd==datathenmelseNode(l,x,data,r,h)endelseifc<0thenletll=update_stdlib xfcmplinifl==llthenmelseballlvdrelseletrr=update_stdlib xfcmprinifr==rrthenmelseballvdrrletmemxcmpmap=letrecloop=function|Node(l,k,_v,r,_)->letc=cmpxkinc=0||loop(ifc<0thenlelser)|Empty->falseinloopmapletiterfmap=letrecloop=function|Empty->()|Node(l,k,v,r,_)->loopl;fkv;looprinloopmapletmapfmap=letrecloop=function|Empty->Empty|Node(l,k,v,r,h)->(* ensure evaluation in increasing order *)letl'=looplinletv'=fvinletr'=looprinNode(l',k,v',r',h)inloopmapletmapifmap=letrecloop=function|Empty->Empty|Node(l,k,v,r,h)->(* ensure evaluation in increasing order *)letl'=looplinletv'=fkvinletr'=looprinNode(l',k,v',r',h)inloopmapletfoldfmapacc=letrecloopacc=function|Empty->acc|Node(l,_k,v,r,_)->loop(fv(loopaccl))rinloopaccmapletfoldifmapacc=letrecloopacc=function|Empty->acc|Node(l,k,v,r,_)->loop(fkv(loopaccl))rinloopaccmapexceptionFoundletat_rank_exnim=ifi<0theninvalid_arg"Map.at_rank_exn: i < 0";letres=ref (get_root m)in(* raises Not_found if empty *)trylet(_:int)=foldi(funkvj->ifj<>ithen j+1elsebeginres:=(k,v);raiseFoundend)m0ininvalid_arg"Map.at_rank_exn: i >= (Map.cardinal s)"withFound->!res(*$T at_rank_exn
(empty |> add 1 true |> at_rank_exn 0) = (1, true)
(empty |> add 1 true |> add 2 false |> at_rank_exn 1) = (2, false)
try ignore(at_rank_exn (-1) empty); false with Invalid_argument _ -> true
try ignore(at_rank_exn 0 empty); false with Not_found -> true
try ignore(add 1 true empty |> at_rank_exn 1); false with Invalid_argument _ -> true
*)letsingletonxd=Node(Empty,x,d,Empty,1)(* beware : those two functions assume that the added k is *strictly*
smaller (or bigger) than all the present keys in the tree; it
does not test for equality with the current min (or max) key.
Indeed, they are only used during the "join" operation which
respects this precondition.
*)letrecadd_min_bindingkv=function|Empty->singletonkv|Node(l,x,d,r,_h)->bal(add_min_bindingkvl)xdrletrecadd_max_bindingkv=function|Empty->singletonkv|Node(l,x,d,r,_h)->ballxd(add_max_bindingkvr)(* Same as create and bal, but no assumptions are made on the
relative heights of l and r.
The stdlib implementation was changed to use the new
[add_{min,max}_binding] functions instead of the [add] function
that would require to pass a comparison function. *)letrecjoinlvdr=match(l,r)with(Empty,_)->add_min_bindingvdr|(_,Empty)->add_max_bindingvdl|(Node(ll,lv,ld,lr,lh),Node(rl,rv,rd,rr,rh))->iflh>rh+2thenballllvld(joinlrvdr)elseifrh>lh+2thenbal(joinlvdrl)rvrdrrelsecreatelvdr(* split also is from stdlib 3.12 *)letrecsplitkeycmp=function|Empty->(Empty,None,Empty)|Node(l,x,d,r,_)->letc=cmpkeyxinifc=0then(l,Somed,r)elseifc<0thenlet(ll,pres,rl)=split keycmplin(ll,pres,join rlxdr)elselet(lr,pres,rr)=split keycmprin(joinlxdlr,pres,rr)type('key,'a)iter=E|Cof'key*'a*('key,'a)map*('key,'a)iterletcardinalmap=letrecloopacc=function|Empty->acc|Node(l,_,_,r,_)->loop(loop(acc+1)r)linloop0mapletrecbindings_auxaccu=function|Empty->accu|Node(l,v,d,r,_)->bindings_aux((v,d)::bindings_auxaccur)lletbindingss=bindings_aux[]sletreccons_iterst=matchswith|Empty->t|Node(l,k,v,r,_)->cons_iterl(C(k,v,r,t))letrecrev_cons_iterst=matchswith|Empty->t|Node(l,k,v,r,_)->rev_cons_iterr(C(k,v,l,t))letreccons_iter_fromcmpk2me=matchmwith|Empty->e|Node(l,k,v,r,_)->ifcmpk2k<=0thencons_iter_fromcmpk2l(C(k,v,r,e))elsecons_iter_fromcmpk2reletenum_nextl()=match!lwithE->raiseBatEnum.No_more_elements|C(k,v,m,t)->l:=cons_itermt;(k,v)letenum_backwards_nextl()=match!lwithE->raiseBatEnum.No_more_elements|C(k,v,m,t)->l:=rev_cons_itermt;(k,v)letenum_countl()=letrecauxn=function|E->n|C(_,_,m,t)->aux(n+1+cardinalm)tinaux0!lletenumt=letrecmakel=letl=reflinletclone()=make!linBatEnum.make~next:(enum_nextl)~count:(enum_countl)~cloneinmake(cons_iter tE)letbackwardst=letrecmakel=letl=reflinletclone()=make!linBatEnum.make~next:(enum_backwards_nextl)~count:(enum_countl)~cloneinmake(rev_cons_iter tE)letkeyst=BatEnum.mapfst(enum t)letvaluest=BatEnum.mapsnd(enum t)letof_enumcmpe=BatEnum.fold(funm(k,v)->addkvcmpm)emptyeletprint?(first="{\n")?(last="\n}")?(sep=",\n")?(kvsep=": ")print_kprint_voutt=BatEnum.print~first~last~sep (fun out(k,v)->BatPrintf.fprintfout"%a%s%a"print_kkkvsepprint_vv)out(enum t)(*We rely on [fold] rather than on ['a implementation] to
make future changes of implementation in the base
library's version of [Map] easier to track, even if the
result is a tad slower.*)(* [filter{,i,_map} f t cmp] do not use [cmp] on [t], but only to
build the result map. The unusual parameter order was chosen to
reflect this. *)letfiltervftcmp=foldi(funkaacc->iffathenaccelseremovekcmpacc)ttletfilterftcmp=foldi(funkaacc->iffkathenaccelseremovekcmpacc)ttletfilter_mapftcmp=foldi(funkaacc->matchfkawith|None->acc|Somev->addkvcmpacc)temptyletfor_allfmap=letrecloop=function|Empty->true|Node(l,k,v,r,_)->fkv&&loopl&&looprinloopmapletexistsfmap=letrecloop=function|Empty->false|Node(l,k,v,r,_)->fkv||loopl||looprinloopmapletpartitionfcmpmap=letrecloopm1m2=function|Empty->(m1,m2)|Node(l,k,v,r,_)->letm1,m2=loopm1m2linletm1,m2=loopm1m2riniffkvthen(addkvcmpm1,m2)else(m1,addkvcmpm2)inloopempty emptymapletchoose=min_binding(*$= choose
(empty |> add 0 1 |> add 1 1 |> choose) (empty |> add 1 1 |> add 0 1 |> choose)
*)letchoose_optm=trySome(choosem)withNot_found->Noneletany=function|Empty->raiseNot_found|Node(_,k,v,_,_)->(k,v)letadd_carryxdcmpmap=letrecloop=function|Node(l,k,v,r,h)->letc=cmpxkinifc=0thenNode(l,x,d,r,h),Somevelseifc<0thenletnl,carry=looplinbalnlkvr,carryelseletnr,carry=looprinballkvnr,carry|Empty->Node(Empty,x,d,Empty,1),Noneinloopmapletmodifyxfcmpmap=letrecloop=function|Node(l,k,v,r,h)->letc=cmpxkinifc=0thenNode(l,x,fv,r,h)elseifc<0thenletnl=looplinbalnlkvrelseletnr=looprinballkvnr|Empty->raiseNot_foundinloopmapletmodify_defv0xfcmpmap=letrecloop=function|Node(l,k,v,r,h)->letc=cmpxkinifc=0thenNode(l,x,fv,r,h)elseifc<0thenletnl=looplinbalnlkvrelseletnr=looprinballkvnr|Empty->Node(Empty,x,fv0,Empty,1)inloopmapletmodify_optxfcmpmap=letrecloop=function|Node(l,k,v,r,h)->letc=cmpxkinifc=0thenmatchf(Somev)with|None->mergelr|Somev'->Node(l,x,v',r,h)elseifc<0thenletnl=looplinbalnlkvrelseletnr=looprinballkvnr|Empty->matchfNonewith|None->raiseExit(* fast exit *)|Somed->Node(Empty,x,d,Empty,1)intryloopmapwithExit->mapletextractxcmpmap=letrecloop=function|Node(l,k,v,r,_)->letc=cmpxkinifc=0thenv,mergelrelseifc<0thenletvout,nl=looplinvout,bal nlkvrelseletvout,nr=looprinvout,bal lkvnr|Empty->raiseNot_foundinloopmapletpopmap=matchmapwith|Empty->raiseNot_found|Node(l,k,v,r,_)->(k,v),mergelr(* Merge two trees l and r into one.
All elements of l must precede the elements of r.
No assumption on the heights of l and r. *)letconcatt1t2=match(t1,t2)with(Empty,t)->t|(t,Empty)->t|(_,_)->let(x,d)=min_binding t2injoint1xd(remove_min_bindingt2)letconcat_or_joint1vdt2=matchdwith|Somed->joint1vdt2|None->concatt1t2letmergefcmp12s1s2=letrecloops1s2=match(s1,s2)with|(Empty,Empty)->Empty|(Node(l1,v1,d1,r1,h1),_)whenh1>=heights2->let(l2,d2,r2)=split v1cmp12s2in(* TODO force correct evaluation order *)concat_or_join(loopl1l2)v1(fv1(Somed1)d2)(loopr1r2)|(_,Node(l2,v2,d2,r2,_h2))->let(l1,d1,r1)=split v2cmp12s1inconcat_or_join(loopl1l2)v2(fv2d1(Somed2))(loopr1r2)|_->assertfalseinloops1s2letmerge_diversefcmp1s1cmp2s2=(* This implementation does not presuppose that the comparison
function of s1 and s2 are the same. It is necessary in the PMap
case, were we can't enforce that the same comparison function is
used on both maps.
For consistency, we will always return a result built with the
comparison function of [m1].
The idea of the algorithm is the following : iterates on keys
of (s1 union s2), computing the merge result for each
f k (find_option k s1) (find_option k s2)
, and adding values to the result s3 accordingly.
The crucial point is that we need to iterate on both keys of s1
and s2. There are several possible implementations :
1. first build the union of the set of keys, then iterate on
it.
2. iterate on s1, then reiterate on s2 checking that the key
wasn't already in s1
3. iterate on s1, and remove keys from s2 during the traversal,
then iterate on the remainder of s2.
Method 1. allocates a temporary map the size of (s1 union s2),
which I think is too costly. Method 3 may seem better than
method 2 (as we only have at the end to iterate on the
remaining keys, instead of dropping almost all keys because
they were in s1 already), but is actually less efficient : the
cost of removing is amortized during s1 traversal, but in
effect we will, for all keys of s2, either remove it (in the
first phase) or traverse it in the second phase. With method 2,
we either ignore it or traverse it (both in the second
phase). As removal induces rebalancing and allocation, it is
indeed more costly.
Method 2 only allocations and rebalancing are during the
building of the final map : s1 and s2 are only looked at, never
changed. This is optimal memory-wise.
Those informal justifications ought to be tested with
a concrete performance measurements, but the current benchmark
methods, outside the module, don't make it easy to test
Concrete values directly (as they're hidden by the interface).
An old benchmark reports than method 2 is sensibly faster than
method 1 : 2700 op/s vs 951 op/s on the test input.
This algorithm is still sensibly slower than the 'merge'
implementation using the same comparison on both maps : a 270%
performance penalty has been measured (it runs three times
slower).
*)letfirst_phase_result=foldi(funkv1acc->matchfk(Somev1)(find_option kcmp2s2)with|None->acc|Somev3->addkv3cmp1acc)s1emptyin(* the second phase will return the result *)foldi(funkv2acc->ifmemkcmp1s1thenaccelsematchfkNone(Somev2)with|None->acc|Somev3->addkv3cmp1acc)s2first_phase_result(* Checks if a given map is "ordered" wrt. a given comparison
function. This means that the key are ordered in strictly
increasing order.
If [ordered cmp s] holds, [cmp] can be used to search elements in
the map *even* if it is not the original comparison function that
was used to build the map; we know that the two comparison
function "agree" on the present keys. Of course, adding an
element with one or the other comparison function may break that
relation.
The [ordered] function will be useful to choose between different
implementations having different comparison requirements. For
example, the implementation of [merge] assuming both maps have
the same comparison function is much faster than the
implementation assuming heterogeneous maps. Before calling the
heterogeneous implementation, one may first check if one of the
comparison actually orders the other map, and in that case use
the fast homogeneous implementation instead. This is the
[heuristic_merge] function.
*)letorderedcmps=ifs=Emptythentrueelsetryignore(foldi(funk_last_k->ifcmplast_kk>=0then raiseExitelsek)(remove_min_bindings)(fst(min_binding s)));truewithExit->false(* Maps are considered compatible by their comparison function when either:
- cmp1 and cmp2 are the *same* function (physical equality)
- cmp1 is a correct ordering on m2 (see comment in [ordered]) *)letcompatible_cmpcmp1_m1cmp2m2=cmp1==cmp2||orderedcmp1m2(* We first try to see if the comparison functions are compatible.
If they are, then we use the [merge] function instead of a much
slower [merge_diverse].
In the "same comparisons" case, we return a map ordered with
the given comparison. In the other case, we arbitrarily use the
comparison function of [m1]. *)letheuristic_mergefcmp1m1cmp2m2=ifcompatible_cmpcmp1m1cmp2m2thenmergefcmp1m1m2elsemerge_diversefcmp1m1cmp2m2(* Binary PMap operations;
When the comparison function are compatible, we use an efficient
merge-based implementation.
Otherwise, we compute the result so that the return comparison
function is the same as the first map parameter. *)letunioncmp1m1cmp2m2=ifcompatible_cmpcmp1m1cmp2m2thenletmerge_fun_kab=ifa<>Nonethenaelsebinmergemerge_funcmp2m2m1elsefoldi(funkvm->addkvcmp1m)m2m1letdiffcmp1m1cmp2m2=ifcompatible_cmpcmp1m1cmp2m2thenletmerge_fun_kab=ifb<>NonethenNoneelseainmergemerge_funcmp1m1m2elsefoldi(funk_vm->removekcmp1m)m2m1letintersectfcmp1m1cmp2m2=ifcompatible_cmpcmp1m1cmp2m2thenletmerge_fun_kab=matcha,bwith|Somev1,Somev2->Some(fv1v2)|None,_|_,None->Noneinmergemerge_funcmp1m1m2elsefoldi(funkv1m->matchfind_optionkcmp2m2with|None->m|Somev2->addk(fv1v2)cmp1m)m1emptyletadd_seqcmpsm=BatSeq.fold_left(funm(k,v)->addkvcmpm)msletof_seqcmps=add_seqcmpsemptyletrecseq_of_iterm()=matchmwith|E->BatSeq.Nil|C(k,v,r,e)->BatSeq.Cons((k,v),seq_of_iter(cons_iterre))letto_seqm=seq_of_iter(cons_itermE)letrecrev_seq_of_iterm()=matchmwith|E->BatSeq.Nil|C(k,v,r,e)->BatSeq.Cons((k,v),rev_seq_of_iter(rev_cons_iterre))letto_rev_seqm=rev_seq_of_iter(rev_cons_itermE)letto_seq_fromcmpkm=seq_of_iter(cons_iter_fromcmpkmE)letunion_stdlibfcmp1m1cmp2m2=letfwrapab1b2=matchb1,b2with|Someb1,Someb2->fab1b2|x,None|None,x->xinheuristic_mergefwrapcmp1m1cmp2m2letcompareckeycvalm1m2=BatEnum.compare(fun(k1,v1)(k2,v2)->BatOrd.bin_compckeyk1k2cvalv1v2)(enum m1)(enum m2)letequalckeyeq_val m1m2=BatEnum.equal(fun(k1,v1)(k2,v2)->ckeyk1k2=0&&eq_valv1v2)(enum m1)(enum m2)endmoduletypeOrderedType=BatInterfaces.OrderedTypemoduletypeS=sigtypekeytype+##V>=4.12##!'atvalempty:'atvalis_empty:'at->boolvalcardinal:'at->intvaladd:key->'a->'at->'atvalupdate_stdlib:key->('aoption->'aoption)->'at->'atvalupdate:key->key->'a->'at->'atvalfind:key->'at->'avalfind_opt:key->'at->'aoptionvalfind_default:'a->key->'at->'avalfind_first:(key->bool)->'at->key*'avalfind_first_opt:(key->bool)->'at->(key*'a)optionvalfind_last:(key->bool)->'at->key*'avalfind_last_opt:(key->bool)->'at->(key*'a)optionvalremove:key->'at->'atvalremove_exn:key->'at->'atvalmodify:key->('a->'a)->'at->'atvalmodify_def:'a->key->('a->'a)->'at->'atvalmodify_opt:key->('aoption->'aoption)->'at->'atvalextract:key->'at->'a*'atvalpop:'at->(key*'a)*'atvalmem:key->'at->boolvaliter:(key->'a->unit)->'at->unitvalmap:('a->'b)->'at->'btvalmapi:(key->'a->'b)->'at->'btvalfold:(key->'a->'b->'b)->'at->'b->'bvalfilterv:('a->bool)->'at->'atvalfilter:(key->'a->bool)->'at->'atvalfilter_map:(key->'a->'boption)->'at->'btvalcompare:('a->'a->int)->'at->'at->intvalequal:('a->'a->bool)->'at->'at->boolvalkeys:_t->keyBatEnum.tvalvalues:'at->'aBatEnum.tvalmin_binding:'at->(key *'a)valmin_binding_opt:'at->(key*'a)optionvalpop_min_binding:'at->(key*'a)*'atvalmax_binding:'at->(key *'a)valmax_binding_opt:'at->(key*'a)optionvalpop_max_binding:'at->(key*'a)*'atvalchoose:'at->(key *'a)valchoose_opt:'at->(key*'a)optionvalany:'at->(key*'a)valsplit:key->'at-> ('at*'aoption*'at)valpartition:(key->'a->bool)->'at->'at*'atvalsingleton:key->'a->'atvalbindings:'at->(key*'a)listvalenum:'at->(key*'a)BatEnum.tvalbackwards:'at->(key*'a)BatEnum.tvalof_enum:(key*'a)BatEnum.t->'atvalfor_all:(key->'a->bool)->'at->boolvalexists:(key->'a->bool)->'at->boolvalmerge:(key->'aoption->'boption->'coption)->'at->'bt->'ctvalunion:(key->'a->'a->'aoption)->'at->'at->'atvalto_seq:'at->(key*'a)BatSeq.tvalto_rev_seq:'at->(key*'a)BatSeq.tvalto_seq_from:key->'at->(key*'a)BatSeq.tvaladd_seq:(key*'a)BatSeq.t->'at->'atvalof_seq:(key*'a)BatSeq.t->'atvalto_list:'at->(key*'a)listvalof_list:(key*'a)list->'atvaladd_to_list:key->'a->'alistt->'alistt(** {7 Printing}*)valprint:?first:string->?last:string->?sep:string->?kvsep:string->('aBatInnerIO.output->key->unit)->('aBatInnerIO.output->'c->unit)->'aBatInnerIO.output->'ct->unitmoduleExceptionless:sigvalfind:key->'at->'aoptionvalchoose:'at->(key*'a)optionvalany:'at->(key*'a)optionendmoduleInfix:sigval(-->):'at->key->'aval(<--):'at->key*'a->'atendmoduleLabels:sigvaladd:key:key->data:'a->'at->'atvaliter:f:(key:key->data:'a->unit)->'at->unitvalmap:f:('a->'b)->'at->'btvalmapi:f:(key:key->data:'a->'b)->'at->'btvalfilterv:f:('a->bool)->'at->'atvalfilter:f:(key->'a->bool)->'at->'atvalfold:f:(key:key->data:'a->'b->'b)->'at->init:'b->'bvalcompare:cmp:('a->'a->int)->'at->'at->intvalequal:cmp:('a->'a->bool)->'at->'at->boolendendmoduleMake(Ord:OrderedType)=structincludeMap.Make(Ord)(* We break the abstraction of stdlib's Map module by exposing
it's underlying datatype, which is exactly ((key, 'a)
Concrete.map). We therefore have O(1) conversion to and from
Concrete, which allow us to add new features to the Map
module while reusing stdlib's implementation (and, in fact,
compiled code) for the old ones.
If this was ever to be a problem, we could desynchronize our
Map implementation from stdlib's, simply reusing Concrete
implementations everywhere. Breaking this abstraction is not
our fate, it's only a convenient choice for now.
*)type'aimplementation=(key,'a)Concrete.mapexternalt_of_impl:'aimplementation->'at="%identity"externalimpl_of_t:'at->'aimplementation ="%identity"letcardinal t=Concrete.cardinal(impl_of_tt)letenumt=Concrete.enum(impl_of_tt)letbackwards t=Concrete.backwards(impl_of_tt)letkeyst=Concrete.keys(impl_of_tt)letvaluest=Concrete.values(impl_of_tt)letupdatek1k2v2t=t_of_impl(Concrete.updatek1 k2v2Ord.compare(impl_of_tt))letupdate_stdlib kfm=t_of_impl(Concrete.update_stdlibkfOrd.compare(impl_of_tm))letfind_default dkt=Concrete.find_defaultdkOrd.compare(impl_of_tt)letfind_opt kt=Concrete.find_optionkOrd.compare(impl_of_tt)letfind_firstft=Concrete.find_firstf(impl_of_tt)letfind_first_opt ft=Concrete.find_first_optf(impl_of_tt)letfind_lastft=Concrete.find_lastf(impl_of_tt)letfind_last_opt ft=Concrete.find_last_optf(impl_of_tt)letof_enume=t_of_impl(Concrete.of_enumOrd.comparee)(* In Ocaml 3.11.2, the implementation of stdlib's Map.S.map(i) are
slightly incorrect in that they don't apply their function
parameter in increasing key order, as advertised in the
documentation. This was fixed in 3.12.
http://caml.inria.fr/mantis/view.php?id=4012
We replace map(i) implementations with the ones derived from
Concrete, to have the expected evaluation order even with 3.11.
*)letmapift=t_of_impl(Concrete.mapif(impl_of_tt))letmapft=t_of_impl(Concrete.mapf(impl_of_tt))let print?first?last?sep?kvsepprint_kprint_voutt=Concrete.print?first?last?sep?kvsepprint_kprint_vout(impl_of_tt)letfiltervft=t_of_impl(Concrete.filtervf(impl_of_tt)Ord.compare)letfilterft=t_of_impl(Concrete.filterf(impl_of_tt)Ord.compare)letfilter_map ft=t_of_impl(Concrete.filter_mapf(impl_of_tt)Ord.compare)letexistsft=Concrete.existsf(impl_of_tt)letfor_allft=Concrete.for_allf(impl_of_tt)letmin_binding t=Concrete.min_binding(impl_of_tt)letpop_min_binding t=letmini,rest=Concrete.pop_min_binding(impl_of_tt)in(mini,t_of_implrest)letmax_binding t=Concrete.max_binding(impl_of_tt)letpop_max_binding t=letmaxi,rest=Concrete.pop_max_binding(impl_of_tt)in(maxi,t_of_implrest)letmax_binding_opt t=Concrete.max_binding_opt(impl_of_tt)letmin_binding_opt t=Concrete.min_binding_opt(impl_of_tt)letchooset=Concrete.choose(impl_of_tt)letchoose_opt t=Concrete.choose_opt(impl_of_tt)letanyt=Concrete.any(impl_of_tt)letsplitkt=letl,v,r=Concrete.splitkOrd.compare(impl_of_tt)in(t_of_impll,v,t_of_implr)letpartition pt=let l,r=Concrete.partitionpOrd.compare(impl_of_tt)in(t_of_impll,t_of_implr)letremove_exn xm=t_of_impl(Concrete.remove_exnxOrd.compare(impl_of_tm))letmodifyxfm=t_of_impl(Concrete.modifyxfOrd.compare(impl_of_tm))letmodify_defv0xfm=t_of_impl(Concrete.modify_defv0xfOrd.compare(impl_of_tm))letmodify_opt xfm=t_of_impl(Concrete.modify_optxfOrd.compare(impl_of_tm))letextractkt=let(v,t')=Concrete.extractkOrd.compare(impl_of_tt)in(v,t_of_implt')letpopt=letkv,t'=Concrete.pop(impl_of_tt)inkv,t_of_implt'letsingleton kv=t_of_impl(Concrete.singletonkv)letbindings t=Concrete.bindings(impl_of_tt)letunionfm1m2=t_of_impl(Concrete.union_stdlibfOrd.compare(impl_of_tm1)Ord.compare(impl_of_tm2))letmergeft1t2=t_of_impl(Concrete.mergefOrd.compare(impl_of_tt1)(impl_of_tt2))letof_seqs=t_of_impl(Concrete.of_seqOrd.compares)letadd_seqsm=t_of_impl(Concrete.add_seqOrd.compares(impl_of_tm))letto_seqm=Concrete.to_seq(impl_of_tm)letto_rev_seq m=Concrete.to_rev_seq(impl_of_tm)letto_seq_from km=Concrete.to_seq_fromOrd.comparek(impl_of_tm)letadd_to_listxdatam=letadd=functionNone->Some [data]|Somel->Some (data::l)inupdate_stdlibxaddmletto_list=bindingsletof_listbs=List.fold_left(funm(k,v)->addkvm)emptybsmoduleExceptionless=structletfindkt=trySome(findkt)withNot_found->Noneletchooset=trySome(chooset)withNot_found->Noneletanyt=trySome(anyt)withNot_found->NoneendmoduleInfix=structlet(-->)mapkey=find keymaplet(<--)map(key,value)=addkeyvalue mapendmoduleLabels=structletadd~key~datat=addkeydatatletiter~ft=iter(funkeydata->f~key ~data)tletmap~ft=mapftletmapi~ft=mapi(funkeydata->f~key ~data)tletfold~ft~init=fold(funkeydataacc->f~key~dataacc)tinitletcompare~cmpab=comparecmpablet equal~cmpab=equalcmpabletfilterv~f=filtervfletfilter~f=filterfendendmoduleInt=Make(BatInt)moduleInt32=Make(BatInt32)moduleInt64=Make(BatInt64)moduleNativeint=Make(BatNativeint)moduleFloat=Make(BatFloat)moduleChar=Make(BatChar)moduleString=Make(BatString)(**
* PMap - Polymorphic maps
*)type('k,'v)t=('k,'v)Concrete.mapletempty=Concrete.emptyletis_empty=Concrete.is_empty(*$T is_empty
is_empty empty
not(is_empty (empty |> add 1 1))
*)letaddxdm=Concrete.addxdPervasives.comparemletupdatek1k2v2m=Concrete.updatek1 k2v2Pervasives.comparemletupdate_stdlib kfm=Concrete.update_stdlibkfPervasives.comparem(*$T update_stdlib
let of_list l = of_enum (BatList.enum l) in \
equal (=) (update_stdlib 1 (fun x -> assert(x = Some 1); Some 3) (of_list [1,1; 2,2])) (of_list [1,3;2,2])
let of_list l = of_enum (BatList.enum l) in \
equal (=) (update_stdlib 3 (fun x -> assert(x = None); Some 3) (of_list [1,1; 2,2])) (of_list [1,1;2,2;3,3])
let of_list l = of_enum (BatList.enum l) in \
equal (=) (update_stdlib 1 (fun x -> assert(x = Some 1); None) (of_list [1,1; 2,2])) (of_list [2,2])
let of_list l = of_enum (BatList.enum l) in \
let s = (of_list [1,1; 2,2]) in (update_stdlib 3 (fun x -> assert(x = None ); None ) s) == s
let of_list l = of_enum (BatList.enum l) in \
let s = (of_list [1,1; 2,2]) in (update_stdlib 1 (fun x -> assert(x = Some 1); Some 1) s) == s
*)letfindxm=Concrete.findxPervasives.comparem(*$T add; find
empty |> add 1 true |> add 2 false |> find 1
empty |> add 1 true |> add 2 false |> find 2 |> not
empty |> add 1 true |> add 2 false |> find 1
empty |> add 1 true |> add 2 false |> find 2 |> not
empty |> add 2 'y' |> add 1 'x' |> find 1 = 'x'
empty |> add 2 'y' |> add 1 'x' |> find 2 = 'y'
*)letfind_optxm=Concrete.find_optionxPervasives.comparem(*$T find_opt
find_opt 4 (add 1 2 empty) = None
find_opt 1 (add 1 2 empty) = Some 2
*)letfind_defaultdefxm=Concrete.find_defaultdefxPervasives.comparem(*$T find_default
find_default 3 4 (add 1 2 empty) = 3
find_default 3 1 (add 1 2 empty) = 2
*)letfind_firstfmap=Concrete.find_firstfmapletfind_first_optfmap=Concrete.find_first_optfmapletfind_lastfmap=Concrete.find_lastfmapletfind_last_optfmap=Concrete.find_last_optfmap(*$Q find ; add
(Q.list Q.small_int) (fun xs -> \
let of_list xs y m0 = List.fold_left (fun acc x -> add x y acc) m0 xs in \
of_list (List.filter ((<>) 100) xs) false (singleton 100 true) |> find 100)
*)letremovexm=Concrete.removexPervasives.comparem(*$Q add ; remove
(Q.list Q.small_int) (fun xs -> \
let of_list xs y m0 = List.fold_left (fun acc x -> add x y acc) m0 xs in \
List.fold_left (fun acc x -> remove x acc) (of_list xs true empty) xs |> is_empty)
*)letremove_exnxm=Concrete.remove_exnxPervasives.comparem(*$Q add ; remove_exn
(Q.list Q.small_int) (fun xs -> \
let xs = List.unique xs in \
let of_list xs y m0 = List.fold_left (fun acc x -> add x y acc) m0 xs in \
List.fold_left (fun acc x -> remove_exn x acc) (of_list xs true empty) xs |> is_empty)
*)(*$T remove_exn
try remove_exn 1 empty |> ignore ; false with Not_found -> true
*)letmemxm=Concrete.memxPervasives.comparemlet iter=Concrete.iterletmap=Concrete.mapletmapi=Concrete.mapiletfold=Concrete.foldletfoldi=Concrete.foldiletat_rank_exn=Concrete.at_rank_exn(*$Q foldi
(Q.list Q.small_int) (fun xs -> \
let m = List.fold_left (fun acc x -> add x true acc) empty xs in \
foldi (fun x _y acc -> x :: acc) m [] |> List.rev = List.sort_unique BatInt.compare xs)
*)letenum=Concrete.enum(*$Q keys
(Q.list Q.small_int) (fun xs -> \
List.fold_left (fun acc x -> add x true acc) \
empty xs |> keys |> List.of_enum \
= List.sort_unique BatInt.compare xs)
*)letbackwards=Concrete.backwardsletkeyst=BatEnum.mapfst(enumt)letvaluest=BatEnum.mapsnd(enumt)letof_enume=Concrete.of_enumPervasives.compareelet print=Concrete.printletfiltervft=Concrete.filtervftPervasives.compareletfilterft=Concrete.filterftPervasives.compareletfilter_mapft=Concrete.filter_mapftPervasives.compareletchoose=Concrete.chooseletchoose_opt=Concrete.choose_optletany=Concrete.anyletmax_binding=Concrete.max_bindingletmin_binding=Concrete.min_bindingletmax_binding_opt=Concrete.max_binding_optletmin_binding_opt=Concrete.min_binding_optletpop_min_binding=Concrete.pop_min_bindingletpop_max_binding=Concrete.pop_max_binding(*$T pop_min_binding
(empty |> add 1 true |> pop_min_binding) = ((1, true), empty)
(empty |> add 1 true |> add 2 false |> pop_min_binding) = \
((1, true), add 2 false empty)
try ignore (pop_min_binding empty); false with Not_found -> true
*)(*$T pop_max_binding
(empty |> add 1 true |> pop_max_binding) = ((1, true), empty)
(empty |> add 1 true |> add 2 false |> pop_max_binding) = \
((2, false), add 1 true empty)
try ignore (pop_max_binding empty); false with Not_found -> true
*)(*$T choose
let of_list l = of_enum (BatList.enum l) in \
(1,1) = choose (of_list [1,1])
try ignore(choose empty); false with Not_found -> true
*)(*$T choose_opt
let of_list l = of_enum (BatList.enum l) in \
Some (1,1) = choose_opt (of_list [1,1])
None = choose_opt (empty)
*)(*$T max_binding
let of_list l = of_enum (BatList.enum l) in \
(3,3) = max_binding (of_list [1,1;2,2;3,3])
try ignore(max_binding empty); false with Not_found -> true
*)(*$T max_binding_opt
let of_list l = of_enum (BatList.enum l) in \
Some (3,3) = max_binding_opt (of_list [1,1;2,2;3,3])
None = max_binding_opt empty
*)(*$T min_binding
let of_list l = of_enum (BatList.enum l) in \
(1,1) = min_binding (of_list [1,1;2,2;3,3])
try ignore(min_binding empty); false with Not_found -> true
*)(*$T min_binding_opt
let of_list l = of_enum (BatList.enum l) in \
Some (1,1) = min_binding_opt (of_list [1,1;2,2;3,3])
None = min_binding_opt empty
*)(*$T add
let s = empty |> add 1 1 |> add 2 2 in s == (s |> add 2 2)
*)(*$T remove
let s = empty |> add 1 1 |> add 2 2 in s == (s |> remove 4)
*)(*$T update
let s = empty |> add 1 1 |> add 2 2 in \
s == (s |> update 2 2 2)
*)(*$T update_stdlib
let s = empty |> add 1 1 |> add 2 2 in \
s == (s |> update_stdlib 2 (fun _ -> Some 2))
*)(*$T filter
let s = empty |> add 1 1 |> add 2 2 in \
s == (filter (fun _ _ -> true) s)
*)letof_seqs=Concrete.of_seqPervasives.comparesletadd_seqsm=Concrete.add_seqPervasives.comparesmletto_seq=Concrete.to_seqletto_rev_seq=Concrete.to_rev_seqletto_seq_fromxm=Concrete.to_seq_fromPervasives.comparexmletunion_stdlib fm1m2=Concrete.union_stdlibfPervasives.comparem1Pervasives.comparem2(*$T union_stdlib
equal (=) (union_stdlib (fun _ -> failwith "must not be called") empty empty) empty
let of_list l = of_enum (BatList.enum l) in \
equal (=) (union_stdlib (fun _ -> failwith "must not be called") (of_list [1,1;2,2]) empty) (of_list [1,1;2,2])
let of_list l = of_enum (BatList.enum l) in \
equal (=) (union_stdlib (fun _ -> failwith "must not be called") empty (of_list [1,1;2,2])) (of_list [1,1;2,2])
let of_list l = of_enum (BatList.enum l) in \
equal (=) (union_stdlib (fun _ -> failwith "must not be called") (of_list [3,3;4,4]) (of_list [1,1;2,2])) (of_list [1,1;2,2;3,3;4,4])
*)letsingletonkv=Concrete.singletonkvletfor_all=Concrete.for_allletexists=Concrete.existsletpartitionfm=Concrete.partitionfPervasives.comparemletcardinal =Concrete.cardinalletsplitkm=Concrete.splitkPervasives.comparemletadd_carry xdm=Concrete.add_carryxdPervasives.comparemletmodifyxfm=Concrete.modifyxfPervasives.comparemletmodify_defv0xfm=Concrete.modify_defv0xfPervasives.comparemletmodify_opt xfm=Concrete.modify_optxfPervasives.comparem(*$T modify_opt
empty |> add 1 false |> \
modify_opt 1 (function Some false -> Some true | _ -> assert false) |> \
find 1
empty |> add 1 true |> \
modify_opt 1 (function Some true -> None | _ -> assert false) |> \
mem 1 |> not
*)letextractxm=Concrete.extractxPervasives.comparemletpop=Concrete.popletsplitkm=Concrete.splitkPervasives.comparem(* We can't compare external primitives directly using the physical equality
operator, since two different occurrences of an external primitive are two
different closures. So we first make a local binding of [Pervasives.compare]
and only then pass it to corresponding functions from Concrete. This way the
physical equality check in [compatible_cmp] will work as needed *)letunion m1m2=let comp=Pervasives.compareinConcrete.union compm1compm2(*$T union
let m1 = empty |> add 1 1 |> add 2 2 in \
let m2 = empty |> add 2 20 |> add 3 30 in \
(union m1 m2 |> find 2 = 20) && (union m2 m1 |> find 2 = 2)
*)letunion_stdlibfm1m2=Concrete.union_stdlibfPervasives.comparem1Pervasives.comparem2letdiffm1m2=let comp=Pervasives.compareinConcrete.diffcompm1compm2letintersectmerge m1m2=let comp=Pervasives.compareinConcrete.intersectmerge compm1compm2letmergefm1m2=Concrete.mergefPervasives.comparem1m2letbindings=Concrete.bindingsletcomparecmp_valm1m2=Concrete.comparePervasives.comparecmp_valm1m2letequaleq_valm1m2=Concrete.equalPervasives.compareeq_valm1m2moduleExceptionless=structletfindkm=trySome(findkm)withNot_found->Noneletchoosem=trySome(choosem)withNot_found->Noneletanym=trySome(anym)withNot_found->NoneendmoduleInfix=structlet(-->)mapkey=find keymaplet(<--)map(key,value)=addkeyvalue mapendincludeInfixmodulePMap=struct(*$< PMap *)(**
* PMap - Polymorphic maps
*)type('k,'v)t={cmp:'k->'k->int;map:('k,'v)Concrete.map;}letcreatecmp={cmp =cmp;map=Concrete.empty}letget_cmp{cmp;_}=cmp(*$T get_cmp
get_cmp (create BatInt.compare) == BatInt.compare
*)let empty={cmp=Pervasives.compare;map=Concrete.empty}letget_cmp{cmp;_}=cmpletis_empty x=x.map=Concrete.Emptyletaddxdm=letnewmap=Concrete.addxdm.cmpm.mapinifnewmap==m.mapthenmelse{mwithmap=newmap}letupdatek1k2v2m=letnewmap=Concrete.updatek1k2v2m.cmpm.mapinifnewmap==m.mapthenmelse{mwithmap=newmap}letupdate_stdlib kfm=letnewmap=Concrete.update_stdlibkfm.cmpm.mapinifnewmap==m.mapthenmelse{mwithmap=newmap}letfindxm=Concrete.findxm.cmpm.mapletfind_opt xm=Concrete.find_optionxm.cmpm.mapletfind_defaultdefxm=Concrete.find_defaultdefxm.cmpm.map(*$T add; find
empty |> add 1 true |> add 2 false |> find 1
empty |> add 1 true |> add 2 false |> find 2 |> not
create BatInt.compare |> add 1 true |> add 2 false |> find 1
create BatInt.compare |> add 1 true |> add 2 false |> find 2 |> not
empty |> add 2 'y' |> add 1 'x' |> find 1 = 'x'
empty |> add 2 'y' |> add 1 'x' |> find 2 = 'y'
*)(*$T find_default
find_default 3 4 (add 1 2 empty) = 3
find_default 3 1 (add 1 2 empty) = 2
*)letfind_first fmap=Concrete.find_firstfmap.mapletfind_first_opt fmap=Concrete.find_first_optfmap.mapletfind_last fmap=Concrete.find_lastfmap.mapletfind_last_opt fmap=Concrete.find_last_optfmap.map(*$T update
add 1 false empty |> update 1 1 true |> find 1
add 1 false empty |> update 1 2 true |> find 2
try ignore (update 1 1 false empty); false with Not_found -> true
empty |> add 1 11 |> add 2 22 |> update 2 2 222 |> find 2 = 222
let m = empty |> add 1 11 |> add 2 22 in \
try ignore (m |> update 3 4 555); false with Not_found -> true
*)(*$Q find ; add
(Q.list Q.small_int) (fun xs -> \
let of_list xs y m0 = List.fold_left (fun acc x -> add x y acc) m0 xs in \
of_list (List.filter ((<>) 100) xs) false (singleton 100 true) |> find 100)
*)letremovexm={mwithmap=Concrete.removexm.cmpm.map}(*$Q add ; remove
(Q.list Q.small_int) (fun xs -> \
let of_list xs y m0 = List.fold_left (fun acc x -> add x y acc) m0 xs in \
List.fold_left (fun acc x -> remove x acc) (of_list xs true empty) xs |> is_empty)
*)letremove_exn xm={mwithmap=Concrete.remove_exnxm.cmpm.map}(*$Q add ; remove_exn
(Q.list Q.small_int) (fun xs -> \
let xs = List.unique xs in \
let of_list xs y m0 = List.fold_left (fun acc x -> add x y acc) m0 xs in \
List.fold_left (fun acc x -> remove_exn x acc) (of_list xs true empty) xs |> is_empty)
*)(*$T remove_exn
add 1 false empty |> remove_exn 1 |> mem 1 |> not
try remove_exn 1 empty |> ignore ; false with Not_found -> true
*)letmemxm=Concrete.memxm.cmpm.mapletiterfm=Concrete.iterfm.mapletmapfm={mwithmap=Concrete.mapfm.map}letmapifm={mwithmap=Concrete.mapifm.map}letfoldfmacc=Concrete.foldfm.mapaccletfoldifmacc=Concrete.foldifm.mapacc(*$Q foldi
(Q.list Q.small_int) (fun xs -> \
let m = List.fold_left (fun acc x -> add x true acc) (create BatInt.compare) xs in \
foldi (fun x _y acc -> x :: acc) m [] |> List.rev = List.sort_unique BatInt.compare xs)
*)letat_rank_exn im=Concrete.at_rank_exnim.mapletenumt=Concrete.enumt.map(*$Q keys
(Q.list Q.small_int) (fun xs -> \
List.fold_left (fun acc x -> add x true acc) \
(create BatInt.compare) xs |> keys |> List.of_enum \
= List.sort_unique BatInt.compare xs)
*)letbackwards t=Concrete.backwardst.maplet keyst=BatEnum.mapfst(enumt)letvaluest=BatEnum.mapsnd(enumt)letof_enum?(cmp=Pervasives.compare)e={cmp =cmp;map=Concrete.of_enumcmpe}let print?first?last?sep?kvsepprint_kprint_voutt=Concrete.print?first?last?sep?kvsepprint_kprint_voutt.mapletfiltervft={twithmap=Concrete.filtervft.mapt.cmp}letfilter_map ft={twithmap=Concrete.filter_mapft.mapt.cmp}letfilterft=letnewmap=Concrete.filterft.mapt.cmpinifnewmap==t.mapthentelse{twithmap=newmap}letmax_binding t=Concrete.max_bindingt.mapletmin_binding t=Concrete.min_bindingt.mapletmax_binding_opt t=Concrete.max_binding_optt.mapletmin_binding_opt t=Concrete.min_binding_optt.mapletpop_min_binding m=letmini,rest=Concrete.pop_min_bindingm.mapin(mini,{mwithmap=rest})letpop_max_binding m=letmaxi,rest=Concrete.pop_max_bindingm.mapin(maxi,{mwithmap=rest})letsingleton ?(cmp=Pervasives.compare)kv={cmp =cmp;map=Concrete.singletonkv}letfor_allfm=Concrete.for_allfm.mapletexistsfm=Concrete.existsfm.mapletpartition fm=let l,r=Concrete.partitionfm.cmpm.mapin{mwithmap=l},{mwithmap=r}letcardinal m=Concrete.cardinalm.mapletchoosem=Concrete.choosem.mapletchoose_opt m=Concrete.choose_optm.mapletanym=Concrete.anym.mapletsplitkm=let(l,v,r)=Concrete.splitkm.cmpm.mapin{mwithmap=l},v,{mwithmap=r}letadd_carry xdm=letmap',carry=Concrete.add_carryxdm.cmpm.mapin{mwithmap=map'},carryletmodifyxfm={mwithmap=Concrete.modifyxfm.cmpm.map}letmodify_defv0xfm={mwithmap=Concrete.modify_defv0xfm.cmpm.map}letmodify_opt xfm={mwithmap=Concrete.modify_optxfm.cmpm.map}letextractxm=letout,map'=Concrete.extractxm.cmpm.mapinout,{mwithmap=map'}letpopm=letout,map'=Concrete.popm.mapinout,{mwithmap=map'}letsplitkm=let(l,v,r)=Concrete.splitkm.cmpm.mapin{mwithmap=l},v,{mwithmap=r}letunion m1m2={m1withmap=Concrete.union m1.cmpm1.mapm2.cmpm2.map}let diffm1m2={m1withmap=Concrete.diffm1.cmpm1.mapm2.cmpm2.map}letintersectmerge m1m2={m1withmap=Concrete.intersectmerge m1.cmpm1.mapm2.cmpm2.map}letmergefm1m2={m1withmap=Concrete.heuristic_mergefm1.cmpm1.mapm2.cmpm2.map}letmerge_unsafe fm1m2={m1withmap=Concrete.mergefm1.cmpm1.mapm2.map}letof_seq?(cmp=Pervasives.compare)s={map=Concrete.of_seqcmps;cmp =cmp}letto_seqm=Concrete.to_seqm.mapletto_rev_seq m=Concrete.to_rev_seqm.mapletto_seq_from km=Concrete.to_seq_fromm.cmpkm.mapletadd_seqsm={mwithmap=Concrete.add_seqm.cmpsm.map}letunion_stdlib fm1m2={m1withmap=Concrete.union_stdlibfm1.cmpm1.mapm2.cmpm2.map}letbindings m=Concrete.bindingsm.mapletcomparecmp_valm1m2=Concrete.comparem1.cmpcmp_valm1.mapm2.mapletequaleq_valm1m2=Concrete.equal m1.cmpeq_valm1.mapm2.mapmoduleExceptionless=structletfindkm=trySome(findkm)withNot_found->Noneletchoosem=trySome(choosem)withNot_found->Noneletanym=trySome(anym)withNot_found->NoneendmoduleInfix=structlet(-->)mapkey=find keymaplet(<--)map(key,value)=addkeyvalue mapendincludeInfixend(*$>*)