123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878(** Abstract domains for collections of elements from disjoint unions of domains.
Formally, the elements form a cofibered domain from a discrete category.
Elements are grouped into disjoint buckets by a congruence or/and a projection.
All operations on elements are performed bucket-wise and must be bucket-closed.
Examples of such domains are path-sensitivity and address sets. *)(** {1 Sets} *)(** {2 By projection} *)(** Buckets defined by projection.
The module is the image (representative) of the projection function {!of_elt}. *)moduletypeRepresentative=sigincludePrintable.S(** @closed *)typeelt(** Type of elements, i.e. the domain of the projection function {!of_elt}. *)valof_elt:elt->t(** Projection function. *)end(** Set of elements [E.t] grouped into buckets by [R],
where each bucket is described by the set [B].
Common choices for [B] are {!SetDomain.Joined} and {!HoareDomain.SetEM}.
Handles {!Lattice.BotValue} from [B]. *)moduleProjectiveSet(E:Printable.S)(B:SetDomain.Swithtypeelt=E.t)(R:Representativewithtypeelt=E.t):sigincludeSetDomain.Swithtypeelt=E.tvalfold_buckets:(R.t->B.t->'a->'a)->t->'a->'aend=structtypeelt=E.tmoduleM=MapDomain.MapBot(R)(B)(** Invariant: no explicit bot buckets.
Required for efficient [is_empty], [cardinal] and [choose]. *)letname()="ProjectiveSet ("^B.name()^")"(* explicitly delegate, so we don't accidentally delegate too much *)typet=M.tletequal=M.equalletcompare=M.comparelethash=M.hashlettag=M.tagletrelift=M.reliftletis_bot=M.is_botletbot=M.botletis_top=M.is_toplettop=M.topletis_empty=M.is_emptyletempty=M.emptyletcardinal=M.cardinalletleq=M.leqletjoin=M.joinletpretty_diff=M.pretty_diffletfoldfma=M.fold(fun_ea->B.foldfea)maletiterfm=M.iter(fun_e->B.iterfe)mletexistspm=M.exists(fun_e->B.existspe)mletfor_allpm=M.for_all(fun_e->B.for_allpe)mletsingletone=M.singleton(R.of_elte)(B.singletone)letchoosem=B.choose(snd(M.choosem))letmemem=matchM.find_opt(R.of_elte)mwith|Someb->B.memeb|None->falseletaddem=letr=R.of_elteinletb'=matchM.find_optrmwith|Someb->B.addeb|None->B.singletoneinM.addrb'mletremoveem=letr=R.of_elteinmatchM.find_optrmwith|Someb->beginmatchB.removeebwith|b'whenB.is_botb'->M.removerm(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->M.removerm(* remove bot bucket to preserve invariant *)|b'->M.addrb'mend|None->mletdiffm1m2=M.merge(fun_b1b2->matchb1,b2with|Someb1,Someb2->beginmatchB.diffb1b2with|b'whenB.is_botb'->None(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->None(* remove bot bucket to preserve invariant *)|b'->Someb'end|Some_,None->b1|None,_->None)m1m2letof_listes=List.fold_left(funacce->addeacc)(empty())esletelementsm=foldList.consm[](* no intermediate per-bucket lists *)letmapfm=fold(funeacc->add(fe)acc)m(empty())(* no intermediate lists *)letwidenm1m2=Lattice.assert_valid_widen~leq~pretty_diffm1m2;M.widenm1m2letmeetm1m2=M.merge(fun_b1b2->matchb1,b2with|Someb1,Someb2->beginmatchB.meetb1b2with|b'whenB.is_botb'->None(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->None(* remove bot bucket to preserve invariant *)|b'->Someb'end|_,_->None)m1m2letnarrowm1m2=M.merge(fun_b1b2->matchb1,b2with|Someb1,Someb2->beginmatchB.narrowb1b2with|b'whenB.is_botb'->None(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->None(* remove bot bucket to preserve invariant *)|b'->Someb'end|_,_->None)m1m2letunion=joinletinter=meetletsubset=leqincludeSetDomain.Print(E)(structtypenonrect=ttypenonrecelt=eltletelements=elementsletiter=iterend)letarbitrary()=failwith"Projective.arbitrary"letfilterpm=SetDomain.unsupported"Projective.filter"letpartitionpm=SetDomain.unsupported"Projective.partition"letmin_eltm=SetDomain.unsupported"Projective.min_elt"letmax_eltm=SetDomain.unsupported"Projective.max_elt"letdisjointm1m2=is_empty(interm1m2)(* TODO: optimize? *)letfold_buckets=M.foldendmoduletypeMayEqualSetDomain=sigincludeSetDomain.Svalmay_be_equal:elt->elt->boolendmoduleProjectiveSetPairwiseMeet(E:Printable.S)(B:MayEqualSetDomainwithtypeelt=E.t)(R:Representativewithtypeelt=E.t):SetDomain.Swithtypeelt=E.t=structincludeProjectiveSet(E)(B)(R)letmeetm1m2=letmeet_bucketsb1b2acc=B.fold(fune1acc->B.fold(fune2acc->ifB.may_be_equale1e2thenadde1(adde2acc)elseacc)b2acc)b1accinfold_buckets(fun_b1acc->fold_buckets(fun_b2acc->meet_bucketsb1b2acc)m2acc)m1(empty())end(** {2 By congruence} *)(** Buckets defined by congruence. *)moduletypeCongruence=sigtypeelt(** Type of elements. *)valcong:elt->elt->bool(** Congruence relation on elements. *)end(** Set of elements [E.t] grouped into buckets by [C],
where each bucket is described by the set [B].
Common choices for [B] are {!SetDomain.Joined} and {!HoareDomain.SetEM}.
Handles {!Lattice.BotValue} from [B]. *)modulePairwiseSet(E:Printable.S)(B:SetDomain.Swithtypeelt=E.t)(C:Congruencewithtypeelt=E.t):SetDomain.Swithtypeelt=E.t=structtypeelt=E.tmoduleS=SetDomain.Make(B)(** Invariant: no explicit bot buckets.
Required for efficient [is_empty], [cardinal] and [choose]. *)letname()="Pairwise ("^B.name()^")"(* explicitly delegate, so we don't accidentally delegate too much *)typet=S.tletequal=S.equalletcompare=S.comparelethash=S.hashlettag=S.tagletrelift=S.reliftletis_bot=S.is_botletbot=S.botletis_top=S.is_toplettop=S.topletis_empty=S.is_emptyletempty=S.emptyletcardinal=S.cardinalletfoldfsa=S.fold(funba->B.foldfba)saletiterfs=S.iter(funb->B.iterfb)sletexistsps=S.exists(funb->B.existspb)sletfor_allps=S.for_all(funb->B.for_allpb)sletsingletone=S.singleton(B.singletone)letchooses=B.choose(S.chooses)(* based on SetDomain.SensitiveConf *)letmemes=S.exists(funb->C.cong(B.chooseb)e&&B.memeb)sletaddes=let(s_match,s_rest)=S.partition(funb->C.cong(B.chooseb)e)sinletb'=matchS.chooses_matchwith|b->assert(S.cardinals_match=1);B.addeb|exceptionNot_found->B.singletoneinS.addb's_restletremovees=let(s_match,s_rest)=S.partition(funb->C.cong(B.chooseb)e)sinmatchS.chooses_matchwith|b->assert(S.cardinals_match=1);beginmatchB.removeebwith|b'whenB.is_botb'->s_rest(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->s_rest(* remove bot bucket to preserve invariant *)|b'->S.addb'send|exceptionNot_found->sletdiffs1s2=letfb2(s1,acc)=lete2=B.chooseb2inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(B.chooseb1)e2)s1inletacc'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);beginmatchB.diffb1b2with|b'whenB.is_botb'->acc(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->acc(* remove bot bucket to preserve invariant *)|b'->S.addb'accend|exceptionNot_found->accin(s1_rest,acc')inlet(s1',acc)=S.foldfs2(s1,empty())inS.unions1'accletof_listes=List.fold_left(funacce->addeacc)(empty())esletelementsm=foldList.consm[](* no intermediate per-bucket lists *)letmapfs=fold(funeacc->add(fe)acc)s(empty())(* no intermediate lists *)letleqs1s2=S.for_all(funb1->lete1=B.chooseb1inS.exists(funb2->C.cong(B.chooseb2)e1&&B.leqb1b2)s2)s1letpretty_diff()(s1,s2)=(* based on HoareDomain.Set *)lets1_not_leq=S.filter(funb1->lete1=B.chooseb1innot(S.exists(funb2->C.cong(B.chooseb2)e1&&B.leqb1b2)s2))s1inletb1_not_leq=S.chooses1_not_leqinlete1_not_leq=B.chooseb1_not_leqinGoblintCil.Pretty.(dprintf"%a:\n"B.prettyb1_not_leq++S.fold(funb2acc->ifC.cong(B.chooseb2)e1_not_leqthendprintf"not leq %a because %a\n"B.prettyb2B.pretty_diff(b1_not_leq,b2)++accelsedprintf"not cong %a\n"B.prettyb2++acc)s2nil)letjoins1s2=letfb2(s1,acc)=lete2=B.chooseb2inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(B.chooseb1)e2)s1inletb'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);B.joinb1b2|exceptionNot_found->b2in(s1_rest,S.addb'acc)inlet(s1',acc)=S.foldfs2(s1,empty())inS.unions1'accletwidens1s2=Lattice.assert_valid_widen~leq~pretty_diffs1s2;letfb2(s1,acc)=lete2=B.chooseb2inlet(s1_match,s1_rest)=S.partition(fune1->C.cong(B.choosee1)e2)s1inletb'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);B.widenb1b2|exceptionNot_found->b2in(s1_rest,S.addb'acc)inlet(s1',acc)=S.foldfs2(s1,empty())inassert(is_emptys1');(* since [leq s1 s2], folding over s2 should remove all s1 *)acc(* TODO: extra union s2 needed? *)letmeets1s2=letfb2(s1,acc)=lete2=B.chooseb2inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(B.chooseb1)e2)s1inletacc'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);beginmatchB.meetb1b2with|b'whenB.is_botb'->acc(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->acc(* remove bot bucket to preserve invariant *)|b'->S.addb'accend|exceptionNot_found->accin(s1_rest,acc')insnd(S.foldfs2(s1,S.empty()))letnarrows1s2=letfb2(s1,acc)=lete2=B.chooseb2inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(B.chooseb1)e2)s1inletacc'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);beginmatchB.narrowb1b2with|b'whenB.is_botb'->acc(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->acc(* remove bot bucket to preserve invariant *)|b'->S.addb'accend|exceptionNot_found->accin(s1_rest,acc')insnd(S.foldfs2(s1,S.empty()))letunion=joinletinter=meetletsubset=leqincludeSetDomain.Print(E)(structtypenonrect=ttypenonrecelt=eltletelements=elementsletiter=iterend)letarbitrary()=failwith"Pairwise.arbitrary"letfilterps=SetDomain.unsupported"Pairwise.filter"letpartitionps=SetDomain.unsupported"Pairwise.partition"letmin_elts=SetDomain.unsupported"Pairwise.min_elt"letmax_elts=SetDomain.unsupported"Pairwise.max_elt"letdisjoints1s2=is_empty(inters1s2)(* TODO: optimize? *)end(** Buckets defined by a coarse projection and a fine congruence.
Congruent elements must have the same representative, but not vice versa ({!Representative} would then suffice). *)moduletypeRepresentativeCongruence=sigincludeRepresentativeincludeCongruencewithtypeelt:=eltend(** Set of elements [E.t] grouped into buckets by [RC],
where each bucket is described by the set [B]. *)moduleCombinedSet(E:Printable.S)(B:SetDomain.Swithtypeelt=E.t)(RC:RepresentativeCongruencewithtypeelt=E.t)=ProjectiveSet(E)(PairwiseSet(E)(B)(RC))(RC)(** {1 Maps}
Generalization of above sets into maps, whose key set behaves like above sets,
but each element can also be associated with a value. *)(** {2 By projection} *)(** Map of keys [E.t] grouped into buckets by [R],
where each bucket is described by the map [B] with values [V.t].
Common choice for [B] is {!MapDomain.Joined}.
Handles {!Lattice.BotValue} from [B]. *)moduleProjectiveMap(E:Printable.S)(V:Printable.S)(B:MapDomain.Swithtypekey=E.tandtypevalue=V.t)(R:Representativewithtypeelt=E.t):MapDomain.Swithtypekey=E.tandtypevalue=B.value=structtypekey=E.ttypevalue=B.valuemoduleM=MapDomain.MapBot(R)(B)(** Invariant: no explicit bot buckets.
Required for efficient [is_empty], [cardinal] and [choose]. *)letname()="ProjectiveMap ("^B.name()^")"(* explicitly delegate, so we don't accidentally delegate too much *)typet=M.tletequal=M.equalletcompare=M.comparelethash=M.hashlettag=M.tagletrelift=M.reliftletis_bot=M.is_botletbot=M.botletis_top=M.is_toplettop=M.topletis_empty=M.is_emptyletempty=M.emptyletcardinal=M.cardinalletleq=M.leqletjoin=M.joinletpretty_diff=M.pretty_diffletfoldfma=M.fold(fun_ea->B.foldfea)maletiterfm=M.iter(fun_e->B.iterfe)mletexistspm=M.exists(fun_e->B.existspe)mletfor_allpm=M.for_all(fun_e->B.for_allpe)mletsingletonev=M.singleton(R.of_elte)(B.singletonev)letchoosem=B.choose(snd(M.choosem))letmemem=matchM.find_opt(R.of_elte)mwith|Someb->B.memeb|None->falseletfindem=letr=R.of_elteinletb=M.findrmin(* raises Not_found *)B.findeb(* raises Not_found *)letfind_optem=letr=R.of_elteinmatchM.find_optrmwith|Someb->B.find_opteb|None->Noneletaddevm=letr=R.of_elteinletb'=matchM.find_optrmwith|Someb->B.addevb|None->B.singletonevinM.addrb'mletremoveem=letr=R.of_elteinmatchM.find_optrmwith|Someb->beginmatchB.removeebwith|b'whenB.is_botb'->M.removerm(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->M.removerm(* remove bot bucket to preserve invariant *)|b'->M.addrb'mend|None->mletadd_listevsm=List.fold_left(funacc(e,v)->addevacc)mevsletadd_list_setesvm=List.fold_left(funacce->addevacc)mesletadd_list_funesfm=List.fold_left(funacce->adde(fe)acc)mesletbindingsm=fold(funevacc->(e,v)::acc)m[](* no intermediate per-bucket lists *)letmapfm=M.map(funb->B.mapfb)mletmapifm=M.map(funb->B.mapifb)mletlong_map2fm1m2=M.long_map2(funb1b2->B.long_map2fb1b2)m1m2letmap2fm1m2=M.map2(funb1b2->B.map2fb1b2)m1m2letmergefm1m2=failwith"ProjectiveMap.merge"(* TODO: ? *)letwidenm1m2=Lattice.assert_valid_widen~leq~pretty_diffm1m2;M.widenm1m2letmeetm1m2=M.merge(fun_b1b2->matchb1,b2with|Someb1,Someb2->beginmatchB.meetb1b2with|b'whenB.is_botb'->None(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->None(* remove bot bucket to preserve invariant *)|b'->Someb'end|_,_->None)m1m2letnarrowm1m2=M.merge(fun_b1b2->matchb1,b2with|Someb1,Someb2->beginmatchB.narrowb1b2with|b'whenB.is_botb'->None(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->None(* remove bot bucket to preserve invariant *)|b'->Someb'end|_,_->None)m1m2includeMapDomain.Print(E)(V)(structtypenonrect=ttypenonreckey=keytypenonrecvalue=valueletfold=foldletiter=iterend)letarbitrary()=failwith"ProjectiveMap.arbitrary"letfilterpm=failwith"ProjectiveMap.filter"letleq_with_fct___=failwith"ProjectiveMap.leq_with_fct"letjoin_with_fct___=failwith"ProjectiveMap.join_with_fct"letwiden_with_fct___=failwith"ProjectiveMap.widen_with_fct"end(** {2 By congruence} *)(** Map of keys [E.t] grouped into buckets by [C],
where each bucket is described by the map [B] with values [R.t].
Common choice for [B] is {!MapDomain.Joined}.
Handles {!Lattice.BotValue} from [B]. *)modulePairwiseMap(E:Printable.S)(R:Printable.S)(B:MapDomain.Swithtypekey=E.tandtypevalue=R.t)(C:Congruencewithtypeelt=E.t):MapDomain.Swithtypekey=E.tandtypevalue=B.value=structtypekey=E.ttypevalue=B.valuemoduleS=SetDomain.Make(B)(** Invariant: no explicit bot buckets.
Required for efficient [is_empty], [cardinal] and [choose]. *)letname()="PairwiseMap ("^B.name()^")"(* explicitly delegate, so we don't accidentally delegate too much *)typet=S.tletequal=S.equalletcompare=S.comparelethash=S.hashlettag=S.tagletrelift=S.reliftletis_bot=S.is_botletbot=S.botletis_top=S.is_toplettop=S.topletis_empty=S.is_emptyletempty=S.emptyletcardinal=S.cardinalletfoldfsa=S.fold(funba->B.foldfba)saletiterfs=S.iter(funb->B.iterfb)sletexistsps=S.exists(funb->B.existspb)sletfor_allps=S.for_all(funb->B.for_allpb)sletsingletoner=S.singleton(B.singletoner)letchooses=B.choose(S.chooses)(* based on SetDomain.SensitiveConf *)letmemes=S.exists(funb->C.cong(fst(B.chooseb))e&&B.memeb)sletfindes=let(s_match,s_rest)=S.partition(funb->C.cong(fst(B.chooseb))e)sinletb=S.chooses_matchin(* raises Not_found *)assert(S.cardinals_match=1);B.findeb(* raises Not_found *)letfind_optes=let(s_match,s_rest)=S.partition(funb->C.cong(fst(B.chooseb))e)sinmatchS.chooses_matchwith|b->assert(S.cardinals_match=1);B.find_opteb|exceptionNot_found->Noneletadders=let(s_match,s_rest)=S.partition(funb->C.cong(fst(B.chooseb))e)sinletb'=matchS.chooses_matchwith|b->assert(S.cardinals_match=1);B.adderb|exceptionNot_found->B.singletonerinS.addb's_restletremovees=let(s_match,s_rest)=S.partition(funb->C.cong(fst(B.chooseb))e)sinmatchS.chooses_matchwith|b->assert(S.cardinals_match=1);beginmatchB.removeebwith|b'whenB.is_botb'->s_rest(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->s_rest(* remove bot bucket to preserve invariant *)|b'->S.addb'send|exceptionNot_found->sletadd_listersm=List.fold_left(funacc(e,r)->adderacc)mersletadd_list_setesrm=List.fold_left(funacce->adderacc)mesletadd_list_funesfm=List.fold_left(funacce->adde(fe)acc)mesletbindingsm=fold(funeracc->(e,r)::acc)m[](* no intermediate per-bucket lists *)letmapfm=S.map(funb->B.mapfb)mletmapifm=S.map(funb->B.mapifb)mletlong_map2fs1s2=letfb2(s1,acc)=lete2=fst(B.chooseb2)inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(fst(B.chooseb1))e2)s1inletb'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);B.long_map2fb1b2|exceptionNot_found->b2in(s1_rest,S.addb'acc)inlet(s1',acc)=S.foldfs2(s1,empty())inS.unions1'accletmap2fs1s2=letfb2(s1,acc)=lete2=fst(B.chooseb2)inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(fst(B.chooseb1))e2)s1inletacc'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);beginmatchB.map2fb1b2with|b'whenB.is_botb'->acc(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->acc(* remove bot bucket to preserve invariant *)|b'->S.addb'accend|exceptionNot_found->accin(s1_rest,acc')insnd(S.foldfs2(s1,S.empty()))letmergefm1m2=failwith"PairwiseMap.merge"(* TODO: ? *)letleqs1s2=S.for_all(funb1->lete1=fst(B.chooseb1)inS.exists(funb2->C.cong(fst(B.chooseb2))e1&&B.leqb1b2)s2)s1letpretty_diff()(s1,s2)=(* based on PairwiseSet *)lets1_not_leq=S.filter(funb1->lete1=fst(B.chooseb1)innot(S.exists(funb2->C.cong(fst(B.chooseb2))e1&&B.leqb1b2)s2))s1inletb1_not_leq=S.chooses1_not_leqinlete1_not_leq=fst(B.chooseb1_not_leq)inGoblintCil.Pretty.(dprintf"%a:\n"B.prettyb1_not_leq++S.fold(funb2acc->ifC.cong(fst(B.chooseb2))e1_not_leqthendprintf"not leq %a because %a\n"B.prettyb2B.pretty_diff(b1_not_leq,b2)++accelsedprintf"not cong %a\n"B.prettyb2++acc)s2nil)letjoins1s2=letfb2(s1,acc)=lete2=fst(B.chooseb2)inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(fst(B.chooseb1))e2)s1inletb'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);B.joinb1b2|exceptionNot_found->b2in(s1_rest,S.addb'acc)inlet(s1',acc)=S.foldfs2(s1,empty())inS.unions1'accletwidens1s2=Lattice.assert_valid_widen~leq~pretty_diffs1s2;letfb2(s1,acc)=lete2=fst(B.chooseb2)inlet(s1_match,s1_rest)=S.partition(fune1->C.cong(fst(B.choosee1))e2)s1inletb'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);B.widenb1b2|exceptionNot_found->b2in(s1_rest,S.addb'acc)inlet(s1',acc)=S.foldfs2(s1,empty())inassert(is_emptys1');(* since [leq s1 s2], folding over s2 should remove all s1 *)acc(* TODO: extra union s2 needed? *)letmeets1s2=letfb2(s1,acc)=lete2=fst(B.chooseb2)inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(fst(B.chooseb1))e2)s1inletacc'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);beginmatchB.meetb1b2with|b'whenB.is_botb'->acc(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->acc(* remove bot bucket to preserve invariant *)|b'->S.addb'accend|exceptionNot_found->accin(s1_rest,acc')insnd(S.foldfs2(s1,S.empty()))letnarrows1s2=letfb2(s1,acc)=lete2=fst(B.chooseb2)inlet(s1_match,s1_rest)=S.partition(funb1->C.cong(fst(B.chooseb1))e2)s1inletacc'=matchS.chooses1_matchwith|b1->assert(S.cardinals1_match=1);beginmatchB.narrowb1b2with|b'whenB.is_botb'->acc(* remove bot bucket to preserve invariant *)|exceptionLattice.BotValue->acc(* remove bot bucket to preserve invariant *)|b'->S.addb'accend|exceptionNot_found->accin(s1_rest,acc')insnd(S.foldfs2(s1,S.empty()))includeMapDomain.Print(E)(R)(structtypenonrect=ttypenonreckey=keytypenonrecvalue=valueletfold=foldletiter=iterend)letarbitrary()=failwith"PairwiseMap.arbitrary"letfilterps=failwith"PairwiseMap.filter"letleq_with_fct___=failwith"PairwiseMap.leq_with_fct"letjoin_with_fct___=failwith"PairwiseMap.join_with_fct"letwiden_with_fct___=failwith"PairwiseMap.widen_with_fct"end