123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044open!Core_kernelincludeIncr_map_intf(** This type lets us capture the kind of map function being performed, so we can with
one implementation perform map and filter-map operations.
Here, ['input_data] is the type of data in the input map, ['output_data] is the type
of data in the output map, and ['f_output] is the return type of the [~f] function
passed to the mapping function. *)moduleMap_type=structtype('input_data,'output_data,'f_output)t=|Map:('input_data,'output_data,'output_data)t|Filter_map:('input_data,'output_data,'output_dataoption)t(* The extra type variable 'a is to allow in future:
| Filter : ('output_data, 'output_data, bool) t *)endmoduleGeneric=structletwith_oldi~f=letopenIncremental.Let_syntaxinletold=refNoneinlet%mapa=iinletb=f~old:!oldainold:=Some(a,b);b;;letunordered_fold?(data_equal=phys_equal)?update?specialized_initialmap~init~add~remove=letupdate=letdefault~key~old_data~new_dataacc=add~key~data:new_data(remove~key~data:old_dataacc)inOption.valueupdate~defaultinwith_oldmap~f:(fun~oldnew_in->matcholdwith|None->(matchspecialized_initialwith|None->Map.fold~init~f:addnew_in|Someinitial->initial~initnew_in)|Some(old_in,old_out)->Map.fold_symmetric_diff~init:old_outold_innew_in~data_equal~f:(funacc(key,change)->matchchangewith|`Leftold->remove~key~data:oldacc|`Rightnew_->add~key~data:new_acc|`Unequal(old,new_)->update~key~old_data:old~new_data:new_acc));;letunordered_fold_nested_maps?(data_equal=phys_equal)?updateincr_map~init~add~remove=letupdate=matchupdatewith|Someupdate->update|None->fun~outer_key~inner_key~old_data~new_dataacc->add~outer_key~inner_key~data:new_data(remove~outer_key~inner_key~data:old_dataacc)inunordered_foldincr_map~init~update:(fun~key:outer_key~old_data:old_inner_map~new_data:new_inner_mapacc->(Map.fold_symmetric_diffold_inner_mapnew_inner_map~data_equal)~init:acc~f:(funacc(inner_key,diff)->matchdiffwith|`Leftdata_removed->remove~outer_key~inner_key~data:data_removedacc|`Rightdata_added->add~outer_key~inner_key~data:data_addedacc|`Unequal(old_data,new_data)->update~outer_key~inner_key~old_data~new_dataacc))~add:(fun~key:outer_key~data:inner_mapacc->Map.foldinner_map~init:acc~f:(fun~key:inner_key~dataacc->add~outer_key~inner_key~dataacc))~remove:(fun~key:outer_key~data:inner_mapacc->Map.foldinner_map~init:acc~f:(fun~key:inner_key~dataacc->remove~outer_key~inner_key~dataacc));;letwith_comparator'get_comparatorxf=Incremental.bind(Incremental.freeze(Incremental.mapx~f:get_comparator))~f;;(** Captures the comparator (which can't change anyway, since the type determines the
comparator) by freezing the corresponding map. Note that by first using Incremental.map to
get the comparator out of the map, we allow the initial map itself to be garbage
collected *)letwith_comparatormapf=with_comparator'Map.comparatormapfletof_setset=with_comparator'Set.comparatorset(funcomparator->letold_input=ref(Set.Using_comparator.empty~comparator)inletold_output=ref(Map.Using_comparator.empty~comparator)inIncremental.mapset~f:(funnew_input->letnew_output=Sequence.fold(Set.symmetric_diff!old_inputnew_input)~init:!old_output~f:(funoutput->function|Firstk->Map.removeoutputk|Secondk->Map.add_exnoutput~key:k~data:())inold_input:=new_input;old_output:=new_output;new_output));;letgeneric_mapi(typeinput_dataoutput_dataf_outputstate_witness)(witness:(input_data,output_data,f_output)Map_type.t)?(data_equal=phys_equal)(map:(('key,input_data,'cmp)Map.t,state_witness)Incremental.t)~(f:key:'key->data:input_data->f_output)=with_oldmap~f:(fun~oldinput->matcholdwith|None->(matchwitnesswith|Map_type.Map->(Map.mapiinput~f:('key,output_data,'cmp)Map.t)|Map_type.Filter_map->Map.filter_mapiinput~f)|Some(old_input,old_output)->Map.fold_symmetric_diffold_inputinput~data_equal~init:old_output~f:(funoutput(key,change)->matchchangewith|`Left_->Map.removeoutputkey|`Rightnew_data|`Unequal(_,new_data)->letres=f~key~data:new_datain(matchwitnesswith|Map_type.Map->Map.setoutput~key~data:res|Map_type.Filter_map->(matchreswith|None->Map.removeoutputkey|Someoutput_data->Map.setoutput~key~data:output_data))));;letmapi?data_equalmap~f=generic_mapiMap?data_equalmap~fletfilter_mapi?data_equalmap~f=generic_mapiFilter_map?data_equalmap~fletwith_old2i1i2~f=letold=refNoneinIncremental.map2i1i2~f:(funa1a2->letb=f~old:!olda1a2inold:=Some(a1,a2,b);b);;letmerge?(data_equal_left=phys_equal)?(data_equal_right=phys_equal)left_mapright_map~f=with_old2left_mapright_map~f:(fun~oldnew_left_mapnew_right_map->letcomparator=Map.comparatornew_left_mapinletold_left_map,old_right_map,old_output=matcholdwith|None->letempty=Map.Using_comparator.empty~comparatorinempty,empty,empty|Somex->xinletleft_diff=Map.symmetric_diffold_left_mapnew_left_map~data_equal:data_equal_leftinletright_diff=Map.symmetric_diffold_right_mapnew_right_map~data_equal:data_equal_rightin(* We merge the two sides of the diffs together so we can make sure to handle each
key exactly once. This relies on symmetric diff giving sorted output. *)Sequence.merge_with_duplicatesleft_diffright_diff~compare:(fun(left_key,_)(right_key,_)->comparator.compareleft_keyright_key)|>Sequence.fold~init:old_output~f:(funoutputdiff_element->letkey=matchdiff_elementwith|Left(key,_)|Right(key,_)->key|Both((left_key,_),(right_key,_))->assert(comparator.compareleft_keyright_key=0);left_keyin(* These values represent whether there is data for the given key in the new
input in the left and right map. *)letleft_data_opt,right_data_opt=letnew_data=function|`Left_->None|`Rightx|`Unequal(_,x)->Somexinmatchdiff_elementwith|Both((_,left_diff),(_,right_diff))->new_dataleft_diff,new_dataright_diff|Left(_,left_diff)->new_dataleft_diff,Map.findnew_right_mapkey|Right(_,right_diff)->Map.findnew_left_mapkey,new_dataright_diffinletoutput_data_opt=matchleft_data_opt,right_data_optwith|None,None->None|Somex,None->f~key(`Leftx)|None,Somey->f~key(`Righty)|Somex,Somey->f~key(`Both(x,y))inmatchoutput_data_optwith|None->Map.removeoutputkey|Somedata->Map.setoutput~key~data));;letgeneric_mapi_with_comparator'(typeinput_dataoutput_dataf_outputstate_witness)(witness:(input_data,output_data,f_output)Map_type.t)?cutoff?(data_equal=phys_equal)(lhs:(('key,input_data,'cmp)Map.t,state_witness)Incremental.t)~(comparator:('key,'cmp)Comparator.t)~(f:key:'key->data:(input_data,state_witness)Incremental.t->(f_output,state_witness)Incremental.t):(('key,output_data,'cmp)Map.t,state_witness)Incremental.t=letmoduleE=Incremental.Expertinletincremental_state=Incremental.statelhsinletempty_map=Map.Using_comparator.empty~comparatorinletprev_map=refempty_mapinletprev_nodes=refempty_mapinletacc:('key,output_data,'cmp)Map.tref=refempty_mapinletresult=E.Node.createincremental_state(fun()->!acc)inlet(on_inner_change:key:'key->f_output->unit)=matchwitnesswith|Map_type.Map->fun~keydata->acc:=Map.set!acc~key~data|Map_type.Filter_map->fun~keyopt->letold=!accinacc:=(matchoptwith|None->ifMap.memoldkeythenMap.removeoldkeyelseold|Somedata->Map.setold~key~data)inletreclhs_change=lazy(Incremental.maplhs~f:(funmap->letnew_nodes=Map.fold_symmetric_diff~data_equal!prev_mapmap~init:!prev_nodes~f:(funnodes(key,changed)->matchchangedwith|`Unequal_->letnode,_dep=Map.find_exnnodeskeyinE.Node.make_stalenode;nodes|`Left_->letnode,dep=Map.find_exnnodeskeyinletnodes=Map.removenodeskeyinE.Node.remove_dependencyresultdep;acc:=Map.remove!acckey;E.Node.invalidatenode;nodes|`Right_->letnode=E.Node.createincremental_state(fun()->Map.find_exn!prev_mapkey)inOption.itercutoff~f:(func->Incremental.set_cutoff(E.Node.watchnode)c);E.Node.add_dependencynode(E.Dependency.create(forcelhs_change));letuser_function_dep=E.Dependency.create(f~key~data:(E.Node.watchnode))~on_change:(on_inner_change~key)inE.Node.add_dependencyresultuser_function_dep;Map.setnodes~key~data:(node,user_function_dep))inprev_nodes:=new_nodes;prev_map:=map))inE.Node.add_dependencyresult(E.Dependency.create(forcelhs_change));E.Node.watchresult;;letfilter_mapi'?cutoff?data_equalmap~f=with_comparatormap(funcomparator->generic_mapi_with_comparator'Map_type.Filter_map?cutoff?data_equalmap~f~comparator);;letmapi'?cutoff?data_equalmap~f=with_comparatormap(funcomparator->generic_mapi_with_comparator'Map_type.Map?cutoff?data_equalmap~f~comparator);;letkeysmap=with_comparatormap(funcomparator->letadd~key~data:_acc=Set.addacckeyinletremove~key~data:_acc=Set.removeacckeyinletdata_equal__=trueinunordered_foldmap~init:(Set.Using_comparator.empty~comparator)~data_equal~add~remove);;letflattenstatemap=letmoduleE=Incremental.Expertinletresult=ref(Map.Using_comparator.empty~comparator:(Map.comparatormap))inletnode=E.Node.createstate(fun()->!result)inMap.iterimap~f:(fun~key~data:incr->E.Node.add_dependencynode(E.Dependency.createincr~on_change:(funa->result:=Map.set!result~key~data:a)));E.Node.watchnode;;letjoin_with_comparatormap_incr~comparator=letmoduleE=Incremental.Expertinletincremental_state=Incremental.statemap_incrinletempty_map=Map.Using_comparator.empty~comparatorinletresult_map=refempty_mapinletold_map_of_incrs=refempty_mapinletcurrent_dependencies=refempty_mapinletresult=E.Node.createincremental_state(fun()->!result_map)inletadd_subnodecurrent_dependencies~key~data_node=letnew_dep=E.Dependency.createdata_node~on_change:(fundata->result_map:=Map.set!result_map~key~data)inE.Node.add_dependencyresultnew_dep;Map.setcurrent_dependencies~key~data:new_depinletremove_subnodecurrent_dependencies~key=letdep=Map.find_exncurrent_dependencieskeyinE.Node.remove_dependencyresultdep;result_map:=Map.remove!result_mapkey;Map.removecurrent_dependencieskeyinletlhs_change=Incremental.mapmap_incr~f:(funmap_of_incrs->letnew_dependency_map=Map.fold_symmetric_diff~data_equal:phys_equal!old_map_of_incrsmap_of_incrs~init:!current_dependencies~f:(funcurrent_dependencies(key,diff)->matchdiffwith|`Left_->remove_subnodecurrent_dependencies~key|`Rightdata_node->add_subnodecurrent_dependencies~key~data_node|`Unequal(_,data_node)->remove_subnodecurrent_dependencies~key|>add_subnode~key~data_node)incurrent_dependencies:=new_dependency_map;old_map_of_incrs:=map_of_incrs)inE.Node.add_dependencyresult(E.Dependency.createlhs_change);E.Node.watchresult;;letjoinmap=with_comparatormap(funcomparator->join_with_comparatormap~comparator);;moduleSeparate_state=structtype('k,'v,'cmp,'w)t={mutableinput_map:('k,'v,'cmp)Map.t;mutableexpert_nodes:('k,('v,'w)Incremental.Expert.Node.t,'cmp)Map.t;mutableoutput_map:('k,('v,'w)Incremental.t,'cmp)Map.t}letcreatecomparator=letempty=Map.Using_comparator.empty~comparatorin{input_map=empty;expert_nodes=empty;output_map=empty};;letcreate_lookup_nodestatetkey=Incremental.Expert.Node.createstate(fun()->Map.find_exnt.input_mapkey);;endletseparateinput_map~data_equal=letincremental_state=Incremental.stateinput_mapinwith_comparatorinput_map(funcomparator->letstate=Separate_state.createcomparatorinletoutput_map_node=Incremental.Expert.Node.createincremental_state(fun()->state.output_map)inletmake_node_depend_on_input_map_changednode~input_map_changed=letdependency=Incremental.Expert.Dependency.create(Lazy.force_valinput_map_changed)inIncremental.Expert.Node.add_dependencynodedependencyin(* We want to make nodes depend on [input_map_changed] so that [input_map_changed]
is allowed to make them stale, but we do not want them to be recomputed for any
other reason. So we make [input_map_changed] a unit incremental (that therefore
never changes) and this way [output_map_node] and the lookup nodes will only be
recomputed when they are explicitly made stale.
*)letrecinput_map_changed=lazy(Incremental.mapinput_map~f:(funinput_map->letprev_input_map=state.input_mapinletexpert_nodes,output_map=Map.fold_symmetric_diffprev_input_mapinput_map~data_equal~init:(state.expert_nodes,state.output_map)~f:(fun(expert_nodes,output_map)(key,change)->matchchangewith|`Left_old_value->letold_node=Map.find_exnexpert_nodeskeyinIncremental.Expert.Node.invalidateold_node;Incremental.Expert.Node.make_staleoutput_map_node;Map.removeexpert_nodeskey,Map.removeoutput_mapkey|`Right_new_value->letnode=Separate_state.create_lookup_nodeincremental_statestatekeyinmake_node_depend_on_input_map_changednode~input_map_changed;Incremental.Expert.Node.make_staleoutput_map_node;(Map.add_exnexpert_nodes~key~data:node,Map.add_exnoutput_map~key~data:(Incremental.Expert.Node.watchnode))|`Unequal(_old_value,_new_value)->Incremental.Expert.Node.make_stale(Map.find_exnexpert_nodeskey);expert_nodes,output_map)instate.input_map<-input_map;state.expert_nodes<-expert_nodes;state.output_map<-output_map))inmake_node_depend_on_input_map_changedoutput_map_node~input_map_changed;Incremental.Expert.Node.watchoutput_map_node);;(* Just for deriving structural equality. *)type'amaybe_bound_structurally='aMaybe_bound.t=|Inclof'a|Exclof'a|Unbounded[@@derivingequal]letsubrange(typekvcmpstate_witness)?(data_equal=phys_equal)(map_incr:((k,v,cmp)Map.t,state_witness)Incremental.t)range=with_old2map_incrrange~f:(fun~oldmaprange->letcompare=(Map.comparatormap).compareinletequallr=comparelr=0inlet(>)ab=compareab>0and(>=)ab=compareab>=0inletmaybe_bound_equalab:bool=equal_maybe_bound_structurallyequalabinletrange_is_empty~min~max:bool=matchmin,maxwith|Unbounded,(Unbounded|Excl_|Incl_)|(Excl_|Incl_),Unbounded->false|Inclmin,Inclmax->min>max|Exclmin,Exclmax|Inclmin,Exclmax|Exclmin,Inclmax->min>=maxinletrange_includes~min~maxkey:bool=Maybe_bound.is_lower_boundmin~of_:key~compare&&Maybe_bound.is_upper_boundmax~of_:key~compareinmatchrangewith|None->(* Empty new range means empty map *)Map.Using_comparator.empty~comparator:(Map.comparatormap)|Some((min,max)asrange)->letfrom_scratch()=Map.subrangemap~lower_bound:min~upper_bound:maxin(matcholdwith|None|Some(_,None,_)->(* no old range *)from_scratch()|Some(_,Some(old_min,old_max),_)whenrange_is_empty~min:old_min~max:old_max||range_is_empty~min~max:old_max||range_is_empty~min:old_min~max->(* empty old range or old range disjoint with new *)from_scratch()|Some(old_map,Some((old_min,old_max)asold_range),old_res)->with_return(fun{return}->(* Returns true iff the key is in both new and old ranges *)letin_range_intersectionkey=range_includes~min~maxkey&&range_includes~min:old_min~max:old_maxkeyin(* Apply changes to keys which are in the intersection of both ranges.
[outside] is the number of updates outside the range intersection that we
tolerate before giving up and reconstructing based on the new range. This
is an optimisation in the case that the map changes in a very big way, at
which point computing based on the new range is cheaper. *)letapply_diff_in_intersection(outside,map)(key,data)=ifin_range_intersectionkeythen(matchdatawith|`Left_->outside,Map.removemapkey|`Rightdata|`Unequal(_,data)->outside,Map.setmap~key~data)else(letoutside=outside-1inifInt.O.(outside<0)thenreturn(from_scratch())elseoutside,Map.removemapkey)in(* First update the keys in /both/ the old and the new range. *)letwith_updated_values_in_intersection=(* Cutoff the big diff computation if we reach O(|submap|) number of
changes that are outside the range *)letoutside_cutoff=Map.lengthold_res/4inMap.fold_symmetric_diff~data_equalold_mapmap~init:(outside_cutoff,old_res)~f:apply_diff_in_intersection|>sndinifTuple2.equal~eq1:maybe_bound_equal~eq2:maybe_bound_equalold_rangerangethen(* There are no keys to remove and everything in range is updated. *)with_updated_values_in_intersectionelse((* Remove any keys which are not in the new range. *)letwithout_keys_out_of_range=Map.subrangewith_updated_values_in_intersection~lower_bound:min~upper_bound:maxin(* Add in any keys which are in the new range but not the old range. *)letwith_new_keys_now_in_range=letmap_append_exnlower_partupper_part=matchMap.append~lower_part~upper_partwith|`Okmap->map|`Overlapping_key_ranges->failwith"impossible case: BUG in incr_map.ml subrange"inletlower_part=matchold_minwith|Unbounded->Map.Using_comparator.empty~comparator:(Map.comparatormap)|Exclold_min->Map.subrangemap~lower_bound:min~upper_bound:(Inclold_min)|Inclold_min->Map.subrangemap~lower_bound:min~upper_bound:(Exclold_min)andupper_part=matchold_maxwith|Unbounded->Map.Using_comparator.empty~comparator:(Map.comparatormap)|Exclold_max->Map.subrangemap~lower_bound:(Inclold_max)~upper_bound:max|Inclold_max->Map.subrangemap~lower_bound:(Exclold_max)~upper_bound:maxinmap_append_exnlower_part(map_append_exnwithout_keys_out_of_rangeupper_part)inwith_new_keys_now_in_range))));;letindex_bymap_incr~comparator:outer_comparator~index=with_comparatormap_incr(funinner_comparator->unordered_foldmap_incr~init:(Map.emptyouter_comparator)~add:(fun~key:inner_key~dataouter_map->matchindexdatawith|None->outer_map|Someouter_key->Map.updateouter_mapouter_key~f:(function|None->Map.Using_comparator.singletoninner_keydata~comparator:inner_comparator|Someinner_map->Map.add_exninner_map~key:inner_key~data))~remove:(fun~key:inner_key~dataouter_map->matchindexdatawith|None->outer_map|Someouter_key->Map.changeouter_mapouter_key~f:(function|None->failwith"BUG: Hit supposedly impossible case in Incr_map.index_by"|Someinner_map->letinner_map=Map.removeinner_mapinner_keyinifMap.is_emptyinner_mapthenNoneelseSomeinner_map)));;(** Find two keys in map by index, O(n). We use just one fold (two Map.nth would use two)
and optimize for keys close to either beginning or end by using either fold or
fold_right.
*)moduleKey_status=structtype'kt=|Knownof'k|Known_none|Unknownletis_known=function|Unknown->false|_->true;;letto_option=function|Unknown|Known_none->None|Knownk->Somek;;endletfind_key_range_linear(typek)~from~to_(map:(k,_,_)Map.t):(k*koption)option=letopenKey_statusinletlen=Map.lengthmapinletbegin_key=ifInt.(>=)fromlenthenKnown_noneelseUnknowninletend_key=ifInt.(>=)to_lenthenKnown_noneelseUnknowninletfind_keysfold~start_pos~advance_pos=with_return(fun{return}->foldmap~init:(begin_key,end_key,start_pos)~f:(fun~key~data:_(begin_key,end_key,pos)->letbegin_key=ifInt.(=)posfromthenKnownkeyelsebegin_keyinletend_key=ifInt.(=)posto_thenKnownkeyelseend_keyinifis_knownbegin_key&&is_knownend_keythenreturn(begin_key,end_key,pos)elsebegin_key,end_key,advance_pospos))inletbegin_key,end_key,_=(* Searching from left takes O(to_), from right - O(len - from), so select the
smaller one. *)ifto_<len-fromthenfind_keysMap.fold~start_pos:0~advance_pos:(funpos->pos+1)elsefind_keysMap.fold_right~start_pos:(len-1)~advance_pos:(funpos->pos-1)inOption.map(Key_status.to_optionbegin_key)~f:(funbegin_key->begin_key,Key_status.to_optionend_key);;letnth_from_either_side(typek)n(map:(k,_,_)Map.t):koption=Option.map~f:fst(find_key_range_linear~from:n~to_:nmap);;(** Find key [by] positions earlier/later in a map. Returns none if out of bounds. *)letrecoffset(key:'k)(map:('k,_,_)Map.t)~by:'koption=ifInt.(=)by0thenSomekeyelse(letclosest_dir,add=ifInt.(<)by0then`Less_than,1else`Greater_than,-1inmatchMap.closest_keymapclosest_dirkeywith|None->None|Some(key,_)->offsetkeymap~by:(by+add));;(** Find how we need to move [key] if [changed_key] changed in the given
way *)letfind_offset~compare~key~changed_keychange=ifInt.(<)(comparechanged_keykey)0then(matchchangewith|`Left_->1|`Right_->-1|_->0)else0;;(** Range map by indices *)letsubrange_by_rank(typekstate_witness)?data_equal(map:((k,_,_)Map.t,state_witness)Incremental.t)(range:(intMaybe_bound.t*intMaybe_bound.t,state_witness)Incremental.t)=letfind_key_range(range:(int*int,state_witness)Incremental.t):((k*koption)option,state_witness)Incremental.t=with_old2maprange~f:(fun~oldmap(from,to_)->(* This function returns no keys, only begin key, or begin and end keys.
These are the keys at [from] and [to_] positions in the map, or None if the
indices are too big. As always [0 <= from && from <= to_], there is no
possibility of only [to_] being a valid position.
*)ifInt.(<)to_from||Int.(<)from0thenraise_s[%message"Invalid indices"(from:int)(to_:int)];matcholdwith|Some(old_map,(old_from,old_to),Some(begin_key,end_key_opt))->letfind_offset=find_offset~compare:(Map.comparatormap).compareinletrange_offset_begin=from-old_frominletrange_offset_end=to_-old_toinletadjust_and_offset~bykey=letby=by+ifby>=0&¬(Map.memmapkey)then1else0inoffsetkeymap~byin(* We only care about the keys changing and not the data, so [data_equal] here
can be always true *)letdiff~init~f=Map.fold_symmetric_diff~data_equal:(fun__->true)old_mapmap~init~finletbegin_key_opt,end_key_opt=matchend_key_optwith|Someend_key->letmap_offset_begin,map_offset_end=diff~init:(0,0)~f:(fun(offset_begin,offset_end)(key,change)->(offset_begin+find_offset~key:begin_key~changed_key:keychange,offset_end+find_offset~key:end_key~changed_key:keychange))in(adjust_and_offsetbegin_key~by:(map_offset_begin+range_offset_begin),adjust_and_offsetend_key~by:(map_offset_end+range_offset_end))|None->letmap_offset_begin=diff~init:0~f:(funoffset_begin(key,change)->offset_begin+find_offset~key:begin_key~changed_key:keychange)in(adjust_and_offsetbegin_key~by:(map_offset_begin+range_offset_begin),nth_from_either_sideto_map)inassert(Option.for_all~f:(Map.memmap)begin_key_opt);assert(Option.for_all~f:(Map.memmap)end_key_opt);Option.mapbegin_key_opt~f:(funbegin_key->begin_key,end_key_opt)|None|Some(_,_,None)->(* On first run (when we have to) or when both the keys are none, run O(n)
scan. This is fine for keys-are-none case as it happens when the positions
are past end of the map, so they shouldn't be too far from end after the
map changes, and [find_key_range_linear] is fast in such case. *)find_key_range_linearmap~from~to_)in(* Handle different Maybe_bound cases and call find_key_range if necessary. It's
nicer to do this here as opposed to making find_key_range even more complicated *)letopenIncremental.Let_syntaxinlet(>>>)new_bound=Maybe_bound.map~f:(fun_->new_)boundinletreturn=Incremental.return(Incremental.statemap)inletkey_range=match%pattern_bindrangewith|Maybe_bound.Unbounded,Maybe_bound.Unbounded->return(Some(Maybe_bound.Unbounded,Maybe_bound.Unbounded))|(((Maybe_bound.Incll|Maybe_bound.Excll)aslb),((Maybe_bound.Inclu|Maybe_bound.Exclu)asub))->let%mapkey_range=find_key_range(Incremental.bothlu)andlb=lbandub=ubin(matchkey_rangewith|Some(begin_key,Someend_key)->Some(begin_key>>>lb,end_key>>>ub)|Some(begin_key,None)->Some(begin_key>>>lb,Unbounded)|None->None)|((Maybe_bound.Incll|Maybe_bound.Excll)aslb),Maybe_bound.Unbounded->let%mapkey_range=find_key_range(Incremental.bothll)andlb=lbin(matchkey_rangewith|Some(key,_)->Some(key>>>lb,Unbounded)|None->None)|Maybe_bound.Unbounded,((Maybe_bound.Inclu|Maybe_bound.Exclu)asub)->let%mapkey_range=find_key_range(Incremental.bothuu)andub=ubin(matchkey_rangewith|Some(key,_)->Some(Unbounded,key>>>ub)|None->None)insubrange?data_equalmapkey_range;;lettranspose:typek1k2vk1_cmpk2_cmpstate_witness.?data_equal:(v->v->bool)->(k2,k2_cmp)Map.comparator->((k1,(k2,v,k2_cmp)Map.t,k1_cmp)Map.t,state_witness)Incremental.t->((k2,(k1,v,k1_cmp)Map.t,k2_cmp)Map.t,state_witness)Incremental.t=fun?(data_equal=phys_equal)k2_comparatorm->with_comparatorm(funk1_comparator->letupdate:key:k1->old_data:(k2,v,k2_cmp)Map.t->new_data:(k2,v,k2_cmp)Map.t->(k2,(k1,v,k1_cmp)Map.t,k2_cmp)Map.t->(k2,(k1,v,k1_cmp)Map.t,k2_cmp)Map.t=fun~key:k1~old_data~new_dataacc->Map.fold_symmetric_diffold_datanew_data~data_equal~init:acc~f:(funacc(k2,diff)->letvalue=matchdiffwith|`Left_->None|`Rightx|`Unequal(_,x)->SomexinMap.changeacck2~f:(funacc_inner->letacc_inner=Map.change(Option.valueacc_inner~default:(Map.Using_comparator.empty~comparator:k1_comparator))k1~f:(fun_->value)inifMap.is_emptyacc_innerthenNoneelseSomeacc_inner))inletadd~key~data=update~key~old_data:(Map.emptyk2_comparator)~new_data:datainletremove~key~data=update~key~old_data:data~new_data:(Map.emptyk2_comparator)inunordered_foldm~init:(Map.emptyk2_comparator)~update~add~remove);;moduleFor_testing=structletfind_key_range_linear=find_key_range_linearendmoduleLookup=structtype('v,'w)entry={mutablesaved_value:'voption;node:('voption,'w)Incremental.Expert.Node.t}type('k,'v,'cmp,'w)t={mutablesaved_map:('k,'v,'cmp)Map.t(* We may have multiple entries per key if nodes become necessary again after being
removed. *);mutablelookup_entries:('k,('v,'w)entrylist,'cmp)Map.t;updater_node:(unit,'w)Incremental.t;scope:'wIncremental.Scope.t}moduleM(K:sigtypettypecomparator_witnessend)=structtypenonrec('v,'w)t=(K.t,'v,K.comparator_witness,'w)tendletcreate?(data_equal=phys_equal)input_map~comparator=letrecself=lazy(letupdater_node=Incremental.mapinput_map~f:(funinput_map->let(lazyself)=selfinMap.fold_symmetric_diffself.saved_mapinput_map~data_equal~init:()~f:(fun()(key,changed_value)->letentries=Map.find_multiself.lookup_entrieskeyinList.iterentries~f:(funentry->entry.saved_value<-(matchchanged_valuewith|`Left_->None|`Rightnew_value|`Unequal(_,new_value)->Somenew_value);Incremental.Expert.Node.make_staleentry.node));self.saved_map<-input_map)inletempty_map=Map.Using_comparator.empty~comparatorin{saved_map=empty_map;lookup_entries=empty_map;updater_node;scope=Incremental.Scope.current(Incremental.stateinput_map)()})inLazy.forceself;;letslow_path_link_entrytentry~key~is_now_observable=let(lazyentry)=entryinletcurrent_entries=Map.find_multit.lookup_entrieskeyinletis_linked=List.existscurrent_entries~f:(phys_equalentry)inifBool.equalis_linkedis_now_observablethen()elseifis_now_observablethent.lookup_entries<-Map.updatet.lookup_entrieskey~f:(function|Some(other_entry::_asother_entries)->(* Update this entry's value to be current. *)entry.saved_value<-other_entry.saved_value;entry::other_entries|None|Some[]->entry.saved_value<-Map.findt.saved_mapkey;[entry])else(letnew_entries=List.filtercurrent_entries~f:(funx->not(phys_equalentryx))int.lookup_entries<-(ifList.is_emptynew_entriesthenMap.removet.lookup_entrieskeyelseMap.sett.lookup_entries~key~data:new_entries));;letslow_path_create_nodetkey=letincremental_state=Incremental.statet.updater_nodeinIncremental.Scope.withinincremental_statet.scope~f:(fun()->letrecentry=lazy{saved_value=Map.findt.saved_mapkey;node=Incremental.Expert.Node.createincremental_state(fun()->(forceentry).saved_value)~on_observability_change:(slow_path_link_entrytentry~key)}inlet(lazyentry)=entryinIncremental.Expert.Node.add_dependencyentry.node(Incremental.Expert.Dependency.createt.updater_node);Incremental.Expert.Node.watchentry.node);;letfindtkey=matchMap.find_multit.lookup_entrieskeywith|entry::_->Incremental.Expert.Node.watchentry.node|[]->slow_path_create_nodetkey;;moduleFor_debug=structletsexp_of_entrysexp_of_valueentry=let{saved_value;node}=entryinletnode=Incremental.Expert.Node.watchnodein[%sexp{saved_value:valueoption;node_info=(Incremental.user_infonode:(Info.toption[@sexp.option]));node_is_const=(Option.some_if(Incremental.is_constnode)():(unitoption[@sexp.option]));node_is_invalid=(Option.some_if(not(Incremental.is_validnode))():(unitoption[@sexp.option]));node_is_unnecessary=(Option.some_if(not(Incremental.is_necessarynode))():(unitoption[@sexp.option]))}];;letsexp_of_tsexp_of_keysexp_of_valuet=letinfo_per_key=Map.merget.saved_mapt.lookup_entries~f:(fun~keydata->letactual_value,entries=matchdatawith|`Leftx->Somex,[]|`Righty->None,y|`Both(x,y)->Somex,yinSome[%sexp{key:key;actual_value:(valueoption[@sexp.option]);entries:valueentrylist}])inSexp.List(Map.datainfo_per_key);;endendendmoduletypeS=sigtypestate_witnessincludeS_genwithtype'aIncr.t=('a,state_witness)Incremental.tandtype'aIncr.Cutoff.t='aIncremental.Cutoff.tandtype('k,'v,'cmp)Lookup.t=('k,'v,'cmp,state_witness)Generic.Lookup.tendmoduleMake(Incr:Incremental.S)=structincludeGenericletflattenx=flattenIncr.State.txmoduleLookup=structincludeLookuptype('k,'v,'cmp)t=('k,'v,'cmp,Incr.state_witness)Lookup.tmoduleM(K:sigtypettypecomparator_witnessend):sigtypenonrec'vt=(K.t,'v,K.comparator_witness)tend=structtypenonrec'vt=(K.t,'v,K.comparator_witness)tendendendincludeGeneric