From 13a4798fcd096610d0faad9d89ec1dc24c96ea71 Mon Sep 17 00:00:00 2001
From: hra687261 <hichem.ait-el-hara@ocamlpro.com>
Date: Tue, 3 Jan 2023 15:26:19 +0100
Subject: [PATCH] [Array] DiffGraph and foreign_dom inetroperation In addition,
 added interface files and headers

---
 Makefile                                      |   4 +-
 .../solve/smt_array/{sat => errors}/eq.txt    |   0
 .../tests/solve/smt_array/errors/ite_1.txt    |  15 +
 .../tests/solve/smt_array/errors/ite_2.txt    |  13 +
 .../tests/solve/smt_array/errors/notes.txt    |   4 +
 colibri2/theories/LRA/LRA_build.mli           |  20 ++
 colibri2/theories/array/DiffGraph.ml          |  68 ++++
 colibri2/theories/array/DiffGraph.mli         |  49 +++
 colibri2/theories/array/GE_Array_DP.ml        | 339 ++++++++++++------
 colibri2/theories/array/GE_Array_DP.mli       |  23 ++
 colibri2/theories/array/SharingIsCaring.ml    |  83 ++---
 colibri2/theories/array/SharingIsCaring.mli   |  23 ++
 colibri2/theories/array/common.ml             | 141 +++++---
 colibri2/theories/array/common.mli            | 104 ++++++
 colibri2/theories/nseq/nseq.ml                |   2 +-
 colibri2/theories/nseq/nseq.mli               |   1 +
 16 files changed, 658 insertions(+), 231 deletions(-)
 rename colibri2/tests/solve/smt_array/{sat => errors}/eq.txt (100%)
 create mode 100644 colibri2/tests/solve/smt_array/errors/ite_1.txt
 create mode 100644 colibri2/tests/solve/smt_array/errors/ite_2.txt
 create mode 100644 colibri2/tests/solve/smt_array/errors/notes.txt
 create mode 100644 colibri2/theories/array/DiffGraph.mli
 create mode 100644 colibri2/theories/array/GE_Array_DP.mli
 create mode 100644 colibri2/theories/array/SharingIsCaring.mli
 create mode 100644 colibri2/theories/array/common.mli

diff --git a/Makefile b/Makefile
index 8b9998196..c5639cb0e 100644
--- a/Makefile
+++ b/Makefile
@@ -106,7 +106,9 @@ WITAN_FILES = colibri2/stdlib/std.ml colibri2/stdlib/std.mli		\
 	colibri2/stdlib/keys_sig.ml colibri2/stdlib/comp_keys.ml	\
 	colibri2/stdlib/comp_keys.mli colibri2/bin/options.ml
 
