123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959open!ImportincludeHashtbl_intfmoduletypeKey=Key.Sletwith_return=With_return.with_returnlethash_param=Hashable.hash_paramlethash=Hashable.hashletraise_s=Error.raise_stype('k,'v)t={mutabletable:('k,'v)Avltree.tarray;mutablelength:int(* [recently_added] is the reference passed to [Avltree.add]. We put it in the hash
table to avoid allocating it at every [set]. *);recently_added:boolref;growth_allowed:bool;hashable:'kHashable.t;mutablemutation_allowed:bool(* Set during all iteration operations *)}type'akey='aletsexp_of_keyt=t.hashable.Hashable.sexp_of_tletcompare_keyt=t.hashable.Hashable.compareletensure_mutation_allowedt=ifnott.mutation_allowedthenfailwith"Hashtbl: mutation not allowed during iteration";;letwithout_mutatingtf=ift.mutation_allowedthen(t.mutation_allowed<-false;matchf()with|x->t.mutation_allowed<-true;x|exceptionexn->t.mutation_allowed<-true;raiseexn)elsef();;(** Internally use a maximum size that is a power of 2. Reverses the above to find the
floor power of 2 below the system max array length *)letmax_table_length=Int.floor_pow2Array.max_length(* The default size is chosen to be 0 (as opposed to 128 as it was before) because:
- 128 can create substantial memory overhead (x10) when creating many tables, most
of which are not big (say, if you have a hashtbl of hashtbl). And memory overhead is
not that easy to profile.
- if a hashtbl is going to grow, it's not clear why 128 is markedly better than other
sizes (if you going to stick 1000 elements, you're going to grow the hashtable once
or twice anyway)
- in other languages (like rust, python, and apparently go), the default is also a
small size. *)letcreate?(growth_allowed=true)?(size=0)~hashable()=letsize=Int.min(Int.max1size)max_table_lengthinletsize=Int.ceil_pow2sizein{table=Array.create~len:sizeAvltree.empty;length=0;growth_allowed;recently_added=reffalse;hashable;mutation_allowed=true};;(** Supplemental hash. This may not be necessary, it is intended as a defense against poor
hash functions, for which the power of 2 sized table will be especially sensitive.
With some testing we may choose to add it, but this table is designed to be robust to
collisions, and in most of my testing this degrades performance. *)let_supplemental_hashh=leth=hlxor((hlsr20)lxor(hlsr12))inhlxor(hlsr7)lxor(hlsr4);;letslottkey=lethash=t.hashable.Hashable.hashkeyin(* this is always non-negative because we do [land] with non-negative number *)hashland(Array.lengtht.table-1);;letadd_workert~replace~key~data=leti=slottkeyinletroot=t.table.(i)inletadded=t.recently_addedinadded:=false;letnew_root=(* The avl tree might replace the value [replace=true] or do nothing [replace=false]
to the entry, in that case the table did not get bigger, so we should not
increment length, we pass in the bool ref t.added so that it can tell us whether
it added or replaced. We do it this way to avoid extra allocation. Since the bool
is an immediate it does not go through the write barrier. *)Avltree.add~replaceroot~compare:(compare_keyt)~added~key~datainif!addedthent.length<-t.length+1;(* This little optimization saves a caml_modify when the tree
hasn't been rebalanced. *)ifnot(phys_equalnew_rootroot)thent.table.(i)<-new_root;;letmaybe_resize_tablet=letlen=Array.lengtht.tableinletshould_grow=t.length>leninifshould_grow&&t.growth_allowedthen(letnew_array_length=Int.min(len*2)max_table_lengthinifnew_array_length>lenthen(letnew_table=Array.create~len:new_array_lengthAvltree.emptyinletold_table=t.tableint.table<-new_table;t.length<-0;letf~key~data=add_worker~replace:truet~key~datainfori=0toArray.lengthold_table-1doAvltree.iterold_table.(i)~fdone));;letsett~key~data=ensure_mutation_allowedt;add_worker~replace:truet~key~data;maybe_resize_tablet;;letaddt~key~data=ensure_mutation_allowedt;add_worker~replace:falset~key~data;if!(t.recently_added)then(maybe_resize_tablet;`Ok)else`Duplicate;;letadd_exnt~key~data=matchaddt~key~datawith|`Ok->()|`Duplicate->letsexp_of_key=sexp_of_keytinleterror=Error.create"Hashtbl.add_exn got key already present"keysexp_of_keyinError.raiseerror;;letcleart=ensure_mutation_allowedt;fori=0toArray.lengtht.table-1dot.table.(i)<-Avltree.emptydone;t.length<-0;;letfind_and_calltkey~if_found~if_not_found=(* with a good hash function these first two cases will be the overwhelming majority,
and Avltree.find is recursive, so it can't be inlined, so doing this avoids a
function call in most cases. *)matcht.table.(slottkey)with|Avltree.Empty->if_not_foundkey|Avltree.Leaf{key=k;value=v}->ifcompare_keytkkey=0thenif_foundvelseif_not_foundkey|tree->Avltree.find_and_calltree~compare:(compare_keyt)key~if_found~if_not_found;;letfind_and_call1tkey~a~if_found~if_not_found=matcht.table.(slottkey)with|Avltree.Empty->if_not_foundkeya|Avltree.Leaf{key=k;value=v}->ifcompare_keytkkey=0thenif_foundvaelseif_not_foundkeya|tree->Avltree.find_and_call1tree~compare:(compare_keyt)key~a~if_found~if_not_found;;letfind_and_call2tkey~a~b~if_found~if_not_found=matcht.table.(slottkey)with|Avltree.Empty->if_not_foundkeyab|Avltree.Leaf{key=k;value=v}->ifcompare_keytkkey=0thenif_foundvabelseif_not_foundkeyab|tree->Avltree.find_and_call2tree~compare:(compare_keyt)key~a~b~if_found~if_not_found;;letfindi_and_calltkey~if_found~if_not_found=(* with a good hash function these first two cases will be the overwhelming majority,
and Avltree.find is recursive, so it can't be inlined, so doing this avoids a
function call in most cases. *)matcht.table.(slottkey)with|Avltree.Empty->if_not_foundkey|Avltree.Leaf{key=k;value=v}->ifcompare_keytkkey=0thenif_found~key:k~data:velseif_not_foundkey|tree->Avltree.findi_and_calltree~compare:(compare_keyt)key~if_found~if_not_found;;letfindi_and_call1tkey~a~if_found~if_not_found=matcht.table.(slottkey)with|Avltree.Empty->if_not_foundkeya|Avltree.Leaf{key=k;value=v}->ifcompare_keytkkey=0thenif_found~key:k~data:vaelseif_not_foundkeya|tree->Avltree.findi_and_call1tree~compare:(compare_keyt)key~a~if_found~if_not_found;;letfindi_and_call2tkey~a~b~if_found~if_not_found=matcht.table.(slottkey)with|Avltree.Empty->if_not_foundkeyab|Avltree.Leaf{key=k;value=v}->ifcompare_keytkkey=0thenif_found~key:k~data:vabelseif_not_foundkeyab|tree->Avltree.findi_and_call2tree~compare:(compare_keyt)key~a~b~if_found~if_not_found;;letfind=letif_foundv=Somevinletif_not_found_=Noneinfuntkey->find_and_calltkey~if_found~if_not_found;;letmemtkey=matcht.table.(slottkey)with|Avltree.Empty->false|Avltree.Leaf{key=k;value=_}->compare_keytkkey=0|tree->Avltree.memtree~compare:(compare_keyt)key;;letremovetkey=ensure_mutation_allowedt;leti=slottkeyinletroot=t.table.(i)inletadded_or_removed=t.recently_addedinadded_or_removed:=false;letnew_root=Avltree.removeroot~removed:added_or_removed~compare:(compare_keyt)keyinifnot(phys_equalrootnew_root)thent.table.(i)<-new_root;if!added_or_removedthent.length<-t.length-1;;letlengtht=t.lengthletis_emptyt=lengtht=0letfoldt~init~f=iflengtht=0theninitelse(letn=Array.lengtht.tableinletacc=refinitinletm=t.mutation_allowedinmatcht.mutation_allowed<-false;fori=0ton-1domatchArray.unsafe_gett.tableiwith|Avltree.Empty->()|Avltree.Leaf{key;value=data}->acc:=f~key~data!acc|bucket->acc:=Avltree.foldbucket~init:!acc~fdonewith|()->t.mutation_allowed<-m;!acc|exceptionexn->t.mutation_allowed<-m;raiseexn);;letiterit~f=ift.length=0then()else(letn=Array.lengtht.tableinletm=t.mutation_allowedinmatcht.mutation_allowed<-false;fori=0ton-1domatchArray.unsafe_gett.tableiwith|Avltree.Empty->()|Avltree.Leaf{key;value=data}->f~key~data|bucket->Avltree.iterbucket~fdonewith|()->t.mutation_allowed<-m|exceptionexn->t.mutation_allowed<-m;raiseexn);;letitert~f=iterit~f:(fun~key:_~data->fdata)letiter_keyst~f=iterit~f:(fun~key~data:_->fkey)letrecchoose_nonemptytablei=letavltree=table.(i)inifAvltree.is_emptyavltreethenchoose_nonemptytable(i+1)elseAvltree.choose_exnavltree;;letchoose_exnt=ift.length=0thenraise_s(Sexp.message"[Hashtbl.choose_exn] of empty hashtbl"[]);choose_nonemptyt.table0;;letchooset=ifis_emptytthenNoneelseSome(choose_nonemptyt.table0)letinvariantinvariant_keyinvariant_datat=fori=0toArray.lengtht.table-1doAvltree.invariantt.table.(i)~compare:(compare_keyt)done;letreal_len=foldt~init:0~f:(fun~key~datai->invariant_keykey;invariant_datadata;i+1)inassert(real_len=t.length);;letfind_exn=letif_foundv_=vinletif_not_foundkt=raise(Not_found_s(List[Atom"Hashtbl.find_exn: not found";t.hashable.sexp_of_tk]))inletfind_exntkey=find_and_call1tkey~a:t~if_found~if_not_foundin(* named to preserve symbol in compiled binary *)find_exn;;letexistsit~f=with_return(funr->iterit~f:(fun~key~data->iff~key~datathenr.returntrue);false);;letexistst~f=existsit~f:(fun~key:_~data->fdata)letfor_allit~f=not(existsit~f:(fun~key~data->not(f~key~data)))letfor_allt~f=not(existsit~f:(fun~key:_~data->not(fdata)))letcountit~f=foldt~init:0~f:(fun~key~dataacc->iff~key~datathenacc+1elseacc);;letcountt~f=foldt~init:0~f:(fun~key:_~dataacc->iffdatathenacc+1elseacc);;letmapit~f=letnew_t=create~growth_allowed:t.growth_allowed~hashable:t.hashable~size:t.length()initerit~f:(fun~key~data->setnew_t~key~data:(f~key~data));new_t;;letmapt~f=mapit~f:(fun~key:_~data->fdata)letcopyt=mapt~f:Fn.idletfilter_mapit~f=letnew_t=create~growth_allowed:t.growth_allowed~hashable:t.hashable~size:t.length()initerit~f:(fun~key~data->matchf~key~datawith|Somenew_data->setnew_t~key~data:new_data|None->());new_t;;letfilter_mapt~f=filter_mapit~f:(fun~key:_~data->fdata)letfilterit~f=filter_mapit~f:(fun~key~data->iff~key~datathenSomedataelseNone);;letfiltert~f=filterit~f:(fun~key:_~data->fdata)letfilter_keyst~f=filterit~f:(fun~key~data:_->fkey)letpartition_mapit~f=lett0=create~growth_allowed:t.growth_allowed~hashable:t.hashable~size:t.length()inlett1=create~growth_allowed:t.growth_allowed~hashable:t.hashable~size:t.length()initerit~f:(fun~key~data->match(f~key~data:_Either.t)with|Firstnew_data->sett0~key~data:new_data|Secondnew_data->sett1~key~data:new_data);t0,t1;;letpartition_mapt~f=partition_mapit~f:(fun~key:_~data->fdata)letpartitioni_tft~f=partition_mapit~f:(fun~key~data->iff~key~datathenFirstdataelseSeconddata);;letpartition_tft~f=partitioni_tft~f:(fun~key:_~data->fdata)letfind_or_addtid~default=find_and_call2tid~a:t~b:default~if_found:(fundata__->data)~if_not_found:(funkeytdefault->letdefault=default()insett~key~data:default;default);;letfindi_or_addtid~default=find_and_call2tid~a:t~b:default~if_found:(fundata__->data)~if_not_found:(funkeytdefault->letdefault=defaultkeyinsett~key~data:default;default);;(* Some hashtbl implementations may be able to perform this more efficiently than two
separate lookups *)letfind_and_removetid=letresult=findtidinifOption.is_someresultthenremovetid;result;;letchangetid~f=matchf(findtid)with|None->removetid|Somedata->sett~key:id~data;;letupdate_and_returntid~f=letdata=f(findtid)insett~key:id~data;data;;letupdatetid~f=ignore(update_and_returntid~f:_)letincr_by~remove_if_zerotkeyby=ifremove_if_zerothenchangetkey~f:(funopt->matchby+Option.valueopt~default:0with|0->None|n->Somen)elseupdatetkey~f:(function|None->by|Somei->by+i);;letincr?(by=1)?(remove_if_zero=false)tkey=incr_by~remove_if_zerotkeybyletdecr?(by=1)?(remove_if_zero=false)tkey=incr_by~remove_if_zerotkey(-by)letadd_multit~key~data=updatetkey~f:(function|None->[data]|Somel->data::l);;letremove_multitkey=matchfindtkeywith|None->()|Some[]|Some[_]->removetkey|Some(_::tl)->sett~key~data:tl;;letfind_multitkey=matchfindtkeywith|None->[]|Somel->l;;letcreate_mapped?growth_allowed?size~hashable~get_key~get_datarows=letsize=matchsizewith|Somes->s|None->List.lengthrowsinletres=create?growth_allowed~hashable~size()inletdupes=ref[]inList.iterrows~f:(funr->letkey=get_keyrinletdata=get_datarinifmemreskeythendupes:=key::!dupeselsesetres~key~data);match!dupeswith|[]->`Okres|keys->`Duplicate_keys(List.dedup_and_sort~compare:hashable.Hashable.comparekeys);;letcreate_mapped_multi?growth_allowed?size~hashable~get_key~get_datarows=letsize=matchsizewith|Somes->s|None->List.lengthrowsinletres=create?growth_allowed~size~hashable()inList.iterrows~f:(funr->letkey=get_keyrinletdata=get_datarinadd_multires~key~data);res;;letof_alist?growth_allowed?size~hashablelst=matchcreate_mapped?growth_allowed?size~hashable~get_key:fst~get_data:sndlstwith|`Okt->`Okt|`Duplicate_keysk->`Duplicate_key(List.hd_exnk);;letof_alist_report_all_dups?growth_allowed?size~hashablelst=create_mapped?growth_allowed?size~hashable~get_key:fst~get_data:sndlst;;letof_alist_or_error?growth_allowed?size~hashablelst=matchof_alist?growth_allowed?size~hashablelstwith|`Okv->Result.Okv|`Duplicate_keykey->letsexp_of_key=hashable.Hashable.sexp_of_tinOr_error.error"Hashtbl.of_alist_exn: duplicate key"keysexp_of_key;;letof_alist_exn?growth_allowed?size~hashablelst=matchof_alist_or_error?growth_allowed?size~hashablelstwith|Result.Okv->v|Result.Errore->Error.raisee;;letof_alist_multi?growth_allowed?size~hashablelst=create_mapped_multi?growth_allowed?size~hashable~get_key:fst~get_data:sndlst;;letto_alistt=fold~f:(fun~key~datalist->(key,data)::list)~init:[]tletsexp_of_tsexp_of_keysexp_of_datat=t|>to_alist|>List.sort~compare:(fun(k1,_)(k2,_)->t.hashable.comparek1k2)|>sexp_of_list(sexp_of_pairsexp_of_keysexp_of_data);;lett_of_sexp~hashablek_of_sexpd_of_sexpsexp=letalist=list_of_sexp(pair_of_sexpk_of_sexpd_of_sexp)sexpinmatchof_alist~hashablealist~size:(List.lengthalist)with|`Okv->v|`Duplicate_keyk->(* find the sexp of a duplicate key, so the error is narrowed to a key and not
the whole map *)letalist_sexps=list_of_sexp(pair_of_sexpFn.idFn.id)sexpinletfound_first_k=reffalseinList.iter2_exnalistalist_sexps~f:(fun(k2,_)(k2_sexp,_)->ifhashable.comparekk2=0thenif!found_first_kthenof_sexp_error"Hashtbl.t_of_sexp: duplicate key"k2_sexpelsefound_first_k:=true);assertfalse;;lett_sexp_grammar(typekv)(k_grammar:kSexplib0.Sexp_grammar.t)(v_grammar:vSexplib0.Sexp_grammar.t):(k,v)tSexplib0.Sexp_grammar.t=Sexplib0.Sexp_grammar.coerce(List.Assoc.t_sexp_grammark_grammarv_grammar);;letkeyst=foldt~init:[]~f:(fun~key~data:_acc->key::acc)letdatat=fold~f:(fun~key:_~datalist->data::list)~init:[]tletadd_to_groupsgroups~get_key~get_data~combine~rows=List.iterrows~f:(funrow->letkey=get_keyrowinletdata=get_datarowinletdata=matchfindgroupskeywith|None->data|Someold->combineolddatainsetgroups~key~data);;letgroup?growth_allowed?size~hashable~get_key~get_data~combinerows=letres=create?growth_allowed?size~hashable()inadd_to_groupsres~get_key~get_data~combine~rows;res;;letcreate_with_key?growth_allowed?size~hashable~get_keyrows=create_mapped?growth_allowed?size~hashable~get_key~get_data:Fn.idrows;;letcreate_with_key_or_error?growth_allowed?size~hashable~get_keyrows=matchcreate_with_key?growth_allowed?size~hashable~get_keyrowswith|`Okt->Result.Okt|`Duplicate_keyskeys->letsexp_of_key=hashable.Hashable.sexp_of_tinOr_error.error_s(Sexp.message"Hashtbl.create_with_key: duplicate keys"["keys",sexp_of_listsexp_of_keykeys]);;letcreate_with_key_exn?growth_allowed?size~hashable~get_keyrows=Or_error.ok_exn(create_with_key_or_error?growth_allowed?size~hashable~get_keyrows);;letmerge=letmaybe_sett~key~fd=matchf~keydwith|None->()|Somev->sett~key~data:vinfunt_leftt_right~f->ifnot(Hashable.equalt_left.hashablet_right.hashable)theninvalid_arg"Hashtbl.merge: different 'hashable' values";letnew_t=create~growth_allowed:t_left.growth_allowed~hashable:t_left.hashable~size:t_left.length()inwithout_mutatingt_left(fun()->without_mutatingt_right(fun()->iterit_left~f:(fun~key~data:left->matchfindt_rightkeywith|None->maybe_setnew_t~key~f(`Leftleft)|Someright->maybe_setnew_t~key~f(`Both(left,right)));iterit_right~f:(fun~key~data:right->matchfindt_leftkeywith|None->maybe_setnew_t~key~f(`Rightright)|Some_->()(* already done above *))));new_t;;letmerge_into~src~dst~f=iterisrc~f:(fun~key~data->letdst_data=finddstkeyinletaction=without_mutatingdst(fun()->f~keydatadst_data)inmatch(action:_Merge_into_action.t)with|Remove->removedstkey|Set_todata->(matchdst_datawith|None->setdst~key~data|Somedst_data->ifnot(phys_equaldst_datadata)thensetdst~key~data));;letfilteri_inplacet~f=letto_remove=foldt~init:[]~f:(fun~key~dataac->iff~key~datathenacelsekey::ac)inList.iterto_remove~f:(funkey->removetkey);;letfilter_inplacet~f=filteri_inplacet~f:(fun~key:_~data->fdata)letfilter_keys_inplacet~f=filteri_inplacet~f:(fun~key~data:_->fkey)letfilter_mapi_inplacet~f=letmap_results=foldt~init:[]~f:(fun~key~dataac->(key,f~key~data)::ac)inList.itermap_results~f:(fun(key,result)->matchresultwith|None->removetkey|Somedata->sett~key~data);;letfilter_map_inplacet~f=filter_mapi_inplacet~f:(fun~key:_~data->fdata)letmapi_inplacet~f=ensure_mutation_allowedt;without_mutatingt(fun()->Array.itert.table~f:(Avltree.mapi_inplace~f));;letmap_inplacet~f=mapi_inplacet~f:(fun~key:_~data->fdata)letequalequaltt'=lengtht=lengtht'&&with_return(funr->without_mutatingt'(fun()->iterit~f:(fun~key~data->matchfindt'keywith|None->r.returnfalse|Somedata'->ifnot(equaldatadata')thenr.returnfalse));true);;letsimilar=equalmoduleAccessors=structletinvariant=invariantletchoose=chooseletchoose_exn=choose_exnletclear=clearletcopy=copyletremove=removeletset=setletadd=addletadd_exn=add_exnletchange=changeletupdate=updateletupdate_and_return=update_and_returnletadd_multi=add_multiletremove_multi=remove_multiletfind_multi=find_multiletmem=memletiter_keys=iter_keysletiter=iterletiteri=iteriletexists=existsletexistsi=existsiletfor_all=for_allletfor_alli=for_alliletcount=countletcounti=countiletfold=foldletlength=lengthletis_empty=is_emptyletmap=mapletmapi=mapiletfilter_map=filter_mapletfilter_mapi=filter_mapiletfilter_keys=filter_keysletfilter=filterletfilteri=filteriletpartition_map=partition_mapletpartition_mapi=partition_mapiletpartition_tf=partition_tfletpartitioni_tf=partitioni_tfletfind_or_add=find_or_addletfindi_or_add=findi_or_addletfind=findletfind_exn=find_exnletfind_and_call=find_and_callletfind_and_call1=find_and_call1letfind_and_call2=find_and_call2letfindi_and_call=findi_and_callletfindi_and_call1=findi_and_call1letfindi_and_call2=findi_and_call2letfind_and_remove=find_and_removeletto_alist=to_alistletmerge=mergeletmerge_into=merge_intoletkeys=keysletdata=dataletfilter_keys_inplace=filter_keys_inplaceletfilter_inplace=filter_inplaceletfilteri_inplace=filteri_inplaceletmap_inplace=map_inplaceletmapi_inplace=mapi_inplaceletfilter_map_inplace=filter_map_inplaceletfilter_mapi_inplace=filter_mapi_inplaceletequal=equalletsimilar=similarletincr=incrletdecr=decrletsexp_of_key=sexp_of_keyendmoduleCreators(Key:sigtype'atvalhashable:'atHashable.tend):sigtype('a,'b)t_=('aKey.t,'b)tvalt_of_sexp:(Sexp.t->'aKey.t)->(Sexp.t->'b)->Sexp.t->('a,'b)t_includeCreators_genericwithtype('a,'b)t:=('a,'b)t_withtype'akey:='aKey.twithtype('key,'data,'a)create_options:=('key,'data,'a)create_options_without_first_class_moduleend=structlethashable=Key.hashabletype('a,'b)t_=('aKey.t,'b)tletcreate?growth_allowed?size()=create?growth_allowed?size~hashable()letof_alist?growth_allowed?sizel=of_alist?growth_allowed~hashable?sizelletof_alist_report_all_dups?growth_allowed?sizel=of_alist_report_all_dups?growth_allowed~hashable?sizel;;letof_alist_or_error?growth_allowed?sizel=of_alist_or_error?growth_allowed~hashable?sizel;;letof_alist_exn?growth_allowed?sizel=of_alist_exn?growth_allowed~hashable?sizel;;lett_of_sexpk_of_sexpd_of_sexpsexp=t_of_sexp~hashablek_of_sexpd_of_sexpsexpletof_alist_multi?growth_allowed?sizel=of_alist_multi?growth_allowed~hashable?sizel;;letcreate_mapped?growth_allowed?size~get_key~get_datal=create_mapped?growth_allowed~hashable?size~get_key~get_datal;;letcreate_with_key?growth_allowed?size~get_keyl=create_with_key?growth_allowed~hashable?size~get_keyl;;letcreate_with_key_or_error?growth_allowed?size~get_keyl=create_with_key_or_error?growth_allowed~hashable?size~get_keyl;;letcreate_with_key_exn?growth_allowed?size~get_keyl=create_with_key_exn?growth_allowed~hashable?size~get_keyl;;letgroup?growth_allowed?size~get_key~get_data~combinel=group?growth_allowed~hashable?size~get_key~get_data~combinel;;endmodulePoly=structtypenonrec('a,'b)t=('a,'b)ttype'akey='alethashable=Hashable.polyincludeCreators(structtype'at='alethashable=hashableend)includeAccessorsletsexp_of_t=sexp_of_tlett_sexp_grammar=t_sexp_grammarendmodulePrivate=structmoduletypeCreators_generic=Creators_genericmoduletypeHashable=Hashable.Hashabletypenonrec('key,'data,'z)create_options_without_first_class_module=('key,'data,'z)create_options_without_first_class_modulelethashablet=t.hashableendletcreate?growth_allowed?sizem=create~hashable:(Hashable.of_keym)?growth_allowed?size();;letof_alist?growth_allowed?sizeml=of_alist~hashable:(Hashable.of_keym)?growth_allowed?sizel;;letof_alist_report_all_dups?growth_allowed?sizeml=of_alist_report_all_dups~hashable:(Hashable.of_keym)?growth_allowed?sizel;;letof_alist_or_error?growth_allowed?sizeml=of_alist_or_error~hashable:(Hashable.of_keym)?growth_allowed?sizel;;letof_alist_exn?growth_allowed?sizeml=of_alist_exn~hashable:(Hashable.of_keym)?growth_allowed?sizel;;letof_alist_multi?growth_allowed?sizeml=of_alist_multi~hashable:(Hashable.of_keym)?growth_allowed?sizel;;letcreate_mapped?growth_allowed?sizem~get_key~get_datal=create_mapped~hashable:(Hashable.of_keym)?growth_allowed?size~get_key~get_datal;;letcreate_with_key?growth_allowed?sizem~get_keyl=create_with_key~hashable:(Hashable.of_keym)?growth_allowed?size~get_keyl;;letcreate_with_key_or_error?growth_allowed?sizem~get_keyl=create_with_key_or_error~hashable:(Hashable.of_keym)?growth_allowed?size~get_keyl;;letcreate_with_key_exn?growth_allowed?sizem~get_keyl=create_with_key_exn~hashable:(Hashable.of_keym)?growth_allowed?size~get_keyl;;letgroup?growth_allowed?sizem~get_key~get_data~combinel=group~hashable:(Hashable.of_keym)?growth_allowed?size~get_key~get_data~combinel;;lethashable_st=Hashable.to_keyt.hashablemoduleM(K:T.T)=structtypenonrec'vt=(K.t,'v)tendmoduletypeSexp_of_m=sigtypet[@@deriving_inlinesexp_of]valsexp_of_t:t->Sexplib0.Sexp.t[@@@end]endmoduletypeM_of_sexp=sigtypet[@@deriving_inlineof_sexp]valt_of_sexp:Sexplib0.Sexp.t->t[@@@end]includeKey.Swithtypet:=tendmoduletypeM_sexp_grammar=sigtypet[@@deriving_inlinesexp_grammar]valt_sexp_grammar:tSexplib0.Sexp_grammar.t[@@@end]endmoduletypeEqual_m=sigendletsexp_of_m__t(typek)(moduleK:Sexp_of_mwithtypet=k)sexp_of_vt=sexp_of_tK.sexp_of_tsexp_of_vt;;letm__t_of_sexp(typek)(moduleK:M_of_sexpwithtypet=k)v_of_sexpsexp=t_of_sexp~hashable:(Hashable.of_key(moduleK))K.t_of_sexpv_of_sexpsexp;;letm__t_sexp_grammar(typek)(moduleK:M_sexp_grammarwithtypet=k)v_grammar=t_sexp_grammarK.t_sexp_grammarv_grammar;;letequal_m__t(module_:Equal_m)equal_vt1t2=equalequal_vt1t2