123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298(**************************************************************************)(* This file is part of the Codex semantics library. *)(* *)(* Copyright (C) 2013-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* 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, version 2.1. *)(* *)(* It 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. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE). *)(* *)(**************************************************************************)(** {1 Nodes} *)(** {2 Nodes without values (simple nodes)} *)moduleMakeNode(Elt:Parameters.SIMPLE_GENERIC_ELT)(Relation:Parameters.GENERIC_GROUP)=structtype'at={mutableparent:'aparent;payload:'aElt.t}and'aparent=|Node:'bt*('a,'b)Relation.t->'aparent|Rootletpolyeqab=Elt.polyeqa.payloadb.payloadmoduleRelation=Relationletget_parentx=x.parentletset_parentxv=x.parent<-vletpayloadx=x.payloadletmake_nodepayload={payload;parent=Root}endmoduleMakeNumberedNode(Elt:HetHashtbl.HETEROGENEOUS_HASHED_TYPE)(Relation:Parameters.GENERIC_GROUP)()=structincludeMakeNode(Elt)(Relation)moduleH=HetHashtbl.Make(Elt)(structtypenonrec('a,_)t='atend)letnodes:unitH.t=H.create100letmake_nodepayload=letnd=make_nodepayloadinH.addnodespayloadnd;ndletget_nodepayload=H.find_optnodespayloadletget_or_make_nodepayload=matchget_nodepayloadwith|Somet->t|None->make_nodepayloadend(** {2 Nodes with values} *)moduleMakeValuedNode(Elt:Parameters.SIMPLE_GENERIC_ELT)(Relation:Parameters.GENERIC_GROUP)(Value:Parameters.SIMPLE_GENERIC_VALUEwithtype('a,'b)relation=('a,'b)Relation.t)=structtype'at={mutableparent:'aparent;payload:'aElt.t}and'aparent=|Node:'bt*('a,'b)Relation.t->'aparent|Rootof'arootand'aroot={mutablevalue:'aValue.t;mutablesize:int}letpolyeqab=Elt.polyeqa.payloadb.payloadmoduleRelation=RelationmoduleValue=Valueletget_parentx=x.parentletset_parentxv=x.parent<-vletpayloadx=x.payloadletmake_nodepayloadvalue={payload;parent=Root{value;size=1}}endmoduleMakeValuedNumberedNode(Elt:HetHashtbl.HETEROGENEOUS_HASHED_TYPE)(Relation:Parameters.GENERIC_GROUP)(Value:Parameters.SIMPLE_GENERIC_VALUEwithtype('a,'b)relation=('a,'b)Relation.t)()=structincludeMakeValuedNode(Elt)(Relation)(Value)moduleH=HetHashtbl.Make(Elt)(structtypenonrec('a,_)t='atend)letnodes:unitH.t=H.create100letmake_nodepayloadvalue=letnd=make_nodepayloadvalueinH.addnodespayloadnd;ndletget_nodepayload=H.find_optnodespayloadletget_or_make_nodepayloadvalue=matchget_nodepayloadwith|Somet->t|None->make_nodepayloadvalueendmoduleHashtblSimpleNode(Elt:HetHashtbl.HETEROGENEOUS_HASHED_TYPE)(Relation:Parameters.GENERIC_GROUP)=structtype'at='aElt.ttype'aparent=|Node:'bt*('a,'b)Relation.t->'aparent|Rootletpolyeq=Elt.polyeqmoduleH=HetHashtbl.Make(Elt)(structtype('a,_)t='aparentend)lettable:unitH.t=H.create125letget_parentx=matchH.findtablexwith|x->x|exceptionNot_found->Rootletset_parentx=function|Root->H.removetablex|n->H.replacetablexnmoduleRelation=Relationend(** {1 Union find structures} *)moduleGenericRelationalValued(Node:Parameters.UF_NODE_WITH_VALUE)=structopenNodetype'at='aNode.ttype('a,'b)relation=('a,'b)Relation.ttype'avalue='aValue.ttype'aret=Exists_b:'bt*'broot*('a,'b)Relation.t->'aretlet(**)=Relation.composelet(~~)=Relation.inverseletrecfind_repr:typea.at->aret=funn->matchget_parentnwith|Rootr->Exists_b(n,r,Relation.identity)|Node(parent,rel_parent)->let(Exists_b(repr,root,rel_repr))=find_reprparentinletrel=rel_repr**rel_parentinlet()=set_parentn(Node(repr,rel))inExists_b(repr,root,rel)type'anode_through_relation=NodeThoughRelation:'bt*('a,'b)Relation.t->'anode_through_relationtype'avalue_through_relation=ValueThroughRelation:'bValue.t*('a,'b)Relation.t->'avalue_through_relationtype'anode_and_value_through_relation=NodeValueThroughRelation:'bt*'bValue.t*('a,'b)Relation.t->'anode_and_value_through_relationletfind_representativen=let(Exists_b(repr,_,rel))=find_reprninNodeThoughRelation(repr,rel)letfind_valuen=let(Exists_b(_,root,rel))=find_reprninValueThroughRelation(root.value,rel)letfindn=let(Exists_b(repr,root,rel))=find_reprninNodeValueThroughRelation(repr,root.value,rel)letcheck_relatedab=letExists_b(ra,_,rel_a)=find_reprainletExists_b(rb,_,rel_b)=find_reprbinmatchpolyeqrarbwith|Eq->Some(~~rel_b**rel_a)|Diff->Noneletadd_valueav=letExists_b(_,root,rel)=find_reprainletv=Value.applyvrelinroot.value<-Value.meetroot.valuev(** Using the following diagram:
a --(rel)--> b
| |
rel_a rel_b
| |
V v
repr_a repr_b
=> repr_a --(inv rel_a; rel; rel_b)--> repr_b (where ";" is composition)
=> repr_b --(inv rel_b; inv rel; rel_a)--> repr_a
We do need to flip the arguments, as R.compose as the opposite argument order.
A good thing about generic types is that the typechecker ensure we generate a valid
relation here. *)letunionabrel=let(Exists_b(repr_a,root_a,rel_a))=find_reprainlet(Exists_b(repr_b,root_b,rel_b))=find_reprbinmatchpolyeqrepr_arepr_bwith|Eq->letold_rel=~~rel_b**rel_ainifRelation.equalrelold_relthenOk()elseErrorold_rel|Diff->(* Favor making [a] a child of [b] over the reverse, as this avoids a
relation flip. *)beginifroot_b.size>=root_a.sizethen(letrel=rel_b**rel**~~rel_ainroot_b.size<-root_b.size+root_a.size;root_b.value<-Value.meetroot_b.value(Value.applyroot_a.valuerel);set_parentrepr_a(Node(repr_b,rel)))elseletrel=rel_a**~~rel**~~rel_binroot_a.size<-root_b.size+root_a.size;root_a.value<-Value.meetroot_a.value(Value.applyroot_b.valuerel);set_parentrepr_b(Node(repr_a,rel))end;Ok()endmoduleGenericRelational(Node:Parameters.UF_NODE)=structopenNodetype'at='aNode.ttype('a,'b)relation=('a,'b)Relation.tlet(**)=Relation.composelet(~~)=Relation.inversetype'anode_through_relation=|NodeThoughRelation:'bt*('a,'b)Relation.t->'anode_through_relationletrecfind_representative:typea.at->anode_through_relation=funn->matchget_parentnwith|Root->NodeThoughRelation(n,Relation.identity)|Node(parent,rel_parent)->let(NodeThoughRelation(repr,rel_repr))=find_representativeparentinletrel=rel_repr**rel_parentinlet()=set_parentn(Node(repr,rel))inNodeThoughRelation(repr,rel)letcheck_relatedab=letNodeThoughRelation(ra,rel_a)=find_representativeainletNodeThoughRelation(rb,rel_b)=find_representativebinmatchpolyeqrarbwith|Eq->Some(~~rel_b**rel_a)|Diff->None(** Using the following diagram:
a --(rel)--> b
| |
rel_a rel_b
| |
V v
repr_a repr_b
=> repr_a --(inv rel_a; rel; rel_b)--> repr_b (where ";" is composition)
=> repr_b --(inv rel_b; inv rel; rel_a)--> repr_a
We do need to flip the arguments, as R.compose as the opposite argument order.
A good thing about generic types is that the typechecker ensure we generate a valid
relation here. *)letunionabrel=letNodeThoughRelation(repr_a,rel_a)=find_representativeainletNodeThoughRelation(repr_b,rel_b)=find_representativebinmatchpolyeqrepr_arepr_bwith|Eq->letold_rel=~~rel_b**rel_ainifRelation.equalrelold_relthenOk()elseErrorold_rel|Diff->(* Favor making [a] a child of [b] over the reverse, as this avoids a
relation flip. *)(* begin *)letrel=rel_b**rel**~~rel_ain(* root_b.size <- root_b.size + root_a.size; *)set_parentrepr_a(Node(repr_b,rel));(* else
let rel = rel_a ** ~~rel ** ~~rel_b in
root_a.size <- root_b.size + root_a.size;
set_parent repr_b (Node (repr_a, rel))
end; *)Ok()end