123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418(* This file is free software, part of containers. See file "license" for more details. *)(** {1 Extension to the standard Hashtbl} *)type'aiter=('a->unit)->unittype'aeq='a->'a->booltype'ahash='a->inttype'aprinter=Format.formatter->'a->unit(** {2 Polymorphic tables} *)modulePoly=structletgettblx=trySome(Hashtbl.findtblx)withNot_found->Noneletget_ortblx~default=tryHashtbl.findtblxwithNot_found->defaultletkeystblk=Hashtbl.iter(funkey_->kkey)tblletvaluestblk=Hashtbl.iter(fun_v->kv)tblletkeys_listtbl=Hashtbl.fold(funk_a->k::a)tbl[]letvalues_listtbl=Hashtbl.fold(fun_va->v::a)tbl[]letadd_listtblkv=letl=tryHashtbl.findtblkwithNot_found->[]inHashtbl.replacetblk(v::l)letincr?(by=1)tblx=letn=get_ortblx~default:0inifn+by<=0thenHashtbl.removetblxelseHashtbl.replacetblx(n+by)letdecr?(by=1)tblx=tryletn=Hashtbl.findtblxinifn-by<=0thenHashtbl.removetblxelseHashtbl.replacetblx(n-by)withNot_found->()letmap_listfh=Hashtbl.fold(funxyacc->fxy::acc)h[]letto_itertblk=Hashtbl.iter(funkeyv->k(key,v))tblletadd_itertbli=i(fun(k,v)->Hashtbl.addtblkv)letadd_iter_with~ftbli=i(fun(k,v)->matchHashtbl.findtblkwith|exceptionNot_found->Hashtbl.addtblkv|v2->Hashtbl.replacetblk(fkvv2))letadd_seqtblseq=Seq.iter(fun(k,v)->Hashtbl.addtblkv)seqletadd_seq_with~ftblseq=Seq.iter(fun(k,v)->matchHashtbl.findtblkwith|exceptionNot_found->Hashtbl.addtblkv|v2->Hashtbl.replacetblk(fkvv2))seq(* helper for building hashtables by bulk mutation *)let[@inline]mk_tbl_fx=lettbl=Hashtbl.create32inftblx;tblletof_iteri=mk_tbl_add_iteriletof_seqi=mk_tbl_add_seqiletof_iter_with~fi=mk_tbl_(add_iter_with~f)iletof_seq_with~fi=mk_tbl_(add_seq_with~f)iletadd_iter_counttbli=i(funk->incrtblk)letadd_seq_counttblseq=Seq.iter(funk->incrtblk)seqletof_iter_counti=mk_tbl_add_iter_countiletof_seq_counti=mk_tbl_add_seq_countiletto_listtbl=Hashtbl.fold(funkvl->(k,v)::l)tbl[]letof_listl=lettbl=Hashtbl.create32inList.iter(fun(k,v)->Hashtbl.addtblkv)l;tblletof_list_with~fl=lettbl=Hashtbl.create32inList.iter(fun(k,v)->matchHashtbl.findtblkwith|exceptionNot_found->Hashtbl.addtblkv|v2->Hashtbl.replacetblk(fkvv2))l;tblletupdatetbl~f~k=letv=gettblkinmatchv,fkvwith|None,None->()|None,Somev'->Hashtbl.addtblkv'|Some_,Somev'->Hashtbl.replacetblkv'|Some_,None->Hashtbl.removetblkletget_or_addtbl~f~k=tryHashtbl.findtblkwithNot_found->letv=fkinHashtbl.addtblkv;vletpp?(pp_start=fun_()->())?(pp_stop=fun_()->())?(pp_sep=funfmt()->Format.fprintffmt",@ ")?(pp_arrow=funfmt()->Format.fprintffmt"@ -> ")pp_kpp_vfmtm=pp_startfmt();letfirst=reftrueinHashtbl.iter(funkv->if!firstthenfirst:=falseelsepp_sepfmt();pp_kfmtk;pp_arrowfmt();pp_vfmtv)m;pp_stopfmt()endincludePoly(** {2 Functor} *)moduletypeS=sigincludeHashtbl.Svalget:'at->key->'aoption(** [get tbl k] finds a binding for the key [k] if present,
or returns [None] if no value is found.
Safe version of {!Hashtbl.find}. *)valget_or:'at->key->default:'a->'a(** [get_or tbl k ~default] returns the value associated to [k] if present,
and returns [default] otherwise (if [k] doesn't belong in [tbl]).
@since 0.16 *)valadd_list:'alistt->key->'a->unit(** [add_list tbl x y] adds [y] to the list [x] is bound to. If [x] is
not bound, it becomes bound to [y].
@since 0.16 *)valincr:?by:int->intt->key->unit(** [incr ?by tbl x] increments or initializes the counter associated with [x].
If [get tbl x = None], then after update, [get tbl x = Some 1];
otherwise, if [get tbl x = Some n], now [get tbl x = Some (n+1)].
@param by if specified, the int value is incremented by [by] rather than 1.
@since 0.16 *)valdecr:?by:int->intt->key->unit(** [decr ?by tbl x] is like {!incr} but subtract 1 (or the value of [by]).
If the value reaches 0, the key is removed from the table.
This does nothing if the key is not already present in the table.
@since 0.16 *)valkeys:'at->keyiter(** [keys tbl f] iterates on keys (similar order as {!Hashtbl.iter}). *)valvalues:'at->'aiter(** [values tbl f] iterates on values in the table. *)valkeys_list:_t->keylist(** [keys_list tbl] is the list of keys in [tbl].
If the key is in the Hashtable multiple times, all occurrences will be returned.
@since 0.8 *)valvalues_list:'at->'alist(** [values_list t] is the list of values in [t].
@since 0.8 *)valmap_list:(key->'a->'b)->'at->'blist(** Map on a hashtable's items, collect into a list. *)valto_iter:'at->(key*'a)iter(** Iterate on bindings in the table.
@since 2.8 *)valadd_iter:'at->(key*'a)iter->unit(** Add the corresponding pairs to the table, using {!Hashtbl.add}.
@since 2.8 *)valadd_iter_with:f:(key->'a->'a->'a)->'at->(key*'a)iter->unit(** Add the corresponding pairs to the table, using {!Hashtbl.add}.
If a key occurs multiple times in the input, the values are combined
using [f] in an unspecified order.
@since 3.3 *)valadd_seq:'at->(key*'a)Seq.t->unit(** Add the corresponding pairs to the table, using {!Hashtbl.add}.
Renamed from [add_std_seq] since 3.0.
@since 3.0 *)valadd_seq_with:f:(key->'a->'a->'a)->'at->(key*'a)Seq.t->unit(** Add the corresponding pairs to the table, using {!Hashtbl.add}.
If a key occurs multiple times in the input, the values are combined
using [f] in an unspecified order.
@since 3.3 *)valof_iter:(key*'a)iter->'at(** From the given bindings, added in order.
@since 2.8 *)valof_iter_with:f:(key->'a->'a->'a)->(key*'a)iter->'at(** From the given bindings, added in order.
If a key occurs multiple times in the input, the values are combined
using [f] in an unspecified order.
@since 3.3 *)valof_seq:(key*'a)Seq.t->'at(** From the given bindings, added in order.
Renamed from [of_std_seq] since 3.0.
@since 3.0 *)valof_seq_with:f:(key->'a->'a->'a)->(key*'a)Seq.t->'at(** From the given bindings, added in order.
If a key occurs multiple times in the input, the values are combined
using [f] in an unspecified order.
@since 3.3 *)valadd_iter_count:intt->keyiter->unit(** [add_iter_count tbl i] increments the count of each element of [i]
by calling {!incr}. This is useful for counting how many times each
element of [i] occurs.
@since 2.8 *)valadd_seq_count:intt->keySeq.t->unit(** [add_seq_count tbl seq] increments the count of each element of [seq]
by calling {!incr}. This is useful for counting how many times each
element of [seq] occurs.
Renamed from [of_std_seq_count] since 3.0.
@since 3.0 *)valof_iter_count:keyiter->intt(** Like {!add_seq_count}, but allocates a new table and returns it.
@since 2.8 *)valof_seq_count:keySeq.t->intt(** Like {!add_seq_count}, but allocates a new table and returns it.
Renamed from [of_std_seq_count] since 3.0.
@since 3.0 *)valto_list:'at->(key*'a)list(** [to_list tbl] returns the list of (key,value) bindings (order unspecified). *)valof_list:(key*'a)list->'at(** [of_list l] builds a table from the given list [l] of bindings [k_i -> v_i],
added in order using {!add}. If a key occurs several times,
it will be added several times, and the visible binding
will be the last one. *)valof_list_with:f:(key->'a->'a->'a)->(key*'a)list->'at(** [of_list l] builds a table from the given list [l] of bindings [k_i -> v_i].
If a key occurs multiple times in the input, the values are combined
using [f] in an unspecified order.
@since 3.3 *)valupdate:'at->f:(key->'aoption->'aoption)->k:key->unit(** [update tbl ~f ~k] updates key [k] by calling [f k (Some v)] if
[k] was mapped to [v], or [f k None] otherwise; if the call
returns [None] then [k] is removed/stays removed, if the call
returns [Some v'] then the binding [k -> v'] is inserted
using {!Hashtbl.replace}.
@since 0.14 *)valget_or_add:'at->f:(key->'a)->k:key->'a(** [get_or_add tbl ~k ~f] finds and returns the binding of [k]
in [tbl], if it exists. If it does not exist, then [f k]
is called to obtain a new binding [v]; [k -> v] is added
to [tbl] and [v] is returned.
@since 1.0 *)valpp:?pp_start:unitprinter->?pp_stop:unitprinter->?pp_sep:unitprinter->?pp_arrow:unitprinter->keyprinter->'aprinter->'atprinter(** [pp ~pp_start ~pp_stop ~pp_sep ~pp arrow pp_k pp_v] returns a table printer
given a [pp_k] printer
for individual key and a [pp_v] printer for individual value.
[pp_start] and [pp_stop] control the opening and closing delimiters,
by default print nothing. [pp_sep] control the separator between binding.
[pp_arrow] control the arrow between the key and value.
Renamed from [print] since 2.0.
@since 0.13 *)endmoduleMake(X:Hashtbl.HashedType):Swithtypekey=X.tandtype'at='aHashtbl.Make(X).t=structincludeHashtbl.Make(X)letgettblx=trySome(findtblx)withNot_found->Noneletget_ortblx~default=tryfindtblxwithNot_found->defaultletincr?(by=1)tblx=letn=get_ortblx~default:0inifn+by<=0thenremovetblxelsereplacetblx(n+by)letadd_listtblkv=letl=tryfindtblkwithNot_found->[]inreplacetblk(v::l)letdecr?(by=1)tblx=tryletn=findtblxinifn-by<=0thenremovetblxelsereplacetblx(n-by)withNot_found->()letkeystblk=iter(funkey_->kkey)tblletvaluestblk=iter(fun_v->kv)tblletkeys_listtbl=fold(funk_a->k::a)tbl[]letvalues_listtbl=fold(fun_va->v::a)tbl[]letmap_listfh=fold(funxyacc->fxy::acc)h[]letupdatetbl~f~k=letv=gettblkinmatchv,fkvwith|None,None->()|None,Somev'->addtblkv'|Some_,Somev'->replacetblkv'|Some_,None->removetblkletget_or_addtbl~f~k=tryfindtblkwithNot_found->letv=fkinaddtblkv;vletto_itertblk=iter(funkeyv->k(key,v))tblletadd_itertbli=i(fun(k,v)->addtblkv)letadd_iter_with~ftbli=i(fun(k,v)->matchfindtblkwith|exceptionNot_found->addtblkv|v2->replacetblk(fkvv2))letadd_seqtblseq=Seq.iter(fun(k,v)->addtblkv)seqletadd_seq_with~ftblseq=Seq.iter(fun(k,v)->matchfindtblkwith|exceptionNot_found->addtblkv|v2->replacetblk(fkvv2))seq(* helper for building hashtables by bulk mutation *)let[@inline]mk_tbl_fx=lettbl=create32inftblx;tblletof_iteri=mk_tbl_add_iteriletof_seqi=mk_tbl_add_seqiletof_iter_with~fi=mk_tbl_(add_iter_with~f)iletof_seq_with~fi=mk_tbl_(add_seq_with~f)iletadd_iter_counttbli=i(funk->incrtblk)letadd_seq_counttblseq=Seq.iter(funk->incrtblk)seqletof_iter_countseq=lettbl=create32inadd_iter_counttblseq;tblletof_seq_counti=lettbl=create32inadd_seq_counttbli;tblletto_listtbl=fold(funkvl->(k,v)::l)tbl[]letof_listl=lettbl=create32inList.iter(fun(k,v)->addtblkv)l;tblletof_list_with~fl=lettbl=create32inList.iter(fun(k,v)->matchfindtblkwith|exceptionNot_found->addtblkv|v2->replacetblk(fkvv2))l;tblletpp?(pp_start=fun_()->())?(pp_stop=fun_()->())?(pp_sep=funfmt()->Format.fprintffmt",@ ")?(pp_arrow=funfmt()->Format.fprintffmt"@ -> ")pp_kpp_vfmtm=pp_startfmt();letfirst=reftrueiniter(funkv->if!firstthenfirst:=falseelsepp_sepfmt();pp_kfmtk;pp_arrowfmt();pp_vfmtv)m;pp_stopfmt()end