-OCAMLPRO_FILES = $(filter-out colibri2/theories/array/dune, $(wildcard colibri2/theories/array/*))
+OCAMLPRO_FILES = \
+	$(filter-out colibri2/theories/array/dune colibri2/theories/nseq/dune, \
+		$(wildcard colibri2/theories/array/* colibri2/theories/nseq/*))
 
 
 COLIBRI2_FILES = Makefile $(filter-out $(WHY3_FILES) $(OCAML_FILES)	\
diff --git a/colibri2/tests/solve/smt_array/sat/eq.txt b/colibri2/tests/solve/smt_array/errors/eq.txt
similarity index 100%
rename from colibri2/tests/solve/smt_array/sat/eq.txt
rename to colibri2/tests/solve/smt_array/errors/eq.txt
diff --git a/colibri2/tests/solve/smt_array/errors/ite_1.txt b/colibri2/tests/solve/smt_array/errors/ite_1.txt
new file mode 100644
index 000000000..651ff0c59
--- /dev/null
+++ b/colibri2/tests/solve/smt_array/errors/ite_1.txt
@@ -0,0 +1,15 @@
+(set-logic QF_AX)
+(declare-sort I 0)
+(declare-sort E 0)
+(declare-fun i () I)
+(declare-fun a () (Array I E))
+(declare-fun j () I)
+(declare-fun v1 () E)
+(declare-fun v2 () E)
+(assert (not
+  (ite
+    (= (store a j v1) (store (store a i v2) j v1))
+    (= i j)
+    true
+  )))
+(check-sat)
\ No newline at end of file
diff --git a/colibri2/tests/solve/smt_array/errors/ite_2.txt b/colibri2/tests/solve/smt_array/errors/ite_2.txt
new file mode 100644
index 000000000..b88968a72
--- /dev/null
+++ b/colibri2/tests/solve/smt_array/errors/ite_2.txt
@@ -0,0 +1,13 @@
+(set-logic ALL)
+(declare-fun i () Int)
+(declare-fun a () (Array Int Int))
+(declare-fun j () Int)
+(declare-fun v1 () Int)
+(declare-fun v2 () Int)
+(assert (not
+  (ite
+    (= (store a j v1) (store (store a i v2) j v1))
+    (= i j)
+    true
+  )))
+(check-sat)
\ No newline at end of file
diff --git a/colibri2/tests/solve/smt_array/errors/notes.txt b/colibri2/tests/solve/smt_array/errors/notes.txt
new file mode 100644
index 000000000..16453a420
--- /dev/null
+++ b/colibri2/tests/solve/smt_array/errors/notes.txt
@@ -0,0 +1,4 @@
+
+- eq.txt: timeout
+- ite_1.txt: unsound (--array-res-ext)
+- ite_2.txt: timeout (--array-res-ext)
diff --git a/colibri2/theories/LRA/LRA_build.mli b/colibri2/theories/LRA/LRA_build.mli
index f182f5b1e..c6f5c216d 100644
--- a/colibri2/theories/LRA/LRA_build.mli
+++ b/colibri2/theories/LRA/LRA_build.mli
@@ -1,3 +1,23 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
 module LastEffort : sig
   val add' : _ Egraph.t -> A.t -> Node.t -> A.t -> Node.t -> Node.t
   val add : _ Egraph.t -> Node.t -> Node.t -> Node.t
diff --git a/colibri2/theories/array/DiffGraph.ml b/colibri2/theories/array/DiffGraph.ml
index 1d16ea432..12247a6ba 100644
--- a/colibri2/theories/array/DiffGraph.ml
+++ b/colibri2/theories/array/DiffGraph.ml
@@ -1,6 +1,28 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
 open Colibri2_core
 open Colibri2_popop_lib
 open Popop_stdlib
+open Common
 
 module NP = struct
   module T = struct
@@ -28,4 +50,50 @@ module DG = struct
   let remove (env : Egraph.wt) = HT.remove diff_graph env
 end
 
+let mk_np id node = { id; node }
 let get_neighbours env a = DG.find env a
+
+let rm_edge env { id; node } rnp k =
+  Debug.dprintf6 debug "DiffGraph: Removing the edge %a linking %a to %a"
+    Node.pp k Node.pp node Node.pp rnp.node;
+  DG.change env id ~f:(function
+    | None ->
+        failwith
+          (Fmt.str
+             "%a is supposed to be a neighbour of %a, it's neighbour map \
+              mustn't be empty"
+             Node.pp node Node.pp rnp.node)
+    | Some m -> (
+        match NP.M.find_opt rnp m with
+        | None ->
+            failwith
+              (Fmt.str "There is no edge from %a to the node %a." Node.pp node
+                 Node.pp rnp.node)
+        | Some k' ->
+            assert (Egraph.is_equal env k k');
+            Some (NP.M.remove rnp m)))
+
+let add_neighbour env src dest k ind_ty =
+  Debug.dprintf6 debug "DiffGraph: adding %a as a neighbour to %a through %a"
+    Node.pp dest.node Node.pp src.node Node.pp k;
+  DG.change env src.id ~f:(function
+    | None -> Some (NP.M.singleton dest k)
+    | Some m -> (
+        match NP.M.find_opt dest m with
+        | Some k' ->
+            if Egraph.is_equal env k k' then Some m
+            else
+              (* if a and b are already neighbours and k and k' are not equal
+                 then set (k = k') to true *)
+              let eqn = sem_to_node (mk_eq env k k' ind_ty) in
+              Egraph.register env eqn;
+              Boolean.set_true env eqn;
+              Some m
+        | None -> Some (NP.M.add dest k m)))
+
+let add_edge env a b k =
+  Debug.dprintf6 debug "DiffGraph: adding the edge %a between %a and %a" Node.pp
+    k Node.pp a.node Node.pp b.node;
+  let ind_ty, _ = array_gty_args (get_node_ty env a.node) in
+  add_neighbour env a b k ind_ty;
+  add_neighbour env b a k ind_ty
diff --git a/colibri2/theories/array/DiffGraph.mli b/colibri2/theories/array/DiffGraph.mli
new file mode 100644
index 000000000..928ead7c7
--- /dev/null
+++ b/colibri2/theories/array/DiffGraph.mli
@@ -0,0 +1,49 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
+include sig
+  module T : sig
+    type t = { id : int; node : Node.t }
+  end
+
+  module M : Colibri2_popop_lib.Map_intf.PMap with type key = T.t
+end
+
+type t = T.t = { id : int; node : Node.t }
+
+module DG : sig
+  module HT : sig
+    type 'a t = 'a Datastructure.Hashtbl(Colibri2_popop_lib.Popop_stdlib.DInt).t
+  end
+
+  val diff_graph : Node.t M.t HT.t
+  val find : Egraph.wt -> int -> Node.t M.t option
+
+  val change :
+    f:(Node.t M.t option -> Node.t M.t option) -> 'a Egraph.t -> int -> unit
+
+  val remove : Egraph.wt -> int -> unit
+end
+
+val mk_np : int -> Node.t -> t
+val get_neighbours : Egraph.wt -> int -> Node.t M.t option
+val rm_edge : 'a Egraph.t -> t -> t -> Node.t -> unit
+val add_edge : Egraph.wt -> t -> t -> Node.t -> unit
diff --git a/colibri2/theories/array/GE_Array_DP.ml b/colibri2/theories/array/GE_Array_DP.ml
index a11ff600b..214258a84 100644
--- a/colibri2/theories/array/GE_Array_DP.ml
+++ b/colibri2/theories/array/GE_Array_DP.ml
@@ -1,8 +1,31 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
 open Colibri2_core
 open Colibri2_popop_lib
 open Popop_stdlib
 open Common
 open Colibri2_theories_quantifiers
+module GHT = Datastructure.Hashtbl (Ground)
+module GTHT = Datastructure.Hashtbl (Ground.Ty)
 
 let restrict_ext =
   Options.register ~pp:Fmt.bool "Array.res-ext"
@@ -11,12 +34,12 @@ let restrict_ext =
       & info [ "array-res-ext" ] ~doc:"Restrict the extensionality rule")
 
 let restrict_aup =
-  Options.register ~pp:Fmt.bool "Array.res-ext"
+  Options.register ~pp:Fmt.bool "Array.res-aup"
     Cmdliner.Arg.(
       value & flag & info [ "array-res-aup" ] ~doc:"Restrict the ⇑ rule")
 
 let extended_comb =
-  Options.register ~pp:Fmt.bool "Array.res-ext"
+  Options.register ~pp:Fmt.bool "Array.res-comb"
     Cmdliner.Arg.(
       value & flag & info [ "array-ext-comb" ] ~doc:"Extended combinators")
 
@@ -74,8 +97,8 @@ let distinct_term_node ~subst env ta tb =
 
 (* ⇓: a ≡ b[i <- v], a[j] |> (i = j) \/ a[j] = b[j] *)
 let adown_pattern, adown_run =
-  let a = mk_store_term Terms.tb Terms.ti Terms.tv in
-  let term = mk_select_term a Terms.tj in
+  let a = mk_store_term STV.tb STV.ti STV.tv in
+  let term = mk_select_term a STV.tj in
   let adown_pattern = Pattern.of_term_exn ~subst:Ground.Subst.empty term in
   let adown_run env subst =
     Debug.dprintf2 debug "Found adown with %a" Ground.Subst.pp subst;
@@ -84,10 +107,9 @@ let adown_pattern, adown_run =
       convert ~subst env
         (Expr.Term._or
            [
-             Expr.Term.eq Terms.ti Terms.tj;
-             Expr.Term.eq
-               (mk_select_term a Terms.tj)
-               (mk_select_term Terms.tb Terms.tj);
+             Expr.Term.eq STV.ti STV.tj;
+             Expr.Term.eq (mk_select_term a STV.tj)
+               (mk_select_term STV.tb STV.tj);
            ])
     in
     Egraph.register env n;
@@ -98,13 +120,13 @@ let adown_pattern, adown_run =
 
 (* ⇑: a ≡ b[i <- v], b[j]  |> (i = j) \/ a[j] = b[j] *)
 let aup_pattern, aup_run =
-  let term = mk_store_term Terms.tb Terms.ti Terms.tv in
+  let term = mk_store_term STV.tb STV.ti STV.tv in
   let aup_pattern = Pattern.of_term_exn ~subst:Ground.Subst.empty term in
   let aup_run env subst =
     let n = convert ~subst env term in
     Egraph.register env n;
     Debug.dprintf2 debug "Found aup1 with %a" Ground.Subst.pp subst;
-    let term_bis = mk_select_term Terms.tb Terms.tj in
+    let term_bis = mk_select_term STV.tb STV.tj in
     let aup_pattern_bis = Pattern.of_term_exn ~subst term_bis in
     let aup_run_bis env subst_bis =
       let subst = Ground.Subst.distinct_union subst_bis subst in
@@ -113,10 +135,10 @@ let aup_pattern, aup_run =
         convert ~subst env
           (Expr.Term._or
              [
-               Expr.Term.eq Terms.ti Terms.tj;
+               Expr.Term.eq STV.ti STV.tj;
                Expr.Term.eq
-                 (mk_select_term term Terms.tj)
-                 (mk_select_term Terms.tb Terms.tj);
+                 (mk_select_term term STV.tj)
+                 (mk_select_term STV.tb STV.tj);
              ])
       in
       Egraph.register env v;
@@ -128,28 +150,28 @@ let aup_pattern, aup_run =
 
 (* ⇑ᵣ: a ≡ b[i <- v], b[j], b ∈ non-linear |> (i = j) \/ a[j] = b[j] *)
 let raup_pattern, raup_run =
-  let term = mk_store_term Terms.tb Terms.ti Terms.tv in
+  let term = mk_store_term STV.tb STV.ti STV.tv in
   let raup_pattern = Pattern.of_term_exn ~subst:Ground.Subst.empty term in
   let raup_run env subst =
     Debug.dprintf2 debug "Found raup1 with %a" Ground.Subst.pp subst;
     let n = convert ~subst env term in
     Egraph.register env n;
-    let term_bis = mk_select_term Terms.tb Terms.tj in
+    let term_bis = mk_select_term STV.tb STV.tj in
     let raup_pattern_bis = Pattern.of_term_exn ~subst term_bis in
     let raup_run_bis env subst_bis =
       Debug.dprintf2 debug "Found raup2 with %a" Ground.Subst.pp subst;
       let subst = Ground.Subst.distinct_union subst_bis subst in
-      let bn = convert ~subst env Terms.tb in
+      let bn = convert ~subst env STV.tb in
       match Egraph.get_dom env Linearity_dom.key bn with
       | Some NonLinear ->
           let v =
             convert ~subst env
               (Expr.Term._or
                  [
-                   Expr.Term.eq Terms.ti Terms.tj;
+                   Expr.Term.eq STV.ti STV.tj;
                    Expr.Term.eq
-                     (mk_select_term term Terms.tj)
-                     (mk_select_term Terms.tb Terms.tj);
+                     (mk_select_term term STV.tj)
+                     (mk_select_term STV.tb STV.tj);
                  ])
           in
           Egraph.register env v;
@@ -162,80 +184,135 @@ let raup_pattern, raup_run =
 
 (* K⇓: a = K(v), a[j] |> a[j] = v *)
 let const_read_pattern, const_read_run =
-  let term = mk_select_term (Builtin.apply_array_const Terms.tv) Terms.tj in
+  let term = mk_select_term (Builtin.apply_array_const STV.tv) STV.tj in
   let const_read_pattern = Pattern.of_term_exn ~subst:Ground.Subst.empty term in
   let const_read_run env subst =
     Debug.dprintf2 debug "Found const_read with %a" Ground.Subst.pp subst;
-    let v = convert ~subst env (Expr.Term.eq term Terms.tv) in
+    let v = convert ~subst env (Expr.Term.eq term STV.tv) in
     Egraph.register env v;
     Boolean.set_true env v
   in
   (const_read_pattern, const_read_run)
 
-let apply_res_ext_1 env s =
-  let rec aux terms_acc tsubsts_acc vtnodes =
-    match vtnodes with
-    | (nvar, nterm, nnode) :: t ->
+let apply_res_ext_1_1_aux _env ind_gty val_gty l tyvt =
+  Debug.dprintf2 debug "Application of the res-ext-1-1 rule on %a"
+    (Fmt.list ~sep:Fmt.comma Node.pp)
+    l;
+  let rec aux tnl =
+    match tnl with
+    | (nterm, _) :: t ->
         let nterms =
           List.fold_left
-            (fun acc (_, oterm, _) -> distinct_arrays nterm oterm :: acc)
+            (fun acc (oterm, _) -> distinct_arrays nterm oterm :: acc)
             [] t
         in
-        aux (nterms @ terms_acc) ((nvar, nnode) :: tsubsts_acc) t
-    | [] -> (terms_acc, tsubsts_acc)
+        List.rev_append nterms (aux t)
+    | [] -> []
   in
-  let l = Node.S.elements s in
-  match l with
-  | h :: t -> (
-      (* TODO: fix *)
-      match get_array_gty env h with
-      | { app = { builtin = Expr.Array; _ }; args = [ ind_gty; val_gty ] } ->
-          Debug.dprintf2 debug "Found unequal arrays (%a)" Node.S.pp s;
-          let tyvt = Expr.Ty.array ind_ty val_ty in
-          (* TODO: all the arrays are supposed to have the same type, right? *)
-          let a0v = Expr.Term.Var.mk "a0" tyvt in
-          let a0vt = Expr.Term.of_var a0v in
-          let terms, var_term_node, _ =
-            List.fold_left
-              (fun (nterms, nsubsts, cnt) node ->
-                let anv = Expr.Term.Var.mk (Format.sprintf "a%d" cnt) tyvt in
-                let anvt = Expr.Term.of_var anv in
-                ( distinct_arrays a0vt anvt :: nterms,
-                  (anv, anvt, node) :: nsubsts,
-                  cnt + 1 ))
-              ([], [], 1) t
-          in
-          let terms, tsubsts = aux terms [ (a0v, h) ] var_term_node in
-          let subst =
-            mk_subst tsubsts [ (ind_ty_var, ind_gty); (val_ty_var, val_gty) ]
-          in
-          let n = convert ~subst env (Expr.Term._and terms) in
-          Debug.dprintf2 debug "Application of the res-ext-1 rule on (%a)"
-            Node.S.pp s;
-          Egraph.register env n;
-          Boolean.set_true env n
-      | (exception Not_found) | _ -> ())
-  | [] -> ()
+  let _, tsubst, tnl =
+    List.fold_left
+      (fun (cnt, tsubst, tns) node ->
+        let anv = Expr.Term.Var.mk (Format.sprintf "a%d" cnt) tyvt in
+        let anvt = Expr.Term.of_var anv in
+        (cnt + 1, (anv, node) :: tsubst, (anvt, node) :: tns))
+      (0, [], []) l
+  in
+  let tysubst = [ (STV.ind_ty_var, ind_gty); (STV.val_ty_var, val_gty) ] in
+  let subst = mk_subst tsubst tysubst in
+  (aux tnl, subst)
+
+let apply_res_ext_1_2_aux env ind_gty val_gty l tyvt =
+  Debug.dprintf2 debug "Application of the res-ext-1-2 rule on %a"
+    (Fmt.list ~sep:Fmt.comma Node.pp)
+    l;
+  let rec aux (tnl : (Expr.term * DiffGraph.t) list) =
+    match tnl with
+    | (nterm, idn) :: t -> (
+        match DiffGraph.get_neighbours env idn.id with
+        | Some m ->
+            let nterms =
+              List.fold_left
+                (fun acc (oterm, _) ->
+                  if DiffGraph.M.mem idn m then
+                    distinct_arrays nterm oterm :: acc
+                  else acc)
+                [] t
+            in
+            List.rev_append nterms (aux t)
+        | None -> aux t)
+    | [] -> []
+  in
+  let _, tsubst, tnl =
+    List.fold_left
+      (fun (cnt, tsubst, tns) node ->
+        let anv = Expr.Term.Var.mk (Format.sprintf "a%d" cnt) tyvt in
+        let anvt = Expr.Term.of_var anv in
+        ( cnt + 1,
+          (anv, node) :: tsubst,
+          (anvt, DiffGraph.mk_np (Id_dom.get_id env node) node) :: tns ))
+      (0, [], []) l
+  in
+  let tysubst = [ (STV.ind_ty_var, ind_gty); (STV.val_ty_var, val_gty) ] in
+  let subst = mk_subst tsubst tysubst in
+  (aux tnl, subst)
+
+(* (a = b) ≡ false |> (a[k] ≠ b[k]) *)
+let apply_res_ext_1_1, apply_res_ext_1_2 =
+  let apply_res_ext_1 f env s =
+    let l = Node.S.elements s in
+
+    match get_array_gty_args env (List.hd l) with
+    | ind_gty, val_gty -> (
+        let tyvt = Expr.Ty.array STV.ind_ty STV.val_ty in
+        let l, subst = f env ind_gty val_gty l tyvt in
+        match l with
+        | [] -> ()
+        | [ t ] ->
+            let n = convert ~subst env t in
+            Egraph.register env n;
+            Boolean.set_true env n
+        | l ->
+            let n = convert ~subst env (Expr.Term._and l) in
+            Egraph.register env n;
+            Boolean.set_true env n)
+    | exception Not_An_Array -> ()
+  in
+  let apply_res_ext_1_1 = apply_res_ext_1 apply_res_ext_1_1_aux in
+  let apply_res_ext_1_2 = apply_res_ext_1 apply_res_ext_1_2_aux in
+  (apply_res_ext_1_1, apply_res_ext_1_2)
+
+(** given a new foreign array and a set of all the other foreign arrays,
+    applies res-ext-2 on the new foreign array and all the other foregin arrays
+    which are its neighbours in the DiffGraph *)
+let get_foreign_neighbours env a arr_set =
+  match DiffGraph.get_neighbours env (Id_dom.get_id env a) with
+  | Some m ->
+      Node.S.filter
+        (fun node -> DiffGraph.M.mem { id = Id_dom.get_id env node; node } m)
+        arr_set
+  | None -> Node.M.empty
 
-let apply_res_ext_2 env =
+(* a, b, {a,b} ⊆ foreign |> (a = b) ⋁ (a[k] ≠ b[k]) *)
+let apply_res_ext_2_1, apply_res_ext_2_2 =
+  let foreign_array_db = GTHT.create Node.S.pp "foreign_array_db" in
   let apply_ext env a b =
     let gtya, gtyb = array_gty_args (get_array_gty env a) in
     let n =
       convert
         ~subst:
           (mk_subst
-             [ (Terms.va, a); (Terms.vb, b) ]
-             [ (ind_ty_var, gtya); (val_ty_var, gtyb) ])
+             [ (STV.va, a); (STV.vb, b) ]
+             [ (STV.ind_ty_var, gtya); (STV.val_ty_var, gtyb) ])
         env
         (Expr.Term._or
            [
-             Expr.Term.eq Terms.ta Terms.tb;
+             Expr.Term.eq STV.ta STV.tb;
              Expr.Term.distinct
                [
-                 Expr.Term.Array.select Terms.ta
-                   (Builtin.apply_array_diff Terms.ta Terms.tb);
-                 Expr.Term.Array.select Terms.tb
-                   (Builtin.apply_array_diff Terms.ta Terms.tb);
+                 Expr.Term.Array.select STV.ta
+                   (Builtin.apply_array_diff STV.ta STV.tb);
+                 Expr.Term.Array.select STV.tb
+                   (Builtin.apply_array_diff STV.ta STV.tb);
                ];
            ])
     in
@@ -244,35 +321,56 @@ let apply_res_ext_2 env =
     Egraph.register env n;
     Boolean.set_true env n
   in
-  let foreign_array_db = GTHT.create Node.S.pp "foreign_array_db" in
-  fun (aty : Ground.Ty.t) a ->
-    Debug.dprintf2 debug "Application of the res-ext-2 rule on %a" Node.pp a;
+  let apply_res_ext_2 f env aty a =
     match aty with
-    | { app = { builtin = Expr.Array; _ }; _ } ->
+    | Ground.Ty.{ app = { builtin = Expr.Array; _ }; _ } ->
         GTHT.change
           (fun opt ->
             match opt with
-            | Some arr_set ->
+            | Some fa_set ->
                 Debug.dprintf2 debug
                   "Found new foreign array (%a) on which to apply \
                    new_foreign_array the hook"
                   Node.pp a;
-                Node.S.iter (apply_ext env a) arr_set;
-                Some (Node.S.add a arr_set)
+                f env a fa_set;
+                Some (Node.S.add a fa_set)
             | None -> Some (Node.S.singleton a))
           foreign_array_db env aty
     | _ -> ()
+  in
+  let apply_res_ext_2_1 env aty a =
+    Debug.dprintf2 debug "Application of the res-ext-2-1 rule on %a" Node.pp a;
+    apply_res_ext_2
+      (fun env a fa_set -> Node.S.iter (apply_ext env a) fa_set)
+      env aty a
+  in
+  let apply_res_ext_2_2 env aty a =
+    Debug.dprintf2 debug "Application of the res-ext-2-2 rule on %a" Node.pp a;
+    apply_res_ext_2
+      (fun env a fa_set ->
+        Node.S.iter (apply_ext env a) (get_foreign_neighbours env a fa_set))
+      env aty a
+  in
+  (apply_res_ext_2_1, apply_res_ext_2_2)
 
+(** applied the res-ext rule only on neighbours in the diff-graphs  *)
 let init env =
   (* extáµ£ (restricted extensionality):
-     - (a = b) ≡ false |> (a = b) ⋁ (a[k] ≠ b[k])
+     - (a = b) ≡ false |> (a[k] ≠ b[k])
      - a, b, {a,b} ⊆ foreign |> (a = b) ⋁ (a[k] ≠ b[k]) *)
-  if Options.get env restrict_ext then (
-    (* (a = b) ≡ false |> (a = b) ⋁ (a[k] ≠ b[k]) *)
-    (* Are a and b always of the same type? *)
-    Equality.register_hook_new_disequality env (apply_res_ext_1 env);
-    (* a, b, {a,b} ⊆ foreign |> (a = b) ⋁ (a[k] ≠ b[k]) *)
-    Foreign_dom.register_hook_new_foreign_array env (apply_res_ext_2 env));
+  if Options.get env restrict_ext then
+    if Options.get env use_diff_graph then (
+      (* (a = b) ≡ false |> (a[k] ≠ b[k]) (when a and b are neighbours) *)
+      Equality.register_hook_new_disequality env (apply_res_ext_1_2 env);
+      (* a, b, {a,b} ⊆ foreign |> (a = b) ⋁ (a[k] ≠ b[k])
+         (when a and b are neighbours) *)
+      Foreign_dom.register_hook_new_foreign_array env (apply_res_ext_2_2 env))
+    else (
+      (* (a = b) ≡ false |> (a[k] ≠ b[k]) *)
+      (* TODO: Are a and b always of the same type? *)
+      Equality.register_hook_new_disequality env (apply_res_ext_1_1 env);
+      (* a, b, {a,b} ⊆ foreign |> (a = b) ⋁ (a[k] ≠ b[k]) *)
+      Foreign_dom.register_hook_new_foreign_array env (apply_res_ext_2_1 env));
   let l = [ (adown_pattern, adown_run) ] in
   let l =
     if Options.get env restrict_aup then (raup_pattern, raup_run) :: l
@@ -293,11 +391,11 @@ let new_array =
       Datastructure.Push.iter db env ~f:(fun f2 ->
           let subst =
             mk_subst
-              [ (Terms.va, Ground.node f2); (Terms.vb, Ground.node f) ]
-              [ (ind_ty_var, ind_gty); (val_ty_var, val_gty) ]
+              [ (STV.va, Ground.node f2); (STV.vb, Ground.node f) ]
+              [ (STV.ind_ty_var, ind_gty); (STV.val_ty_var, val_gty) ]
           in
           Debug.dprintf2 debug "Found ext with %a" Ground.Subst.pp subst;
-          let n = distinct_term_node ~subst env Terms.ta Terms.tb in
+          let n = distinct_term_node ~subst env STV.ta STV.tb in
           Egraph.register env n;
           Boolean.set_true env n);
       Datastructure.Push.push db env f);
@@ -306,13 +404,13 @@ let new_array =
       Debug.dprintf0 debug "Application of the epsilon_delta rule";
       let subst =
         mk_subst
-          [ (Terms.va, Ground.node f) ]
-          [ (ind_ty_var, ind_gty); (val_ty_var, val_gty) ]
+          [ (STV.va, Ground.node f) ]
+          [ (STV.ind_ty_var, ind_gty); (STV.val_ty_var, val_gty) ]
       in
       let epsilon_delta_eq =
         Expr.Term.eq
-          (mk_select_term Terms.ta (Builtin.apply_array_def_index Terms.ta))
-          (Builtin.apply_array_def_value Terms.ta)
+          (mk_select_term STV.ta (Builtin.apply_array_def_index STV.ta))
+          (Builtin.apply_array_def_value STV.ta)
       in
       let n = convert ~subst env epsilon_delta_eq in
       Egraph.register env n;
@@ -322,16 +420,16 @@ let new_array =
 let map_adowm map_term f_term bitl =
   let map_read_pattern =
     Pattern.of_term_exn ~subst:Ground.Subst.empty
-      (mk_select_term map_term Terms.tj)
+      (mk_select_term map_term STV.tj)
   in
   let map_read_run env subst =
     Debug.dprintf2 debug "Found array_map(f,b1, ..., bn)[j] with %a"
       Ground.Subst.pp subst;
     let term =
       Expr.Term.eq
-        (mk_select_term map_term Terms.tj)
+        (mk_select_term map_term STV.tj)
         (Expr.Term.apply f_term []
-           (List.map (fun bi -> mk_select_term bi Terms.tj) bitl))
+           (List.map (fun bi -> mk_select_term bi STV.tj) bitl))
     in
     let n = convert ~subst env term in
     Egraph.register env n;
@@ -346,23 +444,26 @@ let apply_map_aup env index_n gt
   let arg_nodes = List.tl argl in
   let a_node = Ground.node gt in
   let fvar =
-    Expr.Term.Var.mk "f" (Expr.Ty.arrow (replicate f_arity alpha_ty) val_ty)
+    Expr.Term.Var.mk "f"
+      (Expr.Ty.arrow (replicate f_arity STV.alpha_ty) STV.val_ty)
   in
   let fterm = Expr.Term.of_var fvar in
   let ty_subst =
     [
-      (ind_ty_var, bi_ind_ty); (alpha_ty_var, bi_val_ty); (val_ty_var, a_val_ty);
+      (STV.ind_ty_var, bi_ind_ty);
+      (STV.alpha_ty_var, bi_val_ty);
+      (STV.val_ty_var, a_val_ty);
     ]
   in
-  let t_subst = [ (Terms.vj, index_n); (fvar, f_node); (Terms.va, a_node) ] in
+  let t_subst = [ (STV.vj, index_n); (fvar, f_node); (STV.va, a_node) ] in
   let _, bij_list, t_subst =
     List.fold_left
       (fun (n, t_acc, s_acc) node ->
-        let biv = Expr.Term.Var.mk (Format.sprintf "b%n" n) array_ty_alpha in
+        let biv =
+          Expr.Term.Var.mk (Format.sprintf "b%n" n) STV.array_ty_alpha
+        in
         let bit = Expr.Term.of_var biv in
-        ( n - 1,
-          Expr.Term.Array.select bit Terms.tj :: t_acc,
-          (biv, node) :: s_acc ))
+        (n - 1, Expr.Term.Array.select bit STV.tj :: t_acc, (biv, node) :: s_acc))
       (f_arity, [], t_subst) (List.rev arg_nodes)
   in
   let n =
@@ -370,7 +471,7 @@ let apply_map_aup env index_n gt
       ~subst:(mk_subst t_subst ty_subst)
       env
       (Expr.Term.eq
-         (Expr.Term.Array.select Terms.ta Terms.tj)
+         (Expr.Term.Array.select STV.ta STV.tj)
          (Expr.Term.apply fterm [] bij_list))
   in
   Egraph.register env n;
@@ -433,7 +534,7 @@ let map_def map_term f_term bitl =
     let d_bil = List.map (fun bi -> Builtin.apply_array_def_value bi) bitl in
     let term =
       Expr.Term.eq
-        (Builtin.apply_array_def_value Terms.ta)
+        (Builtin.apply_array_def_value STV.ta)
         (Expr.Term.apply f_term [] d_bil)
     in
     let n = convert ~subst env term in
@@ -456,8 +557,8 @@ let new_map =
   in
   let new_map_db =
     NM.create Fmt.nop "new_map_db" (fun env f_arity ->
-        let b_ty = Expr.Ty.array ind_ty alpha_ty in
-        let f_ty = Expr.Ty.arrow (replicate f_arity alpha_ty) val_ty in
+        let b_ty = Expr.Ty.array STV.ind_ty STV.alpha_ty in
+        let f_ty = Expr.Ty.arrow (replicate f_arity STV.alpha_ty) STV.val_ty in
         let bitl = mk_tlist [] f_arity b_ty in
         let f_var = Expr.Term.Var.mk "f" f_ty in
         let f_term = Expr.Term.of_var f_var in
@@ -489,7 +590,9 @@ let get_gty_inhabitants (gty : Ground.Ty.t) =
 let apply_blast_rule =
   let mk_delta_fv ind_gty n =
     Expr.Term.of_var
-      (Expr.Term.Var.mk (Fmt.str "delta_%a_%d" Ground.Ty.pp ind_gty n) val_ty)
+      (Expr.Term.Var.mk
+         (Fmt.str "delta_%a_%d" Ground.Ty.pp ind_gty n)
+         STV.val_ty)
   in
   let mk_conjonction_node env array_node val_gty sigmas =
     let vals, _ =
@@ -497,14 +600,14 @@ let apply_blast_rule =
         (fun (acc, n) sigma ->
           let term =
             Expr.Term.eq
-              (Expr.Term.Array.select Terms.ta sigma)
+              (Expr.Term.Array.select STV.ta sigma)
               (mk_delta_fv val_gty n)
           in
           (term :: acc, n + 1))
         ([], 1) sigmas
     in
     convert
-      ~subst:(mk_subst [ (Terms.va, array_node) ] [ (val_ty_var, val_gty) ])
+      ~subst:(mk_subst [ (STV.va, array_node) ] [ (STV.val_ty_var, val_gty) ])
       env (Expr.Term._and vals)
   in
   fun env array_node (ind_gty : Ground.Ty.t) (val_gty : Ground.Ty.t) ->
@@ -575,10 +678,10 @@ let converter env (f : Ground.t) =
         (* 𝝐≠: v = a[i], i is not 𝝐 |> i ≠ 𝝐 or blast *)
         let subst =
           mk_subst
-            [ (Terms.va, a); (Terms.vi, i) ]
-            [ (ind_ty_var, ind_gty); (val_ty_var, val_gty) ]
+            [ (STV.va, a); (STV.vi, i) ]
+            [ (STV.ind_ty_var, ind_gty); (STV.val_ty_var, val_gty) ]
         in
-        let eps_term = Builtin.apply_array_def_index Terms.ta in
+        let eps_term = Builtin.apply_array_def_index STV.ta in
         let eps_node = convert ~subst env eps_term in
         Egraph.register env eps_node;
         if not (Egraph.is_equal env i eps_node) then (
@@ -589,7 +692,7 @@ let converter env (f : Ground.t) =
           else
             (* application of 𝝐≠ *)
             let i_eps_neq_node =
-              convert ~subst env (Expr.Term.neq eps_term Terms.ti)
+              convert ~subst env (Expr.Term.neq eps_term STV.ti)
             in
             Egraph.register env i_eps_neq_node;
             Boolean.set_true env i_eps_neq_node))
@@ -611,13 +714,13 @@ let converter env (f : Ground.t) =
       (* application of the `idx` rule *)
       let subst =
         mk_subst
-          [ (Terms.va, a); (Terms.vk, k); (Terms.vv, v) ]
-          [ (ind_ty_var, ind_gty); (val_ty_var, val_gty) ]
+          [ (STV.va, a); (STV.vk, k); (STV.vv, v) ]
+          [ (STV.ind_ty_var, ind_gty); (STV.val_ty_var, val_gty) ]
       in
-      let store_term = mk_store_term Terms.ta Terms.tk Terms.tv in
+      let store_term = mk_store_term STV.ta STV.tk STV.tv in
       let rn =
         convert ~subst env
-          (Expr.Term.eq (mk_select_term store_term Terms.tk) Terms.tv)
+          (Expr.Term.eq (mk_select_term store_term STV.tk) STV.tv)
       in
       Egraph.register env rn;
       Boolean.set_true env rn;
@@ -626,7 +729,7 @@ let converter env (f : Ground.t) =
         let eq_term =
           Expr.Term.eq
             (Builtin.apply_array_def_value store_term)
-            (Builtin.apply_array_def_value Terms.ta)
+            (Builtin.apply_array_def_value STV.ta)
         in
         let eq_node = convert ~subst env eq_term in
         Egraph.register env eq_node;
@@ -655,14 +758,14 @@ let converter env (f : Ground.t) =
       Egraph.register env v;
       (* application of the `K𝛿` rule *)
       if Options.get env default_values then (
-        let subst = mk_subst [ (Terms.vv, v) ] [ (val_ty_var, val_gty) ] in
+        let subst = mk_subst [ (STV.vv, v) ] [ (STV.val_ty_var, val_gty) ] in
         (* TODO: make a separate array node and set it's type? *)
         let eq_node =
           convert ~subst env
             (Expr.Term.eq
                (Builtin.apply_array_def_value
-                  (Builtin.apply_array_const Terms.tv))
-               Terms.tv)
+                  (Builtin.apply_array_const STV.tv))
+               STV.tv)
         in
         Egraph.register env eq_node;
         Boolean.set_true env eq_node)
diff --git a/colibri2/theories/array/GE_Array_DP.mli b/colibri2/theories/array/GE_Array_DP.mli
new file mode 100644
index 000000000..325a7d158
--- /dev/null
+++ b/colibri2/theories/array/GE_Array_DP.mli
@@ -0,0 +1,23 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
+val init : Egraph.wt -> unit
+val converter : Egraph.wt -> Ground.t -> unit
diff --git a/colibri2/theories/array/SharingIsCaring.ml b/colibri2/theories/array/SharingIsCaring.ml
index 5445f58d9..0fe087506 100644
--- a/colibri2/theories/array/SharingIsCaring.ml
+++ b/colibri2/theories/array/SharingIsCaring.ml
@@ -1,61 +1,30 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
 open Colibri2_core
 open Colibri2_popop_lib
 open Common
 open DiffGraph
 
-let use_diff_graph =
-  Options.register ~pp:Fmt.bool "Array.diff-graph"
-    Cmdliner.Arg.(
-      value & flag
-      & info [ "array-diff-graph" ] ~doc:"Use the array's diff graph")
-
-let rm_edge env { id; node } rnp k =
-  Debug.dprintf6 debug "DiffGraph: Removing the edge %a linking %a to %a"
-    Node.pp k Node.pp node Node.pp rnp.node;
-  DG.change env id ~f:(function
-    | None ->
-        failwith
-          (Fmt.str
-             "%a is supposed to be a neighbour of %a, it's neighbour map \
-              mustn't be empty"
-             Node.pp node Node.pp rnp.node)
-    | Some m -> (
-        match NP.M.find_opt rnp m with
-        | None ->
-            failwith
-              (Fmt.str "There is no edge from %a to the node %a." Node.pp node
-                 Node.pp rnp.node)
-        | Some k' ->
-            assert (Egraph.is_equal env k k');
-            Some (NP.M.remove rnp m)))
-
-let add_neighbour env src dest k ind_ty =
-  Debug.dprintf6 debug "DiffGraph: adding %a as a neighbour to %a through %a"
-    Node.pp dest.node Node.pp src.node Node.pp k;
-  DG.change env src.id ~f:(function
-    | None -> Some (NP.M.singleton dest k)
-    | Some m -> (
-        match NP.M.find_opt dest m with
-        | Some k' ->
-            if Egraph.is_equal env k k' then Some m
-            else
-              (* if a and b are already neighbours and k and k' are not equal
-                 then set (k = k') to true *)
-              let eqn = sem_to_node (mk_eq env k k' ind_ty) in
-              Egraph.register env eqn;
-              Boolean.set_true env eqn;
-              Some m
-        | None -> Some (NP.M.add dest k m)))
-
-(** if (a = b[k <- _]) then the edge a--k--b is added to the graph.  *)
-let add_edge env a b k =
-  Debug.dprintf6 debug "DiffGraph: adding the edge %a between %a and %a" Node.pp
-    k Node.pp a.node Node.pp b.node;
-  let ind_ty, _ = array_gty_args (get_node_ty env a.node) in
-  add_neighbour env a b k ind_ty;
-  add_neighbour env b a k ind_ty
-
-let neighbours_union env knp rnp { id; node } kk_opt kr_opt acc =
+let merge_node_neighbours env knp rnp { id; node } kk_opt kr_opt acc =
   match (kk_opt, kr_opt) with
   | Some k1, Some k2 ->
       (* if a node X is a neighbour of both A and B through the edges
@@ -67,12 +36,12 @@ let neighbours_union env knp rnp { id; node } kk_opt kr_opt acc =
         Egraph.register env eqn;
         Boolean.set_true env eqn);
       rm_edge env { id; node } rnp k2;
-      NP.M.add knp k1 acc
-  | Some k, None -> NP.M.add knp k acc
+      M.add knp k1 acc
+  | Some k, None -> M.add knp k acc
   | None, Some k ->
       rm_edge env { id; node } rnp k;
       add_edge env { id; node } knp k;
-      NP.M.add knp k acc
+      M.add knp k acc
   | None, None ->
       failwith
         (Fmt.str
@@ -97,7 +66,7 @@ let eq_arrays_norm env (a : t) (b : t) =
   in
   (* iterate over all the neighbours *)
   let abns =
-    NP.M.fold2_union (neighbours_union env knp rnp) knm rnm NP.M.empty
+    M.fold2_union (merge_node_neighbours env knp rnp) knm rnm M.empty
   in
   DG.remove env rnp.id;
   DG.change env knp.id ~f:(function
diff --git a/colibri2/theories/array/SharingIsCaring.mli b/colibri2/theories/array/SharingIsCaring.mli
new file mode 100644
index 000000000..325a7d158
--- /dev/null
+++ b/colibri2/theories/array/SharingIsCaring.mli
@@ -0,0 +1,23 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
+val init : Egraph.wt -> unit
+val converter : Egraph.wt -> Ground.t -> unit
diff --git a/colibri2/theories/array/common.ml b/colibri2/theories/array/common.ml
index 29af59c27..2728f76cc 100644
--- a/colibri2/theories/array/common.ml
+++ b/colibri2/theories/array/common.ml
@@ -1,11 +1,33 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
 open Colibri2_core
 open Colibri2_popop_lib
 open Popop_stdlib
-module NHT = Datastructure.Hashtbl (Node)
-module IHT = Datastructure.Hashtbl (DInt)
-module GHT = Datastructure.Hashtbl (Ground)
-module GTHT = Datastructure.Hashtbl (Ground.Ty)
 
+let use_diff_graph =
+  Options.register ~pp:Fmt.bool "Array.diff-graph"
+    Cmdliner.Arg.(
+      value & flag
+      & info [ "array-diff-graph" ] ~doc:"Use the array's diff graph")
 
 let debug =
   Debug.register_info_flag ~desc:"Debugging messages of the array theory"
@@ -15,22 +37,41 @@ let convert ~subst =
   Colibri2_theories_quantifiers.Subst.convert ~subst_old:Ground.Subst.empty
     ~subst_new:subst
 
-let ind_ty_var = Expr.Ty.Var.mk "ind_ty"
-let val_ty_var = Expr.Ty.Var.mk "val_ty"
-let alpha_ty_var = Expr.Ty.Var.mk "alpha"
-let a_ty_var = Expr.Ty.Var.mk "a"
-let b_ty_var = Expr.Ty.Var.mk "b"
-let c_ty_var = Expr.Ty.Var.mk "c"
-let ind_ty = Expr.Ty.of_var ind_ty_var
-let val_ty = Expr.Ty.of_var val_ty_var
-let a_ty = Expr.Ty.of_var a_ty_var
-let b_ty = Expr.Ty.of_var b_ty_var
-let c_ty = Expr.Ty.of_var c_ty_var
-let alpha_ty = Expr.Ty.of_var alpha_ty_var
-let array_ty = Expr.Ty.array ind_ty val_ty
-let array_ty_ab = Expr.Ty.array a_ty b_ty
-let array_ty_ac = Expr.Ty.array a_ty c_ty
-let array_ty_alpha = Expr.Ty.array ind_ty alpha_ty
+module STV = struct
+  let ind_ty_var = Expr.Ty.Var.mk "ind_ty"
+  let val_ty_var = Expr.Ty.Var.mk "val_ty"
+  let alpha_ty_var = Expr.Ty.Var.mk "alpha"
+  let a_ty_var = Expr.Ty.Var.mk "a"
+  let b_ty_var = Expr.Ty.Var.mk "b"
+  let c_ty_var = Expr.Ty.Var.mk "c"
+  let ind_ty = Expr.Ty.of_var ind_ty_var
+  let val_ty = Expr.Ty.of_var val_ty_var
+  let a_ty = Expr.Ty.of_var a_ty_var
+  let b_ty = Expr.Ty.of_var b_ty_var
+  let c_ty = Expr.Ty.of_var c_ty_var
+  let alpha_ty = Expr.Ty.of_var alpha_ty_var
+  let array_ty = Expr.Ty.array ind_ty val_ty
+  let array_ty_ab = Expr.Ty.array a_ty b_ty
+  let array_ty_ac = Expr.Ty.array a_ty c_ty
+  let array_ty_alpha = Expr.Ty.array ind_ty alpha_ty
+  let term_of_var = Expr.Term.of_var
+  let mk_index_var name = Expr.Term.Var.mk name ind_ty
+  let mk_value_var name = Expr.Term.Var.mk name val_ty
+  let mk_array_var name = Expr.Term.Var.mk name array_ty
+  let vi = mk_index_var "i"
+  let vj = mk_index_var "j"
+  let vk = mk_index_var "k"
+  let vv = mk_value_var "v"
+  let ti = term_of_var vi
+  let tj = term_of_var vj
+  let tk = term_of_var vk
+  let tv = term_of_var vv
+  let va = mk_array_var "a"
+  let vb = mk_array_var "b"
+  let ta = term_of_var va
+  let tb = term_of_var vb
+end
+
 let replicate n v = List.init n (fun _ -> v)
 let mk_store_term = Expr.Term.Array.store
 let mk_select_term = Expr.Term.Array.select
@@ -46,19 +87,22 @@ let array_gty_args : Ground.Ty.t -> Ground.Ty.t * Ground.Ty.t = function
       (ind_gty, val_gty)
   | ty -> failwith (Fmt.str "'%a' is not an array ground type!" Ground.Ty.pp ty)
 
-let node_tyl env n = Ground.Ty.S.elements (Ground.tys env n)
-
 let get_node_ty env n =
   (* TODO: fix somehow *)
   match Ground.Ty.S.elements (Ground.tys env n) with
   | h :: _ -> h
   | [] -> failwith (Fmt.str "The type of the node %a was not set" Node.pp n)
 
+exception Not_An_Array
+
 let get_array_gty env n =
-  List.find
-    (function
-      | Ground.Ty.{ app = { builtin = Expr.Array; _ }; _ } -> true | _ -> false)
-    (Ground.Ty.S.elements (Ground.tys env n))
+  try
+    List.find
+      (function
+        | Ground.Ty.{ app = { builtin = Expr.Array; _ }; _ } -> true
+        | _ -> false)
+      (Ground.Ty.S.elements (Ground.tys env n))
+  with Not_found -> raise Not_An_Array
 
 let get_array_gty_args env n = array_gty_args (get_array_gty env n)
 
@@ -81,14 +125,15 @@ module Builtin = struct
   let array_diff : Dolmen_std.Expr.term_cst =
     Expr.Id.mk ~name:"colibri2_array_diff" ~builtin:Array_diff
       (Dolmen_std.Path.global "colibri2_array_diff")
-      (Expr.Ty.pi [ a_ty_var; b_ty_var ]
-         (Expr.Ty.arrow [ array_ty_ab; array_ty_ab ] a_ty))
+      (Expr.Ty.pi
+         [ STV.a_ty_var; STV.b_ty_var ]
+         (Expr.Ty.arrow [ STV.array_ty_ab; STV.array_ty_ab ] STV.a_ty))
 
   let array_const : Dolmen_std.Expr.term_cst =
     Expr.Id.mk ~name:"colibri2_array_const" ~builtin:Array_const
       (Dolmen_std.Path.global "colibri2_array_const")
-      (Expr.Ty.pi [ b_ty_var ]
-         (Expr.Ty.arrow [ b_ty ] (Expr.Ty.array ind_ty b_ty)))
+      (Expr.Ty.pi [ STV.b_ty_var ]
+         (Expr.Ty.arrow [ STV.b_ty ] (Expr.Ty.array STV.ind_ty STV.b_ty)))
 
   let array_map : int -> Dolmen_std.Expr.term_cst =
     let cache = DInt.H.create 13 in
@@ -98,8 +143,9 @@ module Builtin = struct
       | exception Not_found ->
           let ty =
             Expr.Ty.arrow
-              (Expr.Ty.arrow (replicate i b_ty) c_ty :: replicate i array_ty_ab)
-              array_ty_ac
+              (Expr.Ty.arrow (replicate i STV.b_ty) STV.c_ty
+              :: replicate i STV.array_ty_ab)
+              STV.array_ty_ac
           in
           DInt.H.add cache i ty;
           ty
@@ -107,17 +153,23 @@ module Builtin = struct
     fun i ->
       Expr.Id.mk ~name:"colibri2_array_map" ~builtin:Array_map
         (Dolmen_std.Path.global "colibri2_array_map")
-        (Expr.Ty.pi [ a_ty_var; b_ty_var; c_ty_var ] (get_ty i))
+        (Expr.Ty.pi
+           [ STV.a_ty_var; STV.b_ty_var; STV.c_ty_var ]
+           (get_ty i))
 
   let array_default_index : Dolmen_std.Expr.term_cst =
     Expr.Id.mk ~name:"colibri2_array_default_index" ~builtin:Array_default_index
       (Dolmen_std.Path.global "colibri2_array_default_index")
-      (Expr.Ty.pi [ a_ty_var; b_ty_var ] (Expr.Ty.arrow [ array_ty_ab ] a_ty))
+      (Expr.Ty.pi
+         [ STV.a_ty_var; STV.b_ty_var ]
+         (Expr.Ty.arrow [ STV.array_ty_ab ] STV.a_ty))
 
   let array_default_value : Dolmen_std.Expr.term_cst =
     Expr.Id.mk ~name:"colibri2_array_default_value" ~builtin:Array_default_value
       (Dolmen_std.Path.global "colibri2_array_default_value")
-      (Expr.Ty.pi [ a_ty_var; b_ty_var ] (Expr.Ty.arrow [ array_ty_ab ] b_ty))
+      (Expr.Ty.pi
+         [ STV.a_ty_var; STV.b_ty_var ]
+         (Expr.Ty.arrow [ STV.array_ty_ab ] STV.b_ty))
 
   let apply_array_diff a b =
     let ind_ty, val_ty = array_ty_args a.Expr.term_ty in
@@ -205,22 +257,3 @@ let distinct_arrays a b =
   Expr.Term.distinct [ mk_select_term a diff; mk_select_term b diff ]
 
 let sem_to_node s = Ground.node (Ground.index s)
-
-module Terms = struct
-  let term_of_var = Expr.Term.of_var
-  let mk_index_var name = Expr.Term.Var.mk name ind_ty
-  let mk_value_var name = Expr.Term.Var.mk name val_ty
-  let mk_array_var name = Expr.Term.Var.mk name array_ty
-  let vi = mk_index_var "i"
-  let vj = mk_index_var "j"
-  let vk = mk_index_var "k"
-  let vv = mk_value_var "v"
-  let ti = term_of_var vi
-  let tj = term_of_var vj
-  let tk = term_of_var vk
-  let tv = term_of_var vv
-  let va = mk_array_var "a"
-  let vb = mk_array_var "b"
-  let ta = term_of_var va
-  let tb = term_of_var vb
-end
diff --git a/colibri2/theories/array/common.mli b/colibri2/theories/array/common.mli
new file mode 100644
index 000000000..b1aaf85e7
--- /dev/null
+++ b/colibri2/theories/array/common.mli
@@ -0,0 +1,104 @@
+(*************************************************************************)
+(*  This file is part of Colibri2.                                       *)
+(*                                                                       *)
+(*  Copyright (C) 2014-2021                                              *)
+(*    CEA   (Commissariat à l'énergie atomique et aux énergies           *)
+(*           alternatives)                                               *)
+(*    OCamlPro                                                           *)
+(*                                                                       *)
+(*  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 licenses/LGPLv2.1).           *)
+(*************************************************************************)
+
+val use_diff_graph : bool Options.t
+val debug : Debug.flag
+val convert : subst:Ground.Subst.t -> 'a Egraph.t -> Expr.term -> Node.t
+
+module STV : sig
+  val ind_ty_var : Dolmen_std.Expr.ty_var
+  val val_ty_var : Dolmen_std.Expr.ty_var
+  val alpha_ty_var : Dolmen_std.Expr.ty_var
+  val a_ty_var : Dolmen_std.Expr.ty_var
+  val b_ty_var : Dolmen_std.Expr.ty_var
+  val c_ty_var : Dolmen_std.Expr.ty_var
+  val ind_ty : Expr.ty
+  val val_ty : Expr.ty
+  val a_ty : Expr.ty
+  val b_ty : Expr.ty
+  val c_ty : Expr.ty
+  val alpha_ty : Expr.ty
+  val array_ty : Expr.ty
+  val array_ty_ab : Expr.ty
+  val array_ty_ac : Expr.ty
+  val array_ty_alpha : Expr.ty
+  val term_of_var : Dolmen_std.Expr.term_var -> Expr.term
+  val mk_index_var : string -> Dolmen_std.Expr.term_var
+  val mk_value_var : string -> Dolmen_std.Expr.term_var
+  val mk_array_var : string -> Dolmen_std.Expr.term_var
+  val vi : Dolmen_std.Expr.term_var
+  val vj : Dolmen_std.Expr.term_var
+  val vk : Dolmen_std.Expr.term_var
+  val vv : Dolmen_std.Expr.term_var
+  val ti : Expr.term
+  val tj : Expr.term
+  val tk : Expr.term
+  val tv : Expr.term
+  val va : Dolmen_std.Expr.term_var
+  val vb : Dolmen_std.Expr.term_var
+  val ta : Expr.term
+  val tb : Expr.term
+end
+
+val replicate : int -> 'a -> 'a list
+val mk_store_term : Expr.term -> Expr.term -> Expr.term -> Expr.term
+val mk_select_term : Expr.term -> Expr.term -> Expr.term
+
+val apply_cst :
+  Dolmen_std.Expr.term_cst -> Expr.ty list -> Expr.term list -> Expr.term
+
+val array_gty_args : Ground.Ty.t -> Ground.Ty.t * Ground.Ty.t
+val get_node_ty : 'a Egraph.t -> Node.t -> Ground.Ty.t
+
+exception Not_An_Array
+
+val get_array_gty : 'a Egraph.t -> Node.t -> Ground.Ty.t
+val get_array_gty_args : 'a Egraph.t -> Node.t -> Ground.Ty.t * Ground.Ty.t
+val add_array_gty : Egraph.wt -> Node.t -> Ground.Ty.t -> Ground.Ty.t -> unit
+
+module Builtin : sig
+  type 'a Builtin.t +=
+    | Array_diff
+    | Array_const
+    | Array_map
+    | Array_default_index
+    | Array_default_value
+
+  val array_diff : Dolmen_std.Expr.term_cst
+  val array_const : Dolmen_std.Expr.term_cst
+  val array_map : int -> Dolmen_std.Expr.term_cst
+  val array_default_index : Dolmen_std.Expr.term_cst
+  val array_default_value : Dolmen_std.Expr.term_cst
+  val apply_array_diff : Expr.term -> Expr.term -> Expr.term
+  val apply_array_const : Expr.term -> Expr.term
+  val apply_array_def_index : Expr.term -> Expr.term
+  val apply_array_def_value : Expr.term -> Expr.term
+  val apply_array_map : int -> Expr.term -> Expr.term list -> Expr.term
+end
+
+val mk_subst :
+  (Dolmen_std.Expr.term_var * Node.t) list ->
+  (Dolmen_std.Expr.ty_var * Ground.Ty.t) list ->
+  Ground.Subst.t
+
+val mk_eq : 'a Egraph.t -> Node.t -> Node.t -> Ground.Ty.t -> Ground.s
+val distinct_arrays : Expr.term -> Expr.term -> Expr.term
+val sem_to_node : Ground.s -> Node.t
diff --git a/colibri2/theories/nseq/nseq.ml b/colibri2/theories/nseq/nseq.ml
index 5c5c2f3d1..71dda86de 100644
--- a/colibri2/theories/nseq/nseq.ml
+++ b/colibri2/theories/nseq/nseq.ml
@@ -152,7 +152,7 @@ module Builtin = struct
     in
     Expr.add_builtins (fun env s ->
         match s with
-        | Dolmen_loop.Typer.T.Id { ns = Sort; name = Simple "seq" } ->
+        | Dolmen_loop.Typer.T.Id { ns = Sort; name = Simple "NSeq" } ->
             ty_app1 env s nseq_ty_const
         | Dolmen_loop.Typer.T.Id { ns = Term; name = Simple "nseq_first" } ->
             app1 env s nseq_first
diff --git a/colibri2/theories/nseq/nseq.mli b/colibri2/theories/nseq/nseq.mli
index 4bf15b085..b04ecb6fc 100644
--- a/colibri2/theories/nseq/nseq.mli
+++ b/colibri2/theories/nseq/nseq.mli
@@ -18,3 +18,4 @@
 (*  See the GNU Lesser General Public License version 2.1                *)
 (*  for more details (enclosed in the file licenses/LGPLv2.1).           *)
 (*************************************************************************)
+
-- 
GitLab