12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082(*
* BatHashtbl, extra functions over hashtables.
* Copyright (C) 1996 Xavier Leroy
* 2003 Nicolas Cannasse
* 2005 Damien Doligez
* 2009 David Rajchenbach-Teller, LIFO, Universite d'Orleans
*
* 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
*)(** {6 Import the contents of {!Hashtbl}}
Note: We can't directly [include Hashtbl] as this would cause a
collision on [Make]*)type('a,'b)t=('a,'b)Hashtbl.tletcreates=Hashtbl.createsletclear=Hashtbl.clearletreset=Hashtbl.resetletadd=Hashtbl.addletcopy=Hashtbl.copyletfind=Hashtbl.findletfind_all=Hashtbl.find_allletmem=Hashtbl.memletremove=Hashtbl.removeletreplace=Hashtbl.replaceletiter=Hashtbl.iterletfold=Hashtbl.foldlethash=Hashtbl.hashtype statistics=Hashtbl.statisticsletstats=Hashtbl.stats##V>=4.07##letto_seq=Hashtbl.to_seq##V>=4.07##letto_seq_keys=Hashtbl.to_seq_keys##V>=4.07##letto_seq_values=Hashtbl.to_seq_values##V>=4.07##letadd_seq =Hashtbl.add_seq##V>=4.07##letreplace_seq =Hashtbl.replace_seq##V>=4.07##letof_seq=Hashtbl.of_seqtype('a,'b)h_bucketlist=|Empty|Consof'a*'b*('a,'b)h_bucketlisttype('a,'b)h_t={mutablesize:int;mutabledata:('a,'b)h_bucketlistarray;##V>=4##mutableseed:int;##V>=4##initial_size:int;}externalh_conv:('a,'b)t->('a,'b)h_t="%identity"externalh_make:('a,'b)h_t->('a,'b)t="%identity"##V>=4##letkey_indexhkey=##V>=4##ifObj.size(Obj.reprh)>=3##V>=4##thenHashtbl.seeded_hash(h_convh).seedkey##V>=4##land(Array.length(h_convh).data-1)##V>=4##else(Hashtbl.hashkeylandmax_int)mod(Array.length(h_convh).data)##V<4##letkey_indexhkey=(Hashtbl.hashkeylandmax_int)##V<4##mod(Array.length(h_convh).data)(* NOT EXPOSED
let resize hashfun tbl =
let odata = tbl.data in
let osize = Array.length odata in
let nsize = min (2 * osize + 1) Sys.max_array_length in
if nsize <> osize then (
let ndata = Array.create nsize Empty in
let rec insert_bucket = function
Empty -> ()
| Cons(key, data, rest) ->
insert_bucket rest; (* preserve original order of elements *)
let nidx = (hashfun key) mod nsize in
ndata.(nidx) <- Cons(key, data, ndata.(nidx)) in for i = 0 to osize - 1 do
insert_bucket odata.(i)
done;
tbl.data <- ndata;
)
*)letenumh=letrecmakeiposibuckidataicount=letpos=refiposinletbuck=refibuckinlethdata=refidatainlethcount=reficountinletforce()=(* this is a hack in order to keep an O(1) enum constructor **)if!hcount=-1then(hcount:=(h_convh).size;hdata:=Array.copy(h_convh).data;);inletrecnext()=force();match!buckwith|Empty->if!hcount=0thenraiseBatEnum.No_more_elements;incrpos;buck:=Array.unsafe_get!hdata!pos;next()|Cons(k,i,next_buck)->buck:=next_buck;decr hcount;(k,i)inletcount()=if!hcount =-1then(h_convh).sizeelse!hcountinletclone()=force();make !pos!buck!hdata!hcountinBatEnum.make~next~count~cloneinmake(-1)Empty(Obj.magic())(-1)letto_listht=fold(funkvacc->(k,v)::acc)ht[](*$T to_list
let ht = create 1 in \
add ht 1 '2'; \
to_list ht = [(1, '2')]
*)letof_listl=letres=create11inList.iter(fun(k,v)->addreskv)l;res(*$T of_list
let l = [(1,2);(2,3);(3,4)] in \
List.sort compare (to_list (of_list l))= l
*)letbindingsht=to_listht(*$T bindings
let ht = create 1 in \
add ht 1 '2'; \
bindings ht = [(1, '2')]
*)letkeysh=BatEnum.map(fun(k,_)->k)(enumh)letvaluesh=BatEnum.map(fun(_,v)->v)(enumh)letmapfh=letrecloop=function|Empty->Empty|Cons(k,v,next)->Cons(k,fkv,loopnext)inlethc=h_convhinh_make{hcwithdata=Array.maploophc.data;}(*$T map
(* non regression test for bug #354 *) \
leth = create 20 and k = (0,5)in addh k 3 ; \
let h2 = map (fun _ v -> v) h in mem h2 k
*)letmap_inplacefh=letrecloop=function|Empty->Empty|Cons(k,v,next)->Cons(k,fkv,loopnext)inBatArray.modifyloop(h_convh).data(* Helper functions to test hashtables which values are integers: *)(*$inject
let (|>) x f = f x
let printer = IO.to_string (List.print Int.print)
let to_sorted_list h = values h |> List.of_enum |> List.sort Int.compare
*)(*$= map_inplace & ~printer (let h = Enum.combine (1 -- 5) (1 -- 5) |> of_enum in \
map_inplace (fun _ x ->x+1) h ; \
to_sorted_list h) [2;3;4;5;6]
*)letremove_allhkey=lethc=h_convhinletrecloop=function|Empty->Empty|Cons(k,v,next)->if k=keythen(hc.size<-predhc.size;loopnext)elseCons(k,v,loopnext)inletpos=key_indexhkey inArray.unsafe_sethc.datapos(loop(Array.unsafe_gethc.datapos))let find_defaulthkeydefval=letrecloop=function|Empty->defval|Cons(k,v,next)->ifk=keythenvelseloopnextinletpos=key_index hkeyinloop(Array.unsafe_get(h_convh).datapos)letfind_optionhkey=letrecloop=function|Empty ->None|Cons(k,v,next)->ifk=keythenSomevelseloopnextinletpos=key_indexhkeyinloop(Array.unsafe_get(h_convh).datapos)letfind_opt=find_optionexceptionVerifiedletexistspht=tryiter(funkv->ifpkvthenraiseVerified)ht;falsewithVerified->true(*$T exists
exists (funk v -> k = v) (of_list [(1,2);(2,3);(3,3)])
not (exists (fun k v ->k = v) (of_list []))
*)letof_enum e=leth=create(ifBatEnum.fast_countethenBatEnum.counteelse0)inBatEnum.iter(fun(k,v)->addhkv)e;hletlengthh=(h_convh).sizeletis_emptyh=lengthh=0exceptionHashtbl_key_not_foundletmodify_optkeyfh=lethc=h_convhinletrecloop=function(* Inserting an element might require a resize of the hash table.
We rely on Hashtbl.addfunction to grow thehashtbl if needed
insteadof duplicating logic from the OCaml standard library. *)|Empty->raiseHashtbl_key_not_found|Cons(k,v,next)->ifk=keythenmatchf(Somev)with|Somev->Cons(key,v,next)|None->hc.size<- predhc.size;nextelseCons(k,v,loopnext)intryletpos=key_indexhkeyinArray.unsafe_sethc.datapos(loop(Array.unsafe_gethc.datapos))with|Hashtbl_key_not_found->beginmatchfNonewith|None->()|Somev->(* Add the element to make sure the hashtbl is grown correctly if
needed. *)addhkeyvend(*$T modify_opt
let h = create 3 in \
modify_opt "foo" (function None -> Some 0 | _ -> assert false) h; \
length h = 1 && find_option h "foo" = Some0
let h = create 3 in \
add h "foo" 1; \
modify_opt "foo" (function Some 1 -> None | _ -> assertfalse) h; \
lengthh= 0 && find_option h "foo" =None
*)letmodifykeyfh=lethc=h_conv hinletrecloop =function|Empty->raiseNot_found|Cons(k,v,next)->ifk=keythen (Cons(key,fv,next))elseCons(k,v,loopnext)inletpos=key_indexhkeyinArray.unsafe_sethc.datapos(loop(Array.unsafe_gethc.datapos))(*$T modify
let h = create 3 in \
add h "foo" 1; add h "bar" 2;\ modify "foo" succ h; \
values h |> List.of_enum = [ 2; 2 ]
let h =create 3 in \
try modify "baz" succ h; false \
with Not_found -> true
*)letmodify_defv0keyfh=letf'=function|None->Some(fv0)|Somev->Some(fv)inmodify_optkeyf'h(*$T modify_def
let h = create3 in \
modify_def 0 "foo" succ h; \
length h=1 && find_option h "foo" = Some 1
*)letprint ?(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)letfilteri(f:'key->'a->bool)(t:('key,'a)t)=letresult=create16initer (funka->iffkathenaddresultka)t;resultletfilteri_inplacefh=lethc=h_convhinletrecloop=function|Empty->Empty|Cons(k,v,next)->iffkvthenCons(k,v,loopnext)else(hc.size<-predhc.size;loopnext)inBatArray.modifyloophc.data(*$= filteri_inplace &~printer(let h = Enum.combine(1 -- 5) (1 -- 5) |> of_enumin \
filteri_inplace (fun _ x -> x>3) h ; \
to_sorted_list h) [4; 5]
*)letfilterft=filteri(fun_ka->fa)tletfilter_inplacefh=filteri_inplace(fun_ka->fa)h(*$= filter_inplace & ~printer:(IO.to_string (List.print Int.print))
(let h = Enum.combine (1 --5)(1 -- 5) |> of_enum in \
filter_inplace (fun x ->x>3) h ; \
to_sorted_list h) [4; 5]
*)letfilter_mapft=letresult=create16initer(funka->matchfkawith|None->()|Somev->addresultkv)t;resultletfilter_map_inplacefh=lethc=h_convhinletrecloop=function|Empty->Empty|Cons(k,v,next)->(matchfkvwith|None->hc.size<-predhc.size;loopnext|Somev'->Cons(k,v',loopnext))inBatArray.modifyloophc.data(*$= filter_map_inplace & ~printer(let h = Enum.combine (1 -- 5) (1 -- 5) |> of_enum in \
filter_map_inplace(fun_ x -> if x>3 then Some (x+1) else None) h; \
to_sorted_list h) [5; 6]
*)letmergefh1h2 =letres=create (max(lengthh1)(lengthh2))inletmay_add_reskv1v2=BatOption.may(addresk)(fkv1v2)initer(funkv1->may_add_resk(Somev1)(find_optionh2k))h1;iter(funkv2->ifnot(memh1k)thenmay_add_reskNone(Somev2))h2;res(*$inject
let union = merge (fun _ l r -> if l = None then r else l)
let inter = merge (fun _ l r -> if l = None then l else r)
let equal h1 h2 = to_sorted_list h1 = to_sorted_list h2
let empty = create 0
let h_1_5 = Enum.combine (1 -- 5) (1 -- 5) |> of_enum
let h_1_3 = Enum.combine (1 -- 3) (1 -- 3) |> of_enum
let h_3_5 = Enum.combine (3 -- 5) (3 -- 5) |> of_enum
let of_uniq_list l = List.unique l |> List.map (fun i -> i, i) |> of_list
*)(*$= merge & ~printer
[] \
(merge (fun k _ _ -> Some k) empty empty |> to_sorted_list)
[1; 2; 3; 4; 5] \
(merge (fun _ l _ -> l) h_1_5 empty |> to_sorted_list)
[] \
(merge (fun _ _ r -> r) h_1_5 empty |> to_sorted_list)
[] \
(merge (fun _ l _ -> l) empty h_1_5 |> to_sorted_list)
[1; 2; 3; 4; 5] \
(merge (fun _ _ r -> r) empty h_1_5 |> to_sorted_list)
[1; 2; 3] \
(let h = Enum.combine (3 -- 6) (13 -- 15) |> of_enum in \
merge (fun _ l _ -> l) h_1_3 h |> to_sorted_list)
[13; 14; 15] \
(let h = Enum.combine (3 -- 5) (13 -- 15) |> of_enum in \
merge (fun _ _ r -> r) h_1_3 h |> to_sorted_list)
[] \
(merge (fun _ _ _ -> None) h_1_3 h_3_5 |> to_sorted_list)
*)(*$= union & ~printer
[1; 2; 3; 4; 5] \
(union h_1_3 h_3_5 |> to_sorted_list)
*)(*$= inter & ~printer
[3] \
(inter h_1_3 h_3_5 |> to_sorted_list)
*)(*$Q equal
(Q.list Q.small_int) (fun l -> \
let h = of_uniq_list l in \
equal (inter h h) h)
(Q.list Q.small_int) (fun l -> \
let h = of_uniq_list l in \
equal (union h h) h)
(Q.list Q.small_int) (fun l -> \
let h = of_uniq_list l in \
equal (union h empty) h)
(Q.list Q.small_int) (fun l -> \
let h = of_uniq_list l in \
equal (inter h empty) empty)
(Q.pair (Q.list Q.small_int) (Q.list Q.small_int)) (fun (l1, l2) -> \
let h1 = of_uniq_list l1 and h2 = of_uniq_list l2 in \
equal (inter h1 h2) (inter h2 h1))
(Q.pair (Q.listQ.small_int) (Q.list Q.small_int)) (fun (l1, l2) -> \
let h1 = of_uniq_listl1 and h2 = of_uniq_list l2in \
equal(union h1 h2) (union h2 h1))
*)letmerge_allfh1h2=letres=create (max(lengthh1)(lengthh2))inletmay_add_reskv1v2=List.iter(addresk)(List.rev(fkv1v2))initer(funk_->letl1=find_allh1kandl2 =find_allh2kinmay_add_reskl1l2)h1;iter(funk_->matchfind_allh1kwith|[]->letl2=find_allh2kinmay_add_resk[]l2|_->()(* done above *))h2;res(*$= merge_all & ~printer
[] \
(let h1 = create 0 and h2 = create 0 in \
merge_all (fun k _ _ -> [k]) h1 h2 |> to_sorted_list)
[1; 2; 3; 4; 5] \
(let h = create 0 in \
merge_all (fun _ l _ -> l) h_1_5 h |> to_sorted_list)
[] \
(let h = create 0 in \
merge_all (fun _ _ r -> r) h_1_5 h |> to_sorted_list)
[] \
(let h = create 0 in \
merge_all (fun _ l _ -> l) h h_1_5 |> to_sorted_list)
[1; 2; 3; 4; 5] \
(let h = create 0 in \
merge_all (fun _ _ r -> r) h h_1_5 |> to_sorted_list)
[1; 2; 3] \
(let h = Enum.combine (3 -- 6) (13 -- 15) |> of_enum in \
merge_all (fun _ l _ -> l) h_1_3 h |> to_sorted_list)
[13; 14; 15] \
(let h = Enum.combine (3 -- 5) (13 -- 15) |> of_enum in \
merge_all (fun _ _ r -> r) h_1_3 h |> to_sorted_list)
[] \
(merge_all (fun _ _ _ -> []) h_1_3 h_3_5 |> to_sorted_list)
[2; 1] \
(let h1 = of_list [1, 1] in \
let h2 = copy h1 in \
Hashtbl.add h2 1 2 ;\
let h =merge_all (fun _ _ r ->r)h1 h2 in \
find_all h 1)
*)exceptionFalsified(* test if predicate holds for all key-value pairs *)letfor_allpht=tryiter(funkv->ifnot(pkv)thenraiseFalsified)ht;truewithFalsified->false(*$T for_all
for_all (fun k v -> k = v) (of_list [(1,1);(2,2);(3,3)])
not (for_all (fun k v -> k = v)(of_list [(1,1);(2,2);(3,4)]))
for_all(fun k v ->k = v) empty
*)moduleExceptionless=structletfind=find_optionletmodifykf=BatPervasives.wrap(modifykf)endmoduleInfix=structlet(-->)hk=findhklet(<--)h(k,v)=addhkvendmoduleLabels=structletlabelf=funkeydata->f~key~dataletadde~key~data=addekeydataletreplacee~key~data=replaceekeydataletiter~fe=iter(labelf)eletfor_all~fe=for_all(labelf)eletmap~fe=map(labelf)eletmap_inplace~fe=map_inplace(labelf)eletfilter~fe=filterfeletfilter_inplace~fe=filter_inplace feletfilteri~fe=filteri(labelf)eletfilteri_inplace~fe=filteri_inplace(label f)eletfilter_map~fe=filter_map(label f)eletfilter_map_inplace~fe=filter_map_inplace (labelf)eletfold~fe~init=fold(labelf)einitletexists~fe=exists(label f)eletmodify~key~f=modifykeyfletmodify_def ~default~key~f=modify_def defaultkeyfletmodify_opt~key~f=modify_optkeyfletmerge~f~left~right =mergefleftrightletmerge_all~f~left ~right=merge_allfleftrightendmoduletypeHashedType =Hashtbl.HashedTypemoduletypeS=sigtype keytype'atvalcreate:int->'atvallength:'at->intvalis_empty :'at->boolvalclear :'at->unitvalreset:'at-> unitvalcopy:'at->'atvaladd :'at->key->'a->unitvalremove:'at->key->unitvalremove_all:'at-> key->unitvalfind:'at->key->'avalfind_all:'at->key->'alistvalfind_default:'at->key->'a->'aval find_option:'at->key->'aoptionvalfind_opt:'at->key->'aoptionvalreplace:'at->key->'a->unitvalmem:'at->key->boolvaliter:(key ->'a-> unit)->'at->unitvalfor_all:(key->'a->bool)->'at->boolval fold:(key->'a->'b->'b)->'at->'b->'bvalexists:(key->'a->bool)->'at->boolval map:(key->'b->'c)->'bt->'ctvalmap_inplace :(key-> 'a->'a)->'at->unitvalfilter:('a->bool)->'at->'atvalfilter_inplace:('a->bool)->'at->unitvalfilteri:(key->'a-> bool)->'at->'atval filteri_inplace :(key->'a->bool)->'at-> unitvalfilter_map:(key->'a->'boption)->'at->'btvalfilter_map_inplace:(key->'a->'aoption)->'at->unitvalmodify:key->('a->'a)->'at->unitvalmodify_def :'a->key->('a->'a)->'at->unitvalmodify_opt:key->('aoption ->'aoption)->'at->unitvalmerge:(key->'aoption->'boption->'coption)->'at->'bt->'ctvalmerge_all:(key ->'alist->'blist->'clist)->'at->'bt->'ctval stats:'at->statistics##V>=4.07##valto_seq:'at->(key*'a)Seq.t##V>=4.07##valto_seq_keys:_t->keySeq.t##V>=4.07##valto_seq_values :'at-> 'aSeq.t##V>=4.07##valadd_seq:'at->(key*'a)Seq.t->unit##V>=4.07##val replace_seq :'at->(key*'a)Seq.t->unit##V>=4.07##valof_seq:(key *'a)Seq.t->'atvalkeys:'at->keyBatEnum.tvalvalues:'at->'aBatEnum.tvalenum:'at->(key*'a)BatEnum.tvalto_list:'at->(key*'a)listvalof_enum:(key*'a)BatEnum.t->'atvalof_list:(key*'a)list->'atvalprint:?first:string->?last:string->?sep:string->('aBatInnerIO.output->key->unit)->('aBatInnerIO.output ->'b->unit)->'aBatInnerIO.output->'bt->unit(** Operations on {!Hashtbl} without exceptions.*)module Exceptionless:sigvalfind:'at->key->'aoptionvalmodify:key->('a->'a)->'at->(unit,exn)BatPervasives.resultend(** Infix operators over a {!BatHashtbl} *)moduleInfix:sigval(-->):'at->key->'a(** [tbl-->x] returns the current binding of [x] in [tbl],
or raises [Not_found] if no such binding exists.
Equivalent to [Hashtbl.find tbl x]*)val(<--):'at->key*'a->unit(** [tbl<--(x, y)] adds a binding of [x] to [y] in table [tbl].
Previous bindings for [x] are not removed, but simply
hidden. That is, after performing {!Hashtbl.remove}[ tbl x],
the previous binding for [x], if any, is restored.
(Same behavior as with association lists.)
Equivalent to [Hashtbl.add tbl x y]*)end(** Operations on {!Hashtbl} with labels.
This module overrides a number of functions of {!Hashtbl} by
functions in which some arguments require labels. These labels are
there to improve readability and safety and to let you change the
order of arguments to functions. In every case, the behavior of the
function is identical to that of the corresponding function of {!Hashtbl}.
*)module Labels :sigvaladd:'at->key:key->data:'a->unitvalreplace:'at->key:key->data:'a->unitvaliter :f:(key:key->data:'a->unit)->'at->unitvalfor_all:f:(key:key->data:'a->bool)->'at->boolvalmap:f:(key:key->data:'a->'b)->'at->'btvalmap_inplace:f:(key:key ->data:'a->'a)->'at->unitvalfilter:f:('a->bool)->'at->'atvalfilter_inplace:f:('a->bool)->'at->unitvalfilteri:f:(key:key->data:'a->bool)-> 'at->'atvalfilteri_inplace:f:(key:key->data:'a->bool)->'at->unitvalfilter_map:f:(key:key->data:'a->'boption)->'at->'btvalfilter_map_inplace:f:(key:key->data:'a->'aoption)->'at->unitvalfold:f:(key:key->data:'a->'b->'b)->'at->init:'b->'bvalexists :f:(key:key-> data:'a->bool)->'at->boolvalmodify:key:key->f:('a->'a)->'at->unitvalmodify_def:default:'a->key:key->f:('a->'a)->'at->unitval modify_opt:key:key->f:('aoption ->'aoption)->'at->unitvalmerge:f:(key->'aoption->'boption->'coption)->left:'at->right:'bt->'ctvalmerge_all :f:(key->'alist->'blist ->'clist)->left:'at->right:'bt->'ctendendmoduleMake(H:HashedType):(Swithtypekey=H.tandtype'at='aHashtbl.Make(H).t)=structincludeHashtbl.Make(H)externalto_hash:'at->(key,'a)Hashtbl.t="%identity"externalof_hash:(key,'a)Hashtbl.t->'at="%identity"(* Warning: these two externals are unsafe, as they forget about the user-provided
HashtedType implementation. They are used to avoid code duplication for functions
that do *not* use the hashing function, but only traverse the bucket structure. *)(* type key = H.t
type 'a hashtbl = (key, 'a) t
type 'a t = 'a hashtbl
let create = create
let clear = clear
let copy = copy
let safehash key = (H.hash key) land max_int
let add h key info =
let h = h_conv h in
let i = (safehash key) mod (Array.length h.data) in
let bucket = Cons(key, info, h.data.(i)) in
h.data.(i) <- bucket;
h.size <- succ h.size;
if h.size > Array.length h.data lsl 1 then resize safehash h
let remove h key =
let h = h_conv h in
let rec remove_bucket = function
Empty ->
Empty
| Cons(k, i, next) ->
if H.equal k key
then begin h.size <- pred h.size; next end
else Cons(k, i, remove_bucket next) in
let i = (safehash key) mod (Array.length h.data) in
h.data.(i) <- remove_bucket h.data.(i)
let rec find_rec key = function
Empty ->
raise Not_found
| Cons(k, d, rest) ->
if H.equal key k then d else find_rec key rest
let find h key =
let h = h_conv h in
match h.data.((safehash key) mod (Array.length h.data)) with
Empty -> raise Not_found
| Cons(k1, d1, rest1) ->
if H.equal key k1 then d1 else
match rest1 with
Empty -> raise Not_found
| Cons(k2, d2, rest2) ->
if H.equal key k2 then d2 else
match rest2 with
Empty -> raise Not_found
| Cons(k3, d3, rest3) ->
if H.equal key k3 then d3 else find_rec key rest3
let find_all h key =
let rec find_in_bucket = function
Empty ->
[]
| Cons(k, d, rest) ->
if H.equal k key
then d :: find_in_bucket rest
else find_in_bucket rest in
find_in_bucket h.data.((safehash key) mod (Array.length h.data))
let replace h key info =
let rec replace_bucket = function
Empty ->
raise Not_found
| Cons(k, i, next) ->
if H.equal k key
then Cons(k, info, next)
else Cons(k, i, replace_bucket next) in
let i = (safehash key) mod (Array.length h.data) in
let l = h.data.(i) in
try
h.data.(i) <- replace_bucket l
with Not_found ->
h.data.(i) <- Cons(key, info, l);
h.size <- succ h.size;
if h.size > Array.length h.data lsl 1 then resize safehash h
let mem h key =
let recmem_in_bucket = function | Empty ->
false
| Cons(k, d, rest) ->
H.equal k key || mem_in_bucket rest in
mem_in_bucket h.data.((safehash key) mod (Array.length h.data))*)letkey_indexhkey=(H.hashkey)land(Array.length(h_conv(to_hashh)).data-1)letiter=iterletfor_allpht=for_all p(to_hashht)letfold=foldletlength=lengthletstats=stats(* the functions here do not hash values, they only traverse the buckets *)letenumh=enum(to_hashh)letto_listh=to_list(to_hashh)letvaluesh=values(to_hashh)letkeysh=keys(to_hash h)letmap(f:key->'a->'b)h=of_hash(mapf(to_hashh))letexists(f:key->'a->bool)h=existsf(to_hashh)(* We can use polymorphicfilteri since we do not use the key at all for inline ops *)letmap_inplace(f:key->'a->'b)h=map_inplacef(to_hashh)letfilteri_inplacefh=filteri_inplace f(to_hashh)letfilter_inplacefh=filter_inplacef(to_hashh)##V<4.3##letfilter_map_inplacefh=filter_map_inplacef(to_hashh)(* these functions do need to hash values, so we cannot use {to,of}_hash *)letof_enume=lettbl=create11inBatEnum.iter(fun(k,v)->addtblkv)e;tbllet of_listli=lettbl=create11inList.iter(fun(k,v)-> addtblkv)li;tblletfind_optionhkey=lethc=h_conv(to_hashh)inletrecloop=function|Empty->None|Cons(k,v,next)->ifH.equal kkeythenSomevelseloop nextinletpos=key_indexhkeyinloop(Array.unsafe_gethc.datapos)##V<4.05##letfind_opt=find_optionletfind_defaulthkeydefval=lethc=h_conv(to_hashh)inletrecloop=function|Empty ->defval|Cons(k,v,next)->ifH.equalkkeythenvelseloopnextinletpos=key_indexhkeyinloop(Array.unsafe_gethc.datapos)letremove_allhkey=lethc=h_conv(to_hashh)inletrecloop =function|Empty->Empty|Cons(k,v,next)->ifH.equalkkeythen beginhc.size<-pred hc.size;loop nextendelseCons(k,v,loopnext)inletpos=key_indexhkeyinArray.unsafe_sethc.datapos(loop(Array.unsafe_gethc.datapos))letis_emptyh=lengthh=0letprint?first?last?sepprint_kprint_voutt=print?first?last?sepprint_kprint_vout(to_hasht)letfilterift=letresult=create16initer(fun ka->iffkathenaddresultka)t;resultletfilterft=filteri(fun_ka->fa)tletfilter_mapft=letresult=create16initer(funka->matchfkawith|None->()|Somev->addresultkv)t;resultletmodify_optkeyfh=lethc=h_conv(to_hashh)inletrecloop=function(* Inserting an element might require a resize of the hash table.
We rely onHashtbl.add function to grow the hashtbl if needed
instead of duplicating logic from the OCaml standard library. *)|Empty->raiseHashtbl_key_not_found|Cons(k,v,next)->if H.equal kkeythenmatchf(Somev)with|Somev->Cons(key,v,next)|None->hc.size<-predhc.size;nextelseCons(k,v,loopnext)intryletpos=key_indexhkeyinArray.unsafe_sethc.data pos(loop(Array.unsafe_get hc.datapos))with|Hashtbl_key_not_found ->beginmatchfNonewith|None ->()|Somev->(* Add the elementtomake sure the hashtbl is grown correctly if
needed. *)addhkeyvendletmodify keyfh=lethc=h_conv(to_hash h)inletrecloop=function|Empty->raiseNot_found|Cons(k,v,next)->ifH.equalkkeythen(Cons(key,fv,next))elseCons(k,v,loopnext)inletpos=key_indexhkeyinArray.unsafe_sethc.datapos(loop(Array.unsafe_gethc.datapos))letmodify_def v0keyfh=letf'=function|None->Some(fv0)|Somev->Some(fv)inmodify_optkeyf'hletmergefab=letres=create(max(lengtha)(lengthb))inlet may_add_reskv1v2=BatOption.may(addresk)(fkv1v2)initer(funkv1->may_add_resk(Somev1)(find_optionbk))a;iter(funkv2->ifnot(memak)thenmay_add_res kNone(Somev2))b;resletmerge_allfab=letres=create(max(lengtha)(lengthb))inletmay_add_reskv1v2=List.iter(addresk)(List.rev(fkv1v2))initer(funk_->letl1=find_all akandl2=find_all bkinmay_add_reskl1l2)a;iter(funk_->matchfind_allakwith|[]->letl2=find_allbkinmay_add_resk[]l2|_->()(* done above *))b;resmoduleLabels=structletlabelf=funkeydata->f~key~dataletadde~key~data=addekeydataletreplacee~key~data=replaceekeydataletiter~fe=iter(labelf)eletfor_all~fe=for_all(labelf)eletmap~fe=map(labelf)eletmap_inplace~fe=map_inplace(labelf)eletfilter~fe=filterfeletfilter_inplace ~fe=filter_inplacefeletfilteri~fe=filteri(labelf)eletfilteri_inplace ~fe=filteri_inplace(labelf)eletfilter_map~fe=filter_map(labelf)eletfilter_map_inplace~fe=filter_map_inplace(labelf)eletfold~fe~init=fold(labelf)einitletexists~fe=exists(labelf)eletmodify~key~f=modifykeyfletmodify_def~default ~key~f=modify_defdefaultkeyfletmodify_opt~key~f=modify_optkeyfletmerge~f~left~right=mergefleftrightletmerge_all~f~left~right=merge_allfleftrightendmoduleExceptionless =structletfind=find_optionletmodifykf=BatPervasives.wrap(modifykf)endmoduleInfix=structlet(-->)hk=findhklet(<--)h(k,v)=addhkvendendmoduleCap=structtype('a,'b,'c)t=('a,'b)Hashtbl.tconstraint 'c=[<`Read|`Write]letcreate=createexternalof_table:('a,'b)Hashtbl.t->('a,'b,_)t="%identity"externalto_table:('a,'b,[`Read|`Write])t->('a,'b)Hashtbl.t="%identity"externalread_only:('a,'b,[>`Read])t->('a,'b,[`Read])t="%identity"externalwrite_only:('a,'b,[>`Write])t->('a,'b,[`Write])t="%identity"letlength=lengthletis_empty=is_emptyletadd=addletremove=removeletremove_all=remove_allletreplace=replaceletcopy=copyletclear=clearletfind=findletfind_all=find_allletfind_default=find_defaultletfind_option=find_optionletexists=existsletmem=memletiter=iterletfor_all=for_allletfold=foldletmap=mapletmap_inplace =map_inplaceletfilter=filterletfilter_inplace=filter_inplaceletfilteri=filteriletfilteri_inplace=filteri_inplaceletfilter_map =filter_mapletfilter_map_inplace=filter_map_inplaceletmodify=modifyletmodify_def=modify_defletmodify_opt=modify_optletstats=statsletkeys=keysletvalues=valuesletenum=enumlet to_list=to_listletof_enum =of_enumletof_list=of_listletprint=printletfilter=filterletfilteri=filteriletfilter_map=filter_mapletmerge=mergeletmerge_all=merge_allmoduleLabels=structletlabelf=funkeydata->f~key~dataletadde~key~data=addekeydataletreplacee~key~data=replaceekeydataletiter~fe=iter(labelf)eletfor_all~fe=for_all(labelf)eletmap~fe=map(labelf)eletmap_inplace~fe=map_inplace(labelf)eletfilter~fe=filterfeletfilter_inplace ~fe=filter_inplacefeletfilteri~fe=filteri(labelf)eletfilteri_inplace ~fe=filteri_inplace(labelf)eletfilter_map~fe=filter_map(labelf)eletfilter_map_inplace~fe=filter_map_inplace(labelf)eletfold~fe~init=fold(labelf)einitletexists~fe=exists(labelf)eletmodify~key~f=modifykeyfletmodify_def~default ~key~f=modify_defdefaultkeyfletmodify_opt~key~f=modify_optkeyfletmerge~f~left~right=mergefleftrightletmerge_all~f~left~right=merge_allfleftrightendmoduleExceptionless=structletfind=find_optionletmodifykf=BatPervasives.wrap(modifykf)endend