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 *intlet height=function|Node(_,_,_,_,h)->h|Empty->0let empty=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=heightlandhr=heightrinNode(l,x,d,r,(ifhl>=hrthenhl+1elsehr+1))letballxdr=lethl=matchlwithEmpty->0|Node(_,_,_,_,h)->hinlethr=matchrwithEmpty->0|Node(_,_,_,_,h)->hinifhl>hr+2thenbeginmatchlwithEmpty->invalid_arg"Map.bal"|Node(ll,lv,ld,lr,_)->ifheight ll>=height lrthencreate lllvld(createlrxdr)elsebeginmatchlrwithEmpty->invalid_arg"Map.bal"|Node(lrl,lrv,lrd,lrr,_)->create(create lllvldlrl)lrvlrd(createlrrxdr)endendelseifhr>hl+2thenbeginmatchrwithEmpty->invalid_arg"Map.bal"|Node(rl,rv,rd,rr,_)->ifheight rr>=height rlthencreate(createlxdrl)rvrdrrelsebeginmatchrlwithEmpty->invalid_arg"Map.bal"|Node(rll,rlv,rld,rlr,_)->create(createlxdrll)rlvrld(createrlr rvrdrr)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_binding s=letmini =ref(get_roots)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)kvrinlet others=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_binding s=letmaxi =ref(get_roots)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)inlet others=loopsin(!maxi,others)letrecremove_min_binding =function|Node(Empty,_,_,r,_)->r|Node(l,k,v,r,_)->bal(remove_min_bindingl)kvr|Empty->raiseNot_foundlet merget1t2=matcht1,t2with|Empty,_->t2|_,Empty->t1|_->letk,v=min_bindingt2inbalt1kv(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)inloopmapletfind xcmpmap=letrecloop=function|Node(l,k,v,r,_)->letc=cmpxkinifc<0then looplelseifc>0then looprelsev|Empty->raiseNot_foundinloopmapletrecfind_first_helper_foundk0v0f=function|Empty->(k0,v0)|Node(l,k,v,r,_)->iffkthenfind_first_helper_foundkvflelsefind_first_helper_foundk0v0frletrecfind_first fm=matchmwith|Empty->raiseNot_found|Node(l,k,v,r,_)->iffkthenfind_first_helper_foundkvflelsefind_firstfrletrecfind_first_opt fm=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_last fm=matchmwith|Empty->raiseNot_found|Node(l,k,v,r,_)->iffkthenfind_last_helper_foundkvfrelsefind_lastflletrecfind_last_opt fm=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_option xcmpmap=trySome(find xcmpmap)withNot_found->Noneletfind_default def xcmpmap=tryfind xcmpmapwithNot_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)inloopmapletupdate k1k2v2cmpmap=ifcmp k1k2<>0thenadd k2 v2cmp(remove_exnk1cmpmap)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|Some data->Node(Empty,x,data,Empty,1)end|Node(l,v,d,r,h)asm->letc=cmpxvinifc=0thenbeginmatchf(Somed)with|None->mergelr|Some data->ifd==datathenmelseNode(l,x,data,r,h)endelseifc<0thenletll=update_stdlibxfcmplinifl==llthenmelseballlvdrelseletrr=update_stdlibxfcmprinifr==rrthenmelseballvdrrletmemxcmpmap=letrecloop=function|Node(l,k,_v,r,_)->letc=cmpxkinc=0||loop(ifc<0thenlelser)|Empty->falseinloopmapletiter fmap=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)inloopmapletmapi fmap=letrecloop=function|Empty->Empty|Node(l,k,v,r,h)->(* ensure evaluation in increasing order *)letl'=looplinletv'=fkvinletr'=looprinNode(l',k,v',r',h)inloopmapletfold fmapacc=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))rinloopaccmapexception Foundletat_rank_exn im=ifi<0theninvalid_arg "Map.at_rank_exn: i < 0";letres=ref(get_rootm)in(* raises Not_found if empty *)trylet(_:int)=foldi(funkvj->ifj<>ithenj+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
*)letsingleton xd=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_binding kv=function|Empty->singletonkv|Node(l,x,d,r,_h)->bal(add_min_bindingkvl)xdrletrecadd_max_binding kv=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+2thenbal lllvld(joinlrvdr)elseifrh>lh+2thenbal(joinlvdrl)rvrdrrelsecreatelvdr(* split also is from stdlib 3.12 *)letrec splitkeycmp=function|Empty->(Empty,None,Empty)|Node(l,x,d,r,_)->letc=cmpkeyxinifc=0then(l,Somed,r)elseifc<0thenlet(ll,pres,rl)=splitkeycmplin(ll,pres,joinrlxdr)elselet(lr,pres,rr)=splitkeycmprin(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)linloop 0mapletrecbindings_aux accu=function|Empty->accu|Node(l,v,d,r,_)->bindings_aux((v,d)::bindings_auxaccur)lletbindingss=bindings_aux[]sletreccons_iter st=matchswith|Empty->t|Node(l,k,v,r,_)->cons_iterl(C(k,v,r,t))letrecrev_cons_iter st=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_next l()=match!lwithE->raiseBatEnum.No_more_elements|C(k,v,m,t)->l:=cons_itermt;(k,v)letenum_backwards_next l()=match!lwithE->raiseBatEnum.No_more_elements|C(k,v,m,t)->l:=rev_cons_itermt;(k,v)letenum_count l()=letrecauxn=function|E->n|C(_,_,m,t)->aux(n+1+cardinalm)tinaux0!lletenumt=letrecmakel=letl=reflinlet clone()=make!linBatEnum.make~next:(enum_nextl)~count:(enum_countl)~cloneinmake(cons_itertE)letbackwards t=letrecmakel=letl=reflinlet clone()=make!linBatEnum.make~next:(enum_backwards_nextl)~count:(enum_countl)~cloneinmake(rev_cons_itertE)letkeyst=BatEnum.mapfst(enumt)let valuest=BatEnum.mapsnd(enumt)letof_enumcmpe=BatEnum.fold(funm(k,v)->addkvcmpm)emptyelet print?(first="{\n")?(last="\n}")?(sep=",\n")?(kvsep=": ")print_kprint_voutt=BatEnum.print ~first~last ~sep(funout(k,v)->BatPrintf.fprintfout"%a%s%a"print_kkkvsepprint_vv)out(enumt)(*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&&loop l&&looprinloopmapletexistsfmap=letrecloop=function|Empty->false|Node(l,k,v,r,_)->fkv||loop l||looprinloopmapletpartition fcmpmap=letrecloopm1m2=function|Empty-> (m1,m2)|Node(l,k,v,r,_)->letm1,m2=loopm1m2linletm1,m2=loopm1m2riniffkvthen(addkvcmpm1,m2)else(m1,addkvcmpm2)inloopemptyemptymaplet choose=min_binding(*$= choose
(empty |> add 0 1 |> add 1 1 |> choose) (empty |> add 1 1 |> add 0 1 |> choose)
*)letchoose_opt m=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,balnlkvrelseletvout,nr=looprinvout,ballkvnr|Empty->raiseNot_foundinloopmapletpopmap=match mapwith|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. *)letconcat t1t2=match(t1,t2)with(Empty,t)->t|(t,Empty)->t|(_,_)->let(x,d)=min_bindingt2injoint1xd(remove_min_bindingt2)letconcat_or_joint1 vdt2=matchdwith|Somed->joint1 vdt2|None->concat t1t2letmergefcmp12s1s2=letrecloops1s2=match(s1,s2)with|(Empty,Empty)->Empty|(Node(l1,v1,d1,r1,h1),_)whenh1>=height s2->let(l2,d2,r2)=splitv1cmp12s2in(* TODO force correct evaluation order *)concat_or_join(loopl1 l2)v1(fv1(Somed1)d2)(loopr1r2)|(_,Node(l2,v2,d2,r2,_h2))->let(l1,d1,r1)=splitv2cmp12s1inconcat_or_join(loopl1 l2)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_optionkcmp2s2)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_bindings)));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_seq cmps=add_seqcmpsemptyletrecseq_of_iter m()=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_iter m()=matchmwith|E->BatSeq.Nil|C(k,v,r,e)->BatSeq.Cons((k,v),rev_seq_of_iter(rev_cons_iterre))letto_rev_seq m=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)(enumm1)(enumm2)let equal ckeyeq_val m1m2=BatEnum.equal(fun(k1,v1)(k2,v2)->ckeyk1k2=0&&eq_val v1v2)(enumm1)(enumm2)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->'atval find:key->'at->'avalfind_opt:key->'at->'aoptionvalfind_default:'a-> key->'at->'aval find_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)*'atval mem: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->'atval filter_map:(key->'a->'boption)->'at->'btvalcompare:('a->'a->int)->'at->'at->intvalequal:('a->'a->bool)->'at->'at->boolval keys:_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)val choose_opt:'at-> (key*'a)optionvalany:'at->(key*'a)valsplit:key ->'at-> ('at*'aoption*'at)val partition:(key->'a->bool)->'at->'at*'atvalsingleton:key->'a->'atvalbindings:'at->(key*'a)listvalenum:'at->(key*'a)BatEnum.tval backwards:'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.tval to_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->'atval add_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:sigval find:key->'at->'aoptionvalchoose:'at-> (key*'a)optionval any:'at-> (key*'a)optionendmoduleInfix:sigval(-->):'at->key->'aval(<--):'at->key*'a->'atendmoduleLabels:sigvaladd:key:key->data:'a->'at->'atval iter:f:(key:key->data:'a->unit)->'at->unitvalmap:f:('a->'b)->'at->'btval mapi: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"letcardinalt=Concrete.cardinal(impl_of_tt)letenumt=Concrete.enum(impl_of_tt)letbackwardst=Concrete.backwards(impl_of_tt)letkeyst=Concrete.keys(impl_of_tt)letvaluest=Concrete.values(impl_of_tt)letupdatek1k2v2t=t_of_impl(Concrete.updatek1k2v2Ord.compare(impl_of_tt))letupdate_stdlibkfm=t_of_impl(Concrete.update_stdlibkfOrd.compare(impl_of_tm))letfind_defaultdkt=Concrete.find_defaultdkOrd.compare(impl_of_tt)letfind_optkt=Concrete.find_optionkOrd.compare(impl_of_tt)letfind_firstft=Concrete.find_firstf(impl_of_tt)letfind_first_optft=Concrete.find_first_optf(impl_of_tt)letfind_lastft=Concrete.find_lastf(impl_of_tt)letfind_last_optft=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))letprint?first?last?sep?kvsep print_kprint_voutt=Concrete.print?first?last?sep?kvsep print_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_mapft=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_bindingt=Concrete.min_binding(impl_of_tt)letpop_min_bindingt=letmini,rest=Concrete.pop_min_binding(impl_of_tt)in(mini,t_of_implrest)letmax_bindingt=Concrete.max_binding(impl_of_tt)letpop_max_bindingt=letmaxi,rest=Concrete.pop_max_binding(impl_of_tt)in(maxi,t_of_implrest)letmax_binding_optt=Concrete.max_binding_opt(impl_of_tt)letmin_binding_optt=Concrete.min_binding_opt(impl_of_tt)letchooset=Concrete.choose(impl_of_tt)letchoose_optt=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)letpartitionpt=letl,r=Concrete.partitionpOrd.compare(impl_of_tt)in(t_of_impll,t_of_implr)letremove_exnxm=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_optxfm=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'letsingletonkv=t_of_impl(Concrete.singletonkv)letbindingst=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_seqm=Concrete.to_rev_seq(impl_of_tm)letto_seq_fromkm=Concrete.to_seq_fromOrd.comparek(impl_of_tm)letadd_to_listxdata m=letadd=functionNone->Some[data]|Some l->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=findkeymaplet(<--)map(key,value)=addkeyvaluemapendmoduleLabels=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=comparecmpabletequal~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 |> add11))
*)letaddxdm=Concrete.addxdPervasives.comparemletupdatek1k2v2m=Concrete.updatek1k2v2Pervasives.comparemletupdate_stdlibkfm=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
*)let find_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.comparemletiter=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)letvalues t=BatEnum.mapsnd(enum t)letof_enume=Concrete.of_enumPervasives.compareeletprint=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_carryxdm=Concrete.add_carryxdPervasives.comparemletmodifyxfm=Concrete.modifyxfPervasives.comparemletmodify_defv0xfm=Concrete.modify_defv0xfPervasives.comparemletmodify_optxfm=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 *)letunionm1m2=letcomp=Pervasives.compareinConcrete.unioncompm1compm2(*$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_stdlib fm1m2=Concrete.union_stdlibfPervasives.comparem1Pervasives.comparem2letdiffm1m2=letcomp=Pervasives.compareinConcrete.diff compm1compm2letintersectmergem1m2=letcomp=Pervasives.compareinConcrete.intersectmergecompm1compm2letmergefm1m2=Concrete.mergefPervasives.comparem1m2letbindings=Concrete.bindingsletcomparecmp_valm1m2=Concrete.comparePervasives.comparecmp_valm1m2letequaleq_valm1m2=Concrete.equalPervasives.compare eq_valm1m2moduleExceptionless=structletfindkm=trySome(findkm)withNot_found->Noneletchoosem=trySome(choosem)withNot_found->Noneletanym=trySome(anym)withNot_found ->NoneendmoduleInfix=structlet(-->)mapkey=findkeymaplet(<--)map(key,value)=add keyvaluemapendincludeInfixmodulePMap=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
*)letempty={cmp=Pervasives.compare;map=Concrete.empty}letget_cmp{cmp;_}=cmpletis_emptyx=x.map=Concrete.Emptyletaddxdm=letnewmap=Concrete.addxdm.cmpm.mapinifnewmap==m.mapthenmelse{mwithmap=newmap}letupdatek1k2v2m=letnewmap=Concrete.updatek1k2v2m.cmpm.mapinifnewmap==m.mapthenmelse{mwithmap=newmap}letupdate_stdlibkfm=letnewmap=Concrete.update_stdlibkfm.cmpm.mapinifnewmap==m.mapthenmelse{mwithmap=newmap }letfindxm=Concrete.findxm.cmpm.mapletfind_optxm=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
*)let find_firstfmap=Concrete.find_firstfmap.mapletfind_first_optfmap=Concrete.find_first_optfmap.mapletfind_lastfmap=Concrete.find_lastfmap.mapletfind_last_optfmap=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_exnxm={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_exnim=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)
*)letbackwardst=Concrete.backwardst.mapletkeyst=BatEnum.mapfst(enumt)letvalues t=BatEnum.mapsnd(enumt)letof_enum?(cmp=Pervasives.compare)e={cmp=cmp;map=Concrete.of_enumcmpe}letprint?first?last?sep?kvsep print_kprint_voutt=Concrete.print?first?last?sep?kvsep print_kprint_voutt.mapletfiltervft={twithmap=Concrete.filtervft.mapt.cmp}letfilter_mapft={twithmap=Concrete.filter_mapft.mapt.cmp}letfilterft=letnewmap=Concrete.filterft.mapt.cmpinifnewmap==t.mapthentelse{twithmap=newmap}letmax_bindingt=Concrete.max_bindingt.mapletmin_bindingt=Concrete.min_bindingt.mapletmax_binding_optt=Concrete.max_binding_optt.mapletmin_binding_optt=Concrete.min_binding_optt.mapletpop_min_bindingm=letmini,rest=Concrete.pop_min_bindingm.mapin(mini,{mwithmap=rest})letpop_max_bindingm=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.mapletpartitionfm=letl,r=Concrete.partitionfm.cmpm.mapin{mwithmap=l},{mwithmap=r}letcardinalm=Concrete.cardinalm.mapletchoosem=Concrete.choosem.mapletchoose_optm=Concrete.choose_optm.mapletanym=Concrete.anym.mapletsplitkm=let(l,v,r)=Concrete.splitkm.cmpm.mapin{mwithmap=l},v,{mwithmap=r}letadd_carryxdm=letmap',carry=Concrete.add_carryxdm.cmpm.mapin{mwithmap=map'},carryletmodifyxfm={mwithmap=Concrete.modifyxfm.cmpm.map}letmodify_defv0xfm={mwithmap=Concrete.modify_defv0xfm.cmpm.map}letmodify_optxfm={mwithmap=Concrete.modify_optxfm.cmpm.map}letextractxm=letout,map'=Concrete.extractxm.cmp m.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}letunionm1m2={m1withmap=Concrete.unionm1.cmp m1.mapm2.cmpm2.map }letdiffm1m2={m1withmap=Concrete.diffm1.cmp m1.mapm2.cmpm2.map}letintersectmergem1m2={m1withmap=Concrete.intersectmergem1.cmp m1.mapm2.cmpm2.map}letmergefm1m2={m1withmap=Concrete.heuristic_mergefm1.cmp m1.mapm2.cmpm2.map}letmerge_unsafefm1m2={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_seqm=Concrete.to_rev_seqm.mapletto_seq_fromkm=Concrete.to_seq_fromm.cmpkm.mapletadd_seqsm={mwithmap=Concrete.add_seqm.cmpsm.map}letunion_stdlibfm1m2={m1withmap=Concrete.union_stdlibfm1.cmp m1.mapm2.cmpm2.map}letbindingsm=Concrete.bindingsm.mapletcomparecmp_valm1m2=Concrete.comparem1.cmpcmp_valm1.mapm2.mapletequaleq_valm1m2=Concrete.equalm1.cmp eq_valm1.mapm2.mapmoduleExceptionless=structletfindkm=trySome(findkm)withNot_found->Noneletchoosem=trySome(choosem)withNot_found->Noneletanym=trySome(anym)withNot_found->NoneendmoduleInfix=structlet(-->)mapkey=findkeymaplet(<--)map(key,value)=addkeyvaluemapendincludeInfixend(*$>*)