From 9cf9b1f8b9f70ec3a9b0afa4130e530107490e5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Bobot?= <francois.bobot@cea.fr>
Date: Thu, 18 Nov 2021 09:56:42 +0100
Subject: [PATCH] Propagate distances between nodes that are between -1 < . < 1

---
 src_colibri2/core/colibri2_core.mli           |   6 +-
 src_colibri2/core/egraph.ml                   |   8 +-
 src_colibri2/core/ground.ml                   |   3 +-
 src_colibri2/core/interp.ml                   |   9 +-
 src_colibri2/popop_lib/debug.ml               |  16 +-
 src_colibri2/popop_lib/debug.mli              |   5 +-
 src_colibri2/popop_lib/exthtbl.ml             |  28 +
 src_colibri2/popop_lib/exthtbl.mli            |  10 +-
 src_colibri2/popop_lib/unit.ml                |   4 +
 src_colibri2/solver/scheduler.ml              |  22 +-
 .../solve/all/unsat/ordered_is_ordered.psmt2  |   2 +-
 src_colibri2/theories/LRA/LRA.ml              | 127 +---
 src_colibri2/theories/LRA/LRA_build.ml        | 144 +++++
 src_colibri2/theories/LRA/dom_interval.ml     |   2 +-
 src_colibri2/theories/LRA/fourier.ml          |  27 +-
 src_colibri2/theories/LRA/polynome.ml         |  53 +-
 src_colibri2/theories/LRA/polynome.mli        |   4 +
 src_colibri2/theories/LRA/simplex.ml          | 610 ++++++++++--------
 src_colibri2/theories/LRA/stages/bound.ml     |  17 +-
 .../theories/LRA/stages/stage1/interval.ml    |  83 ++-
 src_colibri2/theories/bool/equality.ml        |  88 ++-
 src_colibri2/theories/quantifier/common.ml    |  11 +-
 22 files changed, 737 insertions(+), 542 deletions(-)
 create mode 100644 src_colibri2/theories/LRA/LRA_build.ml

diff --git a/src_colibri2/core/colibri2_core.mli b/src_colibri2/core/colibri2_core.mli
index bb76702dc..8743ba99b 100644
--- a/src_colibri2/core/colibri2_core.mli
+++ b/src_colibri2/core/colibri2_core.mli
@@ -1639,7 +1639,11 @@ module Debug : sig
 
   type 'a stat
 
-  val register_stats_int : name:string -> init:int -> int stat
+  val register_stats_int : string -> int stat
+
+  val register_stats_time : string -> float stat
+
+  val add_time_during : float stat -> (unit -> 'a) -> 'a
 
   val incr : int stat -> unit
 
diff --git a/src_colibri2/core/egraph.ml b/src_colibri2/core/egraph.ml
index 6b62164f9..a38171f0a 100644
--- a/src_colibri2/core/egraph.ml
+++ b/src_colibri2/core/egraph.ml
@@ -35,14 +35,14 @@ let debug_few = Debug.register_info_flag
     "Egraph.few"
 
 let print_decision = Colibri2_popop_lib.Debug.register_flag ~desc:"Print@ information@ about@ the@ decisions@ made" "decision"
-let print_contradiction = Colibri2_popop_lib.Debug.register_flag ~desc:"Print@ information@ about@ the@ contradiction@ found" "contradiction"
+let print_contradiction = Colibri2_popop_lib.Debug.register_info_flag ~desc:"Print@ information@ about@ the@ contradiction@ found" "contradiction"
 
 let stats_set_dom =
-  Debug.register_stats_int ~name:"Egraph.set_dom/merge" ~init:0
+  Debug.register_stats_int "Egraph.set_dom/merge"
 let stats_set_value =
-  Debug.register_stats_int ~name:"Egraph.set_value/merge" ~init:0
+  Debug.register_stats_int "Egraph.set_value/merge"
 let stats_register =
-  Debug.register_stats_int ~name:"Egraph.register" ~init:0
+  Debug.register_stats_int "Egraph.register"
 
 module DecTag = DInt
 
diff --git a/src_colibri2/core/ground.ml b/src_colibri2/core/ground.ml
index 0207576f9..542493908 100644
--- a/src_colibri2/core/ground.ml
+++ b/src_colibri2/core/ground.ml
@@ -495,8 +495,7 @@ module Defs = struct
   (** Maximum level where the terms can be directly decided *)
   let level_dec = 0
 
-  let stats_function_call =
-    Debug.register_stats_int ~name:"Ground.function_call" ~init:0
+  let stats_function_call = Debug.register_stats_int "Ground.function_call"
 
   let converter d (v : ThTerm.t) =
     let level = Option.value ~default:0 (ThTermH.find_opt levels d v) in
diff --git a/src_colibri2/core/interp.ml b/src_colibri2/core/interp.ml
index 4dffec901..5efc717d7 100644
--- a/src_colibri2/core/interp.ml
+++ b/src_colibri2/core/interp.ml
@@ -232,9 +232,12 @@ let node d n =
                     raise exn
                 | Some th -> (
                     match Ground.of_thterm th with
-                    | None ->
-                        Fmt.epr "@.%a does not have type@." Node.pp n';
-                        raise exn
+                    | None -> (
+                        match Ground.ClosedQuantifier.of_thterm th with
+                        | None ->
+                            Fmt.epr "@.%a does not have type@." Node.pp n';
+                            raise exn
+                        | Some _ -> ty (Egraph.ro d) Ground.Ty.bool)
                     | Some g -> ty (Egraph.ro d) (Ground.sem g).ty)
               else ty (Egraph.ro d) (Ground.Ty.S.choose (Ground.tys d n'))
           in
diff --git a/src_colibri2/popop_lib/debug.ml b/src_colibri2/popop_lib/debug.ml
index 8fb130466..77d4bba8f 100644
--- a/src_colibri2/popop_lib/debug.ml
+++ b/src_colibri2/popop_lib/debug.ml
@@ -299,9 +299,21 @@ let modstats1 r f x =
 let modstats2 r f x y =
   if test_flag stats then r := f !r x y
 
-let register_stats_int ~name ~init =
-  register_stats ~pp:Format.pp_print_int ~name ~init
+let register_stats_int name =
+  register_stats ~pp:Format.pp_print_int ~name ~init:0
+
+let register_stats_time  name =
+  register_stats ~pp:(fun fmt f -> Format.fprintf fmt "%.2fs" f) ~name ~init:0.
 
 let incr r = if test_flag stats then incr r
 let decr r = if test_flag stats then decr r
 
+let add_time_during r f =
+  let gettime () = (Unix.times ()).tms_utime in
+  if test_flag stats then
+    let pre = gettime () in
+    Fun.protect f ~finally:(fun () ->
+        let post = gettime () in
+        r := !r +. (post -. pre)
+      )
+    else f ()
diff --git a/src_colibri2/popop_lib/debug.mli b/src_colibri2/popop_lib/debug.mli
index 54cbd4d25..b709f967f 100644
--- a/src_colibri2/popop_lib/debug.mli
+++ b/src_colibri2/popop_lib/debug.mli
@@ -197,8 +197,11 @@ val modstats0: 'a stat -> ('a -> 'a) -> unit
 val modstats1: 'a stat -> ('a -> 'b -> 'a) -> 'b -> unit
 val modstats2: 'a stat -> ('a -> 'b -> 'c -> 'a) -> 'b -> 'c -> unit
 
-val register_stats_int: name:string -> init:int -> int stat
+val register_stats_int: string -> int stat
+val register_stats_time: string -> float stat
 val incr: int stat -> unit
 val decr: int stat -> unit
 
+val add_time_during: float stat -> (unit -> 'a) -> 'a
+
 val print_stats: unit -> unit
diff --git a/src_colibri2/popop_lib/exthtbl.ml b/src_colibri2/popop_lib/exthtbl.ml
index d6931d37a..79e10417d 100644
--- a/src_colibri2/popop_lib/exthtbl.ml
+++ b/src_colibri2/popop_lib/exthtbl.ml
@@ -90,7 +90,9 @@ module type S =
   val find_def : 'a t -> 'a -> key -> 'a
   val find_opt : 'a t -> key -> 'a option
   val find_exn : 'a t -> exn -> key -> 'a
+  val choose : 'a t -> (key * 'a) option
   val mapi : (key -> 'a -> 'a) -> 'a t -> unit
+  val filter_mapi : (key -> 'a -> 'a option) -> 'a t -> unit
   val memo : (key -> 'a) -> 'a t -> key -> 'a
   val is_empty : 'a t -> bool
   val remove_all: 'a t -> key -> unit
@@ -345,6 +347,18 @@ module MakeSeeded(H: SeededHashedType) =
       done;
       !accu
 
+    let choose h =
+      let rec iter d i =
+        if i < 0 then None
+        else
+          match d.(i) with
+          | Empty ->
+          iter d (i-1)
+          | Cons(k, d, _) ->
+            Some (k,d);
+      in
+      let d = h.data in
+      iter d (Array.length d - 1)
 
     let mapi f h =
       let rec do_bucket f b =
@@ -357,6 +371,20 @@ module MakeSeeded(H: SeededHashedType) =
         d.(i) <- do_bucket f d.(i)
       done
 
+    let filter_mapi f h =
+      let rec do_bucket f b =
+        match b with
+          Empty -> Empty
+        | Cons(k, d, rest) ->
+          (match f k d with
+           | None -> h.size <- h.size - 1; do_bucket f rest
+           | Some v ->
+          Cons(k, v, do_bucket f rest)) in
+      let d = h.data in
+      for i = 0 to Array.length d - 1 do
+        d.(i) <- do_bucket f d.(i)
+      done
+
     let length h = h.size
 
     let rec bucket_length accu = function
diff --git a/src_colibri2/popop_lib/exthtbl.mli b/src_colibri2/popop_lib/exthtbl.mli
index 2ef0e7bbc..8f1e3b009 100644
--- a/src_colibri2/popop_lib/exthtbl.mli
+++ b/src_colibri2/popop_lib/exthtbl.mli
@@ -94,9 +94,15 @@ module type S =
   val find_exn : 'a t -> exn -> key -> 'a
   (** return the first binding or raise the given exception if none found *)
 
+  val choose : 'a t -> (key * 'a) option
+
   val mapi : (key -> 'a -> 'a) -> 'a t -> unit
-  (** change the value of all the key,
-      don't modify the table during this run *)
+  (** change the value of all the key.
+      The table should not be modified externally during this run *)
+
+  val filter_mapi : (key -> 'a -> 'a option) -> 'a t -> unit
+  (** change the value of all the key.
+      The table should not be modified externally during this run *)
 
   val memo : (key -> 'a) -> 'a t -> key -> 'a
   (** convenience function, memoize a function *)
diff --git a/src_colibri2/popop_lib/unit.ml b/src_colibri2/popop_lib/unit.ml
index 10787b6fe..ea297dc29 100644
--- a/src_colibri2/popop_lib/unit.ml
+++ b/src_colibri2/popop_lib/unit.ml
@@ -230,6 +230,10 @@ module H = struct
   let create _ = { c = [] }
   let memo _ = assert false
   let mapi f s = s.c <- List.map (fun e -> f () e) s.c
+  let filter_mapi f s = s.c <- List.filter_map (fun e -> f () e) s.c
+  let choose s = match s.c with
+    | [] -> None
+    | v::_ -> Some ((),v)
   let stats _ = assert false
   let length _ = assert false
   let fold _ = assert false
diff --git a/src_colibri2/solver/scheduler.ml b/src_colibri2/solver/scheduler.ml
index 22a4e8e8b..03dd7d332 100644
--- a/src_colibri2/solver/scheduler.ml
+++ b/src_colibri2/solver/scheduler.ml
@@ -41,22 +41,22 @@ let debug_dotgui =
 
 let var_decay = 1. /. 0.95
 
-let stats_propa = Debug.register_stats_int ~name:"Scheduler.daemon" ~init:0
+let stats_propa = Debug.register_stats_int "Scheduler.daemon"
 
-let stats_lasteffort =
-  Debug.register_stats_int ~name:"Scheduler.lasteffort" ~init:0
+let stats_lasteffort = Debug.register_stats_int "Scheduler.lasteffort"
 
 let stats_lasteffort_propa =
-  Debug.register_stats_int ~name:"Scheduler.lasteffort_propa" ~init:0
+  Debug.register_stats_int "Scheduler.lasteffort_propa"
 
-let stats_dec = Debug.register_stats_int ~name:"Scheduler.decision" ~init:0
+let stats_dec = Debug.register_stats_int "Scheduler.decision"
 
-let stats_con = Debug.register_stats_int ~name:"Scheduler.conflict" ~init:0
+let stats_con = Debug.register_stats_int "Scheduler.conflict"
 
-let stats_step = Debug.register_stats_int ~name:"Scheduler.step" ~init:0
+let stats_con_fix = Debug.register_stats_int "Scheduler.conflict_fix_model"
 
-let stats_fix_model =
-  Debug.register_stats_int ~name:"Scheduler.fix_model" ~init:0
+let stats_step = Debug.register_stats_int "Scheduler.step"
+
+let stats_fix_model = Debug.register_stats_int "Scheduler.fix_model"
 
 exception Contradiction
 
@@ -546,7 +546,9 @@ let rec conflict_analysis t =
   Debug.dprintf0 debug "[Scheduler] End of learning";
   if Colibri2_popop_lib.Debug.test_flag debug_dotgui then
     Backtrackable.draw_graph ~force:true t.solver_state;
-  Debug.incr stats_con;
+  (match Context.Ref.get t.solve_step with
+  | FixModel -> Debug.incr stats_con_fix
+  | BacktrackChoice _ | Propagate -> Debug.incr stats_con);
   t.var_inc := !(t.var_inc) *. var_decay;
   let rec rewind () =
     match t.prev_scheduler_state with
diff --git a/src_colibri2/tests/solve/all/unsat/ordered_is_ordered.psmt2 b/src_colibri2/tests/solve/all/unsat/ordered_is_ordered.psmt2
index b68bf6fca..d3c911337 100644
--- a/src_colibri2/tests/solve/all/unsat/ordered_is_ordered.psmt2
+++ b/src_colibri2/tests/solve/all/unsat/ordered_is_ordered.psmt2
@@ -1,7 +1,7 @@
 ;; produced by local colibri2.drv ;;
 (set-logic ALL)
 (set-info :smt-lib-version 2.6)
-(set-option :max-steps-colibri2 11000)
+(set-option :max-steps-colibri2 14000)
 
 (declare-datatypes ((tuple2 2))
   ((par (a1 a2) ((Tuple2 (Tuple2_proj_1 a1)(Tuple2_proj_2 a2))))))
diff --git a/src_colibri2/theories/LRA/LRA.ml b/src_colibri2/theories/LRA/LRA.ml
index 11a6c011f..1ab90f492 100644
--- a/src_colibri2/theories/LRA/LRA.ml
+++ b/src_colibri2/theories/LRA/LRA.ml
@@ -19,10 +19,6 @@
 (*************************************************************************)
 
 open Colibri2_core
-open Colibri2_stdlib.Std
-
-let default_value = Q.zero
-
 module RealValue = RealValue
 
 let th_register env =
@@ -36,125 +32,4 @@ let th_register env =
 
 let () = Init.add_default_theory th_register
 
-(** Helpers to remove *)
-
-let a = Expr.Term.Var.mk "a" Expr.Ty.real
-
-let b = Expr.Term.Var.mk "b" Expr.Ty.real
-
-let ta = Expr.Term.of_var a
-
-let tb = Expr.Term.of_var b
-
-let c = Expr.Term.Var.mk "c" Expr.Ty.real
-
-let d = Expr.Term.Var.mk "d" Expr.Ty.real
-
-let tc = Expr.Term.of_var c
-
-let td = Expr.Term.of_var d
-
-let tadd = Expr.Term.Real.add ta tb
-
-let add d na nb =
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
-    }
-  in
-  Ground.convert ~subst d tadd
-
-let tadd' = Expr.Term.Real.(add (mul tc ta) (mul td tb))
-
-let add' env qa na qb nb =
-  let qa = RealValue.cst qa in
-  let qb = RealValue.cst qb in
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb); (c, qa); (d, qb) ];
-    }
-  in
-  Ground.convert ~subst env tadd'
-
-let tsub = Expr.Term.Real.sub ta tb
-
-let sub env na nb =
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
-    }
-  in
-  Ground.convert ~subst env tsub
-
-let tmult_cst = Expr.Term.Real.(mul tc ta)
-
-let mult_cst env qa na =
-  let qa = RealValue.cst qa in
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (c, qa) ];
-    }
-  in
-  Ground.convert ~subst env tmult_cst
-
-let tmult = Expr.Term.Real.(mul ta tb)
-
-let mult env na nb =
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
-    }
-  in
-  Ground.convert ~subst env tmult
-
-let tgt_zero = Expr.Term.Real.(gt ta tb)
-
-let gt_zero env na =
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (b, RealValue.zero) ];
-    }
-  in
-  Ground.convert ~subst env tgt_zero
-
-let tge_zero = Expr.Term.Real.(ge ta tb)
-
-let ge_zero env na =
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (b, RealValue.zero) ];
-    }
-  in
-  Ground.convert ~subst env tge_zero
-
-let cmp tcmp env na nb =
-  let subst =
-    {
-      Ground.Subst.empty with
-      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
-    }
-  in
-  Ground.convert ~subst env tcmp
-
-let tlt = Expr.Term.Real.lt ta tb
-
-let tle = Expr.Term.Real.le ta tb
-
-let tgt = Expr.Term.Real.gt ta tb
-
-let tge = Expr.Term.Real.ge ta tb
-
-let lt env na nb = cmp tlt env na nb
-
-let le env na nb = cmp tle env na nb
-
-let gt env na nb = cmp tgt env na nb
-
-let ge env na nb = cmp tge env na nb
+include LRA_build
diff --git a/src_colibri2/theories/LRA/LRA_build.ml b/src_colibri2/theories/LRA/LRA_build.ml
new file mode 100644
index 000000000..7f0b7e530
--- /dev/null
+++ b/src_colibri2/theories/LRA/LRA_build.ml
@@ -0,0 +1,144 @@
+(*************************************************************************)
+(*  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).           *)
+(*************************************************************************)
+
+open Colibri2_core
+
+(** Helpers to remove *)
+
+let a = Expr.Term.Var.mk "a" Expr.Ty.real
+
+let b = Expr.Term.Var.mk "b" Expr.Ty.real
+
+let ta = Expr.Term.of_var a
+
+let tb = Expr.Term.of_var b
+
+let c = Expr.Term.Var.mk "c" Expr.Ty.real
+
+let d = Expr.Term.Var.mk "d" Expr.Ty.real
+
+let tc = Expr.Term.of_var c
+
+let td = Expr.Term.of_var d
+
+let tadd = Expr.Term.Real.add ta tb
+
+let add d na nb =
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
+    }
+  in
+  Ground.convert ~subst d tadd
+
+let tadd' = Expr.Term.Real.(add (mul tc ta) (mul td tb))
+
+let add' env qa na qb nb =
+  let qa = RealValue.cst qa in
+  let qb = RealValue.cst qb in
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb); (c, qa); (d, qb) ];
+    }
+  in
+  Ground.convert ~subst env tadd'
+
+let tsub = Expr.Term.Real.sub ta tb
+
+let sub env na nb =
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
+    }
+  in
+  Ground.convert ~subst env tsub
+
+let tmult_cst = Expr.Term.Real.(mul tc ta)
+
+let mult_cst env qa na =
+  let qa = RealValue.cst qa in
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (c, qa) ];
+    }
+  in
+  Ground.convert ~subst env tmult_cst
+
+let tmult = Expr.Term.Real.(mul ta tb)
+
+let mult env na nb =
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
+    }
+  in
+  Ground.convert ~subst env tmult
+
+let tgt_zero = Expr.Term.Real.(gt ta tb)
+
+let gt_zero env na =
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (b, RealValue.zero) ];
+    }
+  in
+  Ground.convert ~subst env tgt_zero
+
+let tge_zero = Expr.Term.Real.(ge ta tb)
+
+let ge_zero env na =
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (b, RealValue.zero) ];
+    }
+  in
+  Ground.convert ~subst env tge_zero
+
+let cmp tcmp env na nb =
+  let subst =
+    {
+      Ground.Subst.empty with
+      term = Expr.Term.Var.M.of_list [ (a, na); (b, nb) ];
+    }
+  in
+  Ground.convert ~subst env tcmp
+
+let tlt = Expr.Term.Real.lt ta tb
+
+let tle = Expr.Term.Real.le ta tb
+
+let tgt = Expr.Term.Real.gt ta tb
+
+let tge = Expr.Term.Real.ge ta tb
+
+let lt env na nb = cmp tlt env na nb
+
+let le env na nb = cmp tle env na nb
+
+let gt env na nb = cmp tgt env na nb
+
+let ge env na nb = cmp tge env na nb
diff --git a/src_colibri2/theories/LRA/dom_interval.ml b/src_colibri2/theories/LRA/dom_interval.ml
index 47b52e036..d64a5a9a0 100644
--- a/src_colibri2/theories/LRA/dom_interval.ml
+++ b/src_colibri2/theories/LRA/dom_interval.ml
@@ -242,7 +242,7 @@ let init env =
   Daemon.attach_reg_value env RealValue.key (fun d value ->
       let v = RealValue.value value in
       let s = D.singleton v in
-      Egraph.set_dom d dom (RealValue.node value) s);
+      upd_dom d (RealValue.node value) s);
   Ground.register_converter env converter;
   Interp.Register.node env (fun _ d n ->
       match Egraph.get_dom d dom n with
diff --git a/src_colibri2/theories/LRA/fourier.ml b/src_colibri2/theories/LRA/fourier.ml
index 78d1d1833..0aa6ae821 100644
--- a/src_colibri2/theories/LRA/fourier.ml
+++ b/src_colibri2/theories/LRA/fourier.ml
@@ -29,7 +29,9 @@ let scheduled =
 let debug =
   Debug.register_info_flag ~desc:"Reasoning about <= < in LRA" "fourier"
 
-let stats_run = Debug.register_stats_int ~name:"Fourier.run" ~init:0
+let stats_run = Debug.register_stats_int "Fourier.run"
+
+let stats_time = Debug.register_stats_time "Fourier.time"
 
 type eq = { p : Polynome.t; bound : Bound.t; origins : Ground.S.t }
 [@@deriving show]
@@ -143,15 +145,18 @@ let add_eq d eqs p bound origins =
         (fun g ->
           let truth = Base.Option.value_exn (Boolean.is d (Ground.node g)) in
           apply d truth g ~f:(fun d bound a b ->
-              match bound with
-              | Strict
-                when Dom_interval.is_integer d a && Dom_interval.is_integer d b
-                ->
-                  Dom_polynome.assume_equality d a
-                    (Polynome.of_list Q.minus_one [ (b, Q.one) ])
-              | Large ->
-                  Dom_polynome.assume_equality d a (Polynome.monome Q.one b)
-              | Strict -> assert false))
+              let p =
+                match bound with
+                | Strict
+                  when Dom_interval.is_integer d a
+                       && Dom_interval.is_integer d b ->
+                    Polynome.of_list Q.minus_one [ (b, Q.one) ]
+                | Large -> Polynome.monome Q.one b
+                | Strict -> assert false
+              in
+              Debug.dprintf4 debug "[LRA/Fourier] Found %a = %a" Node.pp a
+                Polynome.pp p;
+              Dom_polynome.assume_equality d a p))
         origins;
       eqs
   | Some p, _ when Q.sign p < 0 ->
@@ -257,6 +262,8 @@ let fm (d : Egraph.wt) =
   in
   aux eqs vars
 
+let fm d = Debug.add_time_during stats_time (fun () -> fm d)
+
 module Daemon = struct
   let key =
     Events.Dem.create
diff --git a/src_colibri2/theories/LRA/polynome.ml b/src_colibri2/theories/LRA/polynome.ml
index 27a7dccab..d80e81848 100644
--- a/src_colibri2/theories/LRA/polynome.ml
+++ b/src_colibri2/theories/LRA/polynome.ml
@@ -21,33 +21,38 @@
 open Colibri2_popop_lib
 open Colibri2_core
 
+let print_poly fmt poly =
+  let print_not_1 first fmt q =
+    if not first && Q.compare q Q.zero >= 0
+    then Format.pp_print_string fmt "+";
+    if Q.equal q Q.zero then  Format.pp_print_string fmt "!0!"
+    else if Q.equal Q.minus_one q then Format.pp_print_string fmt "-"
+    else if not (Q.equal Q.one q) then Q.pp fmt q
+  in
+    let print_mono k v (fmt,first) =
+      Format.fprintf fmt "@[%a%a@]@," (print_not_1 first) v Node.pp k;
+      (fmt,false)
+    in
+  let _,first = Node.M.fold print_mono poly (fmt,true) in
+  first
+
 module T = struct
   type t = { cst : Q.t; poly : Q.t Node.M.t}
   [@@deriving eq,ord,hash]
 
   let pp fmt v =
-    let print_not_1 first fmt q =
-      if not first && Q.compare q Q.zero >= 0
-      then Format.pp_print_string fmt "+";
-      if Q.equal q Q.zero then  Format.pp_print_string fmt "!0!"
-      else if Q.equal Q.minus_one q then Format.pp_print_string fmt "-"
-      else if not (Q.equal Q.one q) then Q.pp fmt q
-    in
-    let print_not_0 first fmt q =
-      if first
-      then Q.pp fmt q
-      else
-      if not (Q.equal Q.zero q) then begin
-        if Q.compare q Q.zero > 0 then Format.pp_print_string fmt "+";
-        Q.pp fmt q
-      end
-    in
-    let print_mono k v (fmt,first) =
-      Format.fprintf fmt "@[%a%a@]@," (print_not_1 first) v Node.pp k;
-      (fmt,false)
-    in
+      let print_not_0 first fmt q =
+    if first
+    then Q.pp fmt q
+    else
+    if not (Q.equal Q.zero q) then begin
+      if Q.compare q Q.zero > 0 then Format.pp_print_string fmt "+";
+      Q.pp fmt q
+    end
+  in
+
     Format.fprintf fmt "@[";
-    let _,first = Node.M.fold print_mono v.poly (fmt,true) in
+    let first = print_poly fmt v.poly in
     Format.fprintf fmt "%a@]" (print_not_0 first) v.cst
 
 end
@@ -185,6 +190,10 @@ let of_list cst l =
       | Some q' -> none_zero (Q.add q q')) node acc in
   {cst;poly= List.fold_left fold Node.M.empty l}
 
+let of_map cst poly =
+  assert ( Node.M.for_all (fun _ q -> not (Q.equal Q.zero q)) poly );
+  {cst;poly}
+
 module ClM = Extmap.Make(Node)
 
 type 'a tree = 'a ClM.view =
@@ -206,3 +215,5 @@ let normalize p =
       let conv q = Q.make (Z.divexact q.Q.num num) (Z.divexact q.Q.den den) in
       { poly = Node.M.map conv p.poly;
         cst = conv p.cst }
+
+let print_poly fmt poly = let _ = print_poly fmt poly in ()
diff --git a/src_colibri2/theories/LRA/polynome.mli b/src_colibri2/theories/LRA/polynome.mli
index b2cb3e993..2d5aa6c39 100644
--- a/src_colibri2/theories/LRA/polynome.mli
+++ b/src_colibri2/theories/LRA/polynome.mli
@@ -64,6 +64,8 @@ val sub : t -> t -> t
 
 val of_list : Q.t -> (Node.t * Q.t) list -> t
 
+val of_map : Q.t -> Q.t Node.M.t -> t
+
 val x_p_cy : t -> Q.t -> t -> t
 
 val cx_p_cy : Q.t -> t -> Q.t -> t -> t
@@ -82,3 +84,5 @@ val get_tree : t -> Q.t tree * Q.t
 
 val normalize : t -> t
 (** divide by the pgcd *)
+
+val print_poly : Q.t Node.M.t Fmt.t
diff --git a/src_colibri2/theories/LRA/simplex.ml b/src_colibri2/theories/LRA/simplex.ml
index b0e874054..b71e33c80 100644
--- a/src_colibri2/theories/LRA/simplex.ml
+++ b/src_colibri2/theories/LRA/simplex.ml
@@ -20,8 +20,9 @@
 
 open Colibri2_popop_lib
 open Colibri2_core
+open Base
 
-let comparisons = Datastructure.Push.create Ground.pp "LRA.simplex.comparisons"
+let comparisons = Datastructure.Push.create Node.pp "LRA.simplex.comparisons"
 
 let scheduled =
   Datastructure.Ref.create Base.Bool.pp "LRA.simplex.scheduled" false
@@ -29,7 +30,11 @@ let scheduled =
 let debug =
   Debug.register_info_flag ~desc:"Reasoning about <= < in LRA" "simplex"
 
-let stats_run = Debug.register_stats_int ~name:"Simplex.run" ~init:0
+let stats_run = Debug.register_stats_int "Simplex.run"
+
+let stats_maximization = Debug.register_stats_int "Simplex.maximization"
+
+let stats_time = Debug.register_stats_time "Simplex.time"
 
 module Var = struct
   include Node
@@ -42,7 +47,7 @@ end
 module Ex = struct
   type t = unit
 
-  let print fmt () = Format.fprintf fmt "()"
+  let print fmt () = Fmt.pf fmt "()"
 
   let empty = ()
 
@@ -54,7 +59,7 @@ module Rat = struct
 
   let m_one = Q.of_int (-1)
 
-  let print fmt t = Format.fprintf fmt "%s" (Q.to_string t)
+  let print fmt t = Fmt.pf fmt "%s" (Q.to_string t)
 
   let is_int x = Z.equal Z.one x.Q.den
 
@@ -71,173 +76,167 @@ end
 
 module Sim = OcplibSimplex.Basic.Make (Var) (Rat) (Ex)
 
-type eq = { p : Polynome.t; bound : Bound.t; origins : Ground.S.t }
-[@@deriving show]
-(** p <= 0 or p < 0 *)
-
-let divide d (p : Polynome.t) =
-  try
-    (match Polynome.is_cst p with None -> () | Some _ -> raise Exit);
-    if Q.sign p.cst <> 0 then raise Exit;
-    let l = Node.M.bindings p.poly in
-    let l =
-      List.fold_left
-        (fun acc (e, q) ->
-          match
-            ( Dom_product.SolveAbs.get_repr d e,
-              Dom_product.SolveSign.get_repr d e )
-          with
-          | None, _ when Egraph.is_equal d RealValue.zero e -> acc
-          | None, _ | _, None -> raise Exit
-          | Some abs, Some sign -> (abs, sign, q) :: acc)
-        [] l
-    in
-    (* Debug.dprintf4 debug "@[eq:%a@ %a@]" Polynome.pp p
-     *   Fmt.(list ~sep:(any "+") (using CCPair.swap (pair Q.pp Product.pp)))
-     *   l; *)
-    match l with
-    | [] -> raise Exit
-    | (hd, __, _) :: _ ->
-        let common =
-          List.fold_left
-            (fun acc (abs, _, _) ->
-              Node.M.inter
-                (fun _ a b -> if Q.equal a b then Some a else None)
-                acc abs.Product.poly)
-            hd.Product.poly l
-        in
-        let common =
-          Node.M.fold_left
-            (fun acc abs v ->
-              if Dom_interval.is_not_zero d abs then Node.M.add abs v acc
-              else acc)
-            Node.M.empty common
-        in
-        if Node.M.is_empty common then raise Exit;
-        let common = Product.of_map common in
-        Debug.dprintf4 debug "[Simplex] found possible division: %a / |%a|"
-          Polynome.pp p Product.pp common;
-        let cst, l =
-          List.fold_left
-            (fun (cst, acc) (abs, sign, v) ->
-              let abs = Product.div abs common in
-              let pos, sign = Sign_product.extract_cst sign in
-              let v =
-                match pos with Zero -> Q.zero | Pos -> v | Neg -> Q.neg v
-              in
-              match (Product.classify abs, Sign_product.classify sign) with
-              | _, MINUS_ONE -> assert false
-              | ONE, PLUS_ONE -> (Q.add v cst, acc)
-              | NODE n, NODE n' when Egraph.is_equal d n n' ->
-                  (cst, (n, v) :: acc)
-              | _ ->
-                  let n = Dom_product.node_of_product d abs sign in
-                  (cst, (n, v) :: acc))
-            (Q.zero, []) l
-        in
-        Some (Polynome.of_list cst l, common)
-  with Exit -> None
+type bound = No | Large of Q.t | Strict of Q.t
+
+let pp_bound_inf fmt = function
+  | No -> ()
+  | Large q -> Fmt.pf fmt "%a ≤" Q.pp q
+  | Strict q -> Fmt.pf fmt "%a <" Q.pp q
+
+let pp_bound_sup fmt = function
+  | No -> ()
+  | Large q -> Fmt.pf fmt "≤ %a" Q.pp q
+  | Strict q -> Fmt.pf fmt "< %a" Q.pp q
+
+let to_bound = function
+  | None -> No
+  | Some (q, Bound.Large) -> Large q
+  | Some (q, Bound.Strict) -> Strict q
+
+let inter_inf inf1 inf2 =
+  match (inf1, inf2) with
+  | No, inf | inf, No -> inf
+  | (Large q1, (Strict q2 as inf) | (Strict q2 as inf), Large q1)
+    when Q.equal q1 q2 ->
+      inf
+  | ((Large q1 | Strict q1) as inf1), ((Strict q2 | Large q2) as inf2) ->
+      if Q.leq q2 q1 then inf1 else inf2
+
+let inter_sup inf1 inf2 =
+  match (inf1, inf2) with
+  | No, inf | inf, No -> inf
+  | (Large q1, (Strict q2 as inf) | (Strict q2 as inf), Large q1)
+    when Q.equal q1 q2 ->
+      inf
+  | ((Large q1 | Strict q1) as inf1), ((Strict q2 | Large q2) as inf2) ->
+      if Q.leq q2 q1 then inf2 else inf1
+
+type eq = { inf : bound; p : Q.t Node.M.t; sup : bound }
+
+let pp_eq fmt { inf; p; sup } =
+  Fmt.pf fmt "%a%a%a" pp_bound_inf inf Polynome.print_poly p pp_bound_sup sup
 
 let delta d eq =
+  let add d src bnd dst =
+    match bnd with
+    | No -> ()
+    | Large dist -> Delta.add_le d src dist dst
+    | Strict dist -> Delta.add_lt d src dist dst
+  in
   (* add info to delta *)
-  if Node.M.is_num_elt 2 eq.p.poly then
-    match Node.M.bindings eq.p.poly with
+  if Node.M.is_num_elt 2 eq.p then
+    match Node.M.bindings eq.p with
     | [ (src, a); (dst, b) ] when Q.equal Q.one a && Q.equal Q.minus_one b ->
-        Delta.add d src (Q.neg eq.p.cst) eq.bound dst
+        add d src eq.sup dst;
+        add d dst eq.inf src
     | [ (dst, b); (src, a) ] when Q.equal Q.one a && Q.equal Q.minus_one b ->
-        Delta.add d src (Q.neg eq.p.cst) eq.bound dst
+        add d src eq.sup dst;
+        add d dst eq.inf src
     | _ -> ()
 
-let apply d ~f truth g =
-  let aux d bound a b =
-    let bound =
-      if truth then bound
-      else match bound with Bound.Large -> Strict | Strict -> Large
-    in
-    let a, b = if truth then (a, b) else (b, a) in
-    f d bound a b
+let add_eq _ eqs eq =
+  let p = Polynome.of_map Q.zero eq.p in
+  Polynome.H.change
+    (function
+      | None -> Some eq
+      | Some eq' ->
+          Some
+            {
+              inf = inter_inf eq'.inf eq.inf;
+              sup = inter_sup eq'.sup eq.sup;
+              p = eq.p;
+            })
+    eqs p;
+  eqs
+
+let mk_eq _ p dom =
+  assert (not (Node.M.is_empty p.Polynome.poly));
+  let _, q = Node.M.choose p.poly in
+  assert (not (Q.equal q Q.zero));
+  let p = Polynome.mult_cst (Q.inv q) p in
+  let inf, sup = Dom_interval.D.get_convexe_hull dom in
+  let inf, sup = if Q.sign q < 0 then (sup, inf) else (inf, sup) in
+  let translate = function
+    | No -> No
+    | Strict c -> Strict (Q.sub (Q.div c q) p.cst)
+    | Large c -> Large (Q.sub (Q.div c q) p.cst)
   in
-  match Ground.sem g with
-  | { app = { builtin = Expr.Lt }; tyargs = []; args; _ } ->
-      let a, b = IArray.extract2_exn args in
-      aux d Bound.Strict a b
-  | { app = { builtin = Expr.Leq }; tyargs = []; args; _ } ->
-      let a, b = IArray.extract2_exn args in
-      aux d Large a b
-  | { app = { builtin = Expr.Geq }; tyargs = []; args; _ } ->
-      let a, b = IArray.extract2_exn args in
-      aux d Large b a
-  | { app = { builtin = Expr.Gt }; tyargs = []; args; _ } ->
-      let a, b = IArray.extract2_exn args in
-      aux d Strict b a
-  | _ -> assert false
-
-let add_eq d eqs p bound origins =
-  match (Polynome.is_cst p, bound) with
-  | None, _ ->
-      let eq = { p; bound; origins } in
-      delta d eq;
-      eq :: eqs
-  | Some p, Bound.Large when Q.sign p = 0 ->
-      Ground.S.iter
-        (fun g ->
-          let truth = Base.Option.value_exn (Boolean.is d (Ground.node g)) in
-          apply d truth g ~f:(fun d bound a b ->
-              match bound with
-              | Strict
-                when Dom_interval.is_integer d a && Dom_interval.is_integer d b
-                ->
-                  Dom_polynome.assume_equality d a
-                    (Polynome.of_list Q.minus_one [ (b, Q.one) ])
-              | Large ->
-                  Dom_polynome.assume_equality d a (Polynome.monome Q.one b)
-              | Strict -> assert false))
-        origins;
-      eqs
-  | Some p, _ when Q.sign p < 0 ->
-      Debug.dprintf2 debug "[Simplex] discard %a" Ground.S.pp origins;
-      eqs
-  | Some p, bound ->
-      Debug.dprintf4 Debug.contradiction "[LRA/Simplex] Found %a%a0" Q.pp p
-        Bound.pp bound;
-      Egraph.contradiction d
-
-let mk_eq d bound a b =
-  let ( ! ) n =
-    match Dom_polynome.get_repr d n with
-    | None -> Polynome.monome Q.one (Egraph.find_def d n)
-    | Some p -> p
+  let inf = translate @@ to_bound inf in
+  let sup = translate @@ to_bound sup in
+  { inf; p = p.poly; sup }
+
+let make_equations d (eqs, vars) n =
+  Debug.dprintf2 debug "[Simplex] %a" Node.pp n;
+  let addition (eqs, vars) n =
+    match Dom_interval.get_dom d n with
+    | None -> (eqs, vars)
+    | Some dom ->
+        let p =
+          match Dom_polynome.get_repr d n with
+          | None -> Polynome.monome Q.one (Egraph.find_def d n)
+          | Some p -> p
+        in
+        if Node.M.is_empty p.poly then (eqs, vars)
+        else
+          let eq = mk_eq d p dom in
+          Debug.dprintf6 debug "[Simplex] %a ∈ %a -> %a" Polynome.pp p
+            Dom_interval.D.pp dom pp_eq eq;
+          let eqs, vars =
+            ( add_eq d eqs eq,
+              Node.M.union_merge (fun _ _ _ -> Some ()) vars p.Polynome.poly )
+          in
+          (eqs, vars)
   in
-  let p, bound' =
-    match bound with
-    | Bound.Strict
-      when Dom_interval.is_integer d a && Dom_interval.is_integer d b ->
-        (Polynome.add_cst (Polynome.sub !a !b) Q.one, Bound.Large)
-    | _ -> (Polynome.sub !a !b, bound)
+  let product acc =
+    let abs = Dom_product.SolveAbs.get_repr d n in
+    let sign = Dom_product.SolveSign.get_repr d n in
+    match (abs, sign) with
+    | Some abs, Some sign ->
+        Debug.dprintf6 debug "[Simplex] %a: %a --- %a" Node.pp n Product.pp abs
+          Sign_product.pp sign;
+        if Sign_product.(equal one sign) then
+          (* only simple case currently *)
+          let all_pos =
+            Node.M.for_all
+              (fun n _ ->
+                let sign = Dom_product.SolveSign.get_repr d n in
+                Option.exists ~f:Sign_product.(equal one) sign)
+              abs.poly
+          in
+          if all_pos then
+            Node.M.fold_left (fun acc n _ -> addition acc n) acc abs.poly
+          else acc
+        else acc
+    | _ -> acc
   in
-  ( p,
-    bound',
-    Polynome.sub (Polynome.monome Q.one a) (Polynome.monome Q.one b),
-    bound )
-
-let make_equations d (eqs, vars) g =
-  Debug.dprintf2 debug "[Simplex] %a" Ground.pp g;
-  match Boolean.is d (Ground.node g) with
-  | None -> (eqs, vars)
-  | Some truth -> (
-      let p, bound, p_non_norm, bound_non_norm = apply d ~f:mk_eq truth g in
-      Debug.dprintf6 debug "[Simplex] %a(%a)%a0" Polynome.pp p Polynome.pp
-        p_non_norm Bound.pp bound;
-      let eqs, vars =
-        ( add_eq d eqs p bound (Ground.S.singleton g),
-          Node.M.union_merge (fun _ _ _ -> Some ()) vars p.Polynome.poly )
-      in
-      match divide d p_non_norm with
-      | Some (p', _) ->
-          let p' = Dom_polynome.normalize d p' in
-          ( add_eq d eqs p' bound_non_norm Ground.S.empty,
-            Node.M.union_merge (fun _ _ _ -> Some ()) vars p'.Polynome.poly )
-      | None -> (eqs, vars))
+  product (addition (eqs, vars) n)
+
+module Edge = struct
+  module T = struct
+    type t = { from : Node.t; to_ : Node.t } [@@deriving show, eq, ord, hash]
+  end
+
+  include T
+  include Popop_stdlib.MkDatatype (T)
+end
+
+let of_bound_sup = function
+  | No -> None
+  | Large q ->
+      assert (Q.is_real q);
+      Some (q, Q.zero)
+  | Strict q ->
+      assert (Q.is_real q);
+      Some (q, Q.minus_one)
+
+let of_bound_inf = function
+  | No -> None
+  | Large q ->
+      assert (Q.is_real q);
+      Some (q, Q.zero)
+  | Strict q ->
+      assert (Q.is_real q);
+      Some (q, Q.one)
 
 let find_equalities d vars (env : Sim.Core.t) (s : Sim.Core.solution Lazy.t) =
   (* Env must be sat *)
@@ -245,39 +244,42 @@ let find_equalities d vars (env : Sim.Core.t) (s : Sim.Core.solution Lazy.t) =
     let s = Lazy.force s in
     let h = Node.H.create 10 in
     Base.List.iter ~f:(fun (n, q) -> Node.H.replace h n q) s.Sim.Core.main_vars;
+    Base.List.iter ~f:(fun (n, q) -> Node.H.replace h n q) s.Sim.Core.slake_vars;
     h
   in
-  let partition l s =
-    let h = val_of_sol s in
-    let aux l =
-      let l =
-        Base.List.stable_sort
-          ~compare:(fun n1 n2 ->
-            Q.compare (Node.H.find h n1) (Node.H.find h n2))
-          l
-      in
-      let l =
-        Base.List.group
-          ~break:(fun n1 n2 ->
-            not (Q.equal (Node.H.find h n1) (Node.H.find h n2)))
-          l
-      in
-
-      let l =
-        Base.List.filter ~f:(function [] | [ _ ] -> false | _ -> true) l
-      in
-      l
-    in
-    let l = Base.List.concat_map ~f:aux l in
-    (* Debug.dprintf1 debug "partition %s" ([%show: Node.t list list] l); *)
-    l
+  let under_approx = Edge.H.create 10 in
+  let _ =
+    Node.S.fold_left
+      (fun acc from ->
+        let is_int_from = Dom_interval.is_integer d from in
+        let acc = Node.S.remove from acc in
+        Node.S.iter
+          (fun to_ ->
+            let is_int_to = Dom_interval.is_integer d to_ in
+            Edge.H.add_new Impossible under_approx { Edge.from; to_ }
+              (is_int_from, is_int_to, Q.inf, Q.minus_inf))
+          acc;
+        acc)
+      vars vars
   in
-  let pair_seen = Node.H.create 10 in
-  let add_pair_seen a b =
-    let h = Node.H.memo (fun _ -> Node.H.create 10) pair_seen a in
-    Node.H.add_new Impossible h b ()
+  let update_under_approx s =
+    let h = val_of_sol s in
+    Edge.H.filter_mapi
+      (fun { from; to_ } (is_int_from, is_int_to, inf, sup) ->
+        let dist = Q.sub (Node.H.find h to_) (Node.H.find h from) in
+        Debug.dprintf10 debug "[Simplex] under_approx: %a <= %a - %a <= %a : %a"
+          Q.pp inf Node.pp to_ Node.pp from Q.pp sup Q.pp dist;
+        let inf = Q.min inf dist in
+        let sup = Q.max sup dist in
+        if is_int_from || is_int_to then
+          if Q.le inf Q.minus_one || Q.le Q.one sup then None
+          else Some (is_int_from, is_int_to, inf, sup)
+        else if Q.lt inf Q.zero || Q.lt Q.zero sup then None
+        else Some (is_int_from, is_int_to, inf, sup))
+      under_approx
   in
-  let one_dist env a b =
+  let one_dist env is_int a b =
+    Debug.incr stats_maximization;
     (* Debug.dprintf4 debug "one_dist %a %a" Node.pp a Node.pp b; *)
     let p = Sim.Core.P.from_list [ (a, Q.one); (b, Q.minus_one) ] in
     let env, opt = Sim.Solve.maximize env p in
@@ -286,61 +288,64 @@ let find_equalities d vars (env : Sim.Core.t) (s : Sim.Core.solution Lazy.t) =
     | Sim.Core.Unsat _ -> assert false
     | Sim.Core.Sat _ -> assert false
     | Sim.Core.Unbounded s ->
-        let s' = Lazy.force s in
-        let qa = Base.List.Assoc.find_exn ~equal:Node.equal s'.main_vars a in
-        let qb = Base.List.Assoc.find_exn ~equal:Node.equal s'.main_vars b in
-        (if Q.equal qa qb then
-         let a, b = if Node.compare a b < 0 then (a, b) else (b, a) in
-         (* Debug.dprintf4 debug "pair_seen %a %a" Node.pp a Node.pp b; *)
-         add_pair_seen a b);
+        update_under_approx s;
+        (* s is often not interesting for the update; all at zero, so the
+           distance is pushed a little further *)
+        let p' = Polynome.of_list Q.zero [ (a, Q.one); (b, Q.minus_one) ] in
+        let n = Dom_polynome.node_of_polynome d p' in
+        let env', _ =
+          Sim.Assert.poly env p n (of_bound_inf (Large (Q.of_int 2))) () None ()
+        in
+        let env' = Sim.Solve.solve env' in
+        let s =
+          match Sim.Result.get opt env' with
+          | Sim.Core.Sat s -> s
+          | Sim.Core.Unbounded s -> s
+          | _ -> assert false
+        in
         (`Distinct, env, s)
     | Sim.Core.Max (m, s) ->
-        Debug.dprintf2 debug "Max %a" Q.pp (Lazy.force m).max_v;
-        let s' = Lazy.force s in
-        let qa = Base.List.Assoc.find_exn ~equal:Node.equal s'.main_vars a in
-        let qb = Base.List.Assoc.find_exn ~equal:Node.equal s'.main_vars b in
-        if Q.equal qa qb then (`NotDistinct, env, s) else (`Distinct, env, s)
-  in
-  let rec test_equality env l =
-    match l with
-    | [] -> env
-    | [ _ ] :: l -> test_equality env l
-    | [] :: _ -> assert false
-    | (a :: l1) :: l2 -> (
-        let skip =
-          match Node.H.find_opt pair_seen a with
-          | None -> Base.List.hd l1
-          | Some h -> Base.List.find l1 ~f:(fun b -> not (Node.H.mem h b))
+        let m = Lazy.force m in
+        Debug.dprintf2 debug "Max %a" Q.pp m.max_v;
+        (if m.is_le then Delta.add_le else Delta.add_lt) d a m.max_v b;
+        let notdistinct =
+          if is_int then Q.lt (Q.abs m.max_v) Q.one else Q.equal m.max_v Q.zero
         in
-        match skip with
-        | None -> test_equality env (l1 :: l2)
-        | Some b -> (
-            match one_dist env a b with
+        if notdistinct then (`NotDistinct, env, s) else (`Distinct, env, s)
+  in
+  (* test equality and propagate small distance *)
+  let rec test_equality env =
+    match Edge.H.choose under_approx with
+    | None -> env
+    | Some (({ from; to_ } as e), (is_int_from, is_int_to, _, _)) -> (
+        Edge.H.remove under_approx e;
+        let is_int = is_int_from && is_int_to in
+        match one_dist env is_int from to_ with
+        | `Distinct, env, s ->
+            update_under_approx s;
+            let env =
+              if is_int_from || is_int_to then (
+                let _, env, s = one_dist env is_int to_ from in
+                update_under_approx s;
+                env)
+              else env
+            in
+            test_equality env
+        | `NotDistinct, env, s -> (
+            update_under_approx s;
+            match one_dist env is_int to_ from with
             | `Distinct, env, s ->
-                Debug.dprintf1 debug "Distinct %s"
-                  ([%show: (Node.t * Q.t) list]
-                     (Lazy.force s).Sim.Core.main_vars);
-                let l = partition l s in
-                test_equality env l
-            | `NotDistinct, env, s -> (
-                let l = partition l s in
-                match one_dist env b a with
-                | `Distinct, env, s ->
-                    let l = partition l s in
-                    test_equality env l
-                | `NotDistinct, env, s -> (
-                    let l = partition l s in
-                    Egraph.merge d a b;
-                    match l with
-                    | [] -> assert false
-                    | ([] | [ _ ]) :: _ -> assert false
-                    | (a' :: l1) :: l2 ->
-                        assert (Node.equal a a');
-                        test_equality env (l1 :: l2)))))
+                update_under_approx s;
+                test_equality env
+            | `NotDistinct, env, s ->
+                update_under_approx s;
+                Debug.dprintf4 debug "[LRA/Simplex] Found %a = %a" Node.pp from
+                  Node.pp to_;
+                Egraph.merge d from to_;
+                test_equality env))
   in
-  let l = [ Node.S.elements vars ] in
-  let l = partition l s in
-  test_equality env l
+  update_under_approx s;
+  test_equality env
 
 let update_domains d env =
   let update_domain n ((v : Sim.Core.var_info), _) =
@@ -385,7 +390,7 @@ let simplex (d : Egraph.wt) =
   Datastructure.Ref.set scheduled d false;
   let eqs, vars =
     Datastructure.Push.fold comparisons d ~f:(make_equations d)
-      ~init:([], Node.S.empty)
+      ~init:(Polynome.H.create 10, Node.S.empty)
   in
   let env = Sim.Core.empty ~is_int:false ~check_invs:false ~debug:0 in
   let of_poly (p : Polynome.t) =
@@ -393,49 +398,47 @@ let simplex (d : Egraph.wt) =
       (fun n r acc -> fst (Sim.Core.P.accumulate n r acc))
       p.poly Sim.Core.P.empty
   in
-  let of_bound_sup q = function
-    | Bound.Large -> (q, Q.zero)
-    | Strict -> (q, Q.minus_one)
-  in
-  let of_bound_inf q = function
-    | Bound.Large -> (q, Q.zero)
-    | Strict -> (q, Q.one)
-  in
   let add_vars env v =
     let inf, sup =
       match Egraph.get_dom d Dom_interval.dom v with
-      | None -> (None, None)
-      | Some d -> Dom_interval.D.get_convexe_hull d
+      | None -> (No, No)
+      | Some d ->
+          let conv = function
+            | None -> No
+            | Some (q, Bound.Large) -> Large q
+            | Some (q, Bound.Strict) -> Strict q
+          in
+          let inf, sup = Dom_interval.D.get_convexe_hull d in
+          (conv inf, conv sup)
     in
-    let app f = function None -> None | Some (q, b) -> Some (f q b) in
     let env, _ =
-      Sim.Assert.var env v (app of_bound_inf inf) () (app of_bound_sup sup) ()
+      Sim.Assert.var env v (of_bound_inf inf) () (of_bound_sup sup) ()
     in
     env
   in
   let env = Node.S.fold_left add_vars env vars in
   (* Debug.dprintf2 debug "[Simplex] after_adding variables: %a"
    *   (Sim.Core.print Unknown) env; *)
-  let add_eqs env eq =
-    assert (not (Node.M.is_empty eq.p.poly));
-    if Node.M.is_num_elt 1 eq.p.poly then
-      let n, q = Node.M.choose eq.p.poly in
-      if Q.sign q < 0 then
-        let b = of_bound_inf (Q.neg (Q.div eq.p.cst q)) eq.bound in
-        let env, _ = Sim.Assert.var env n (Some b) () None () in
-        env
-      else
-        let b = of_bound_sup (Q.neg (Q.div eq.p.cst q)) eq.bound in
-        let env, _ = Sim.Assert.var env n None () (Some b) () in
-        env
+  let add_eqs _ eq (env, vars) =
+    assert (not (Node.M.is_empty eq.p));
+    Debug.dprintf2 debug "%a@." pp_eq eq;
+    if Node.M.is_num_elt 1 eq.p then (
+      let n, q = Node.M.choose eq.p in
+      assert (Q.equal q Q.one);
+      let inf = of_bound_inf eq.inf in
+      let sup = of_bound_sup eq.sup in
+      let env, _ = Sim.Assert.var env n inf () sup () in
+      (env, vars))
     else
-      let b = of_bound_sup (Q.neg eq.p.cst) eq.bound in
-      let p = Polynome.sub_cst eq.p eq.p.cst in
+      let inf = of_bound_inf eq.inf in
+      let sup = of_bound_sup eq.sup in
+      let p = Polynome.of_map Q.zero eq.p in
       let n = Dom_polynome.node_of_polynome d p in
-      let env, _ = Sim.Assert.poly env (of_poly p) n None () (Some b) () in
-      env
+      let vars = Node.S.add n vars in
+      let env, _ = Sim.Assert.poly env (of_poly p) n inf () sup () in
+      (env, vars)
   in
-  let env = List.fold_left add_eqs env eqs in
+  let env, vars = Polynome.H.fold add_eqs eqs (env, vars) in
   let env = Sim.Solve.solve env in
   let res = Sim.Result.get None env in
   (* Debug.dprintf2 debug "[Simplex] Solving done: %a" (Sim.Core.print res) env; *)
@@ -448,7 +451,9 @@ let simplex (d : Egraph.wt) =
       let env = find_equalities d vars env s in
       update_domains d env
 
-module Daemon = struct
+let simplex d = Debug.add_time_during stats_time (fun () -> simplex d)
+
+module DaemonSimplex = struct
   let key =
     Events.Dem.create
       (module struct
@@ -475,25 +480,68 @@ module Daemon = struct
   let run d () = simplex d
 end
 
-let () = Events.register (module Daemon)
+let () = Events.register (module DaemonSimplex)
+
+let ord_inv = function
+  | Expr.Lt -> Expr.Gt
+  | Expr.Leq -> Expr.Geq
+  | Expr.Geq -> Expr.Leq
+  | Expr.Gt -> Expr.Lt
+  | _ -> assert false
+
+let ord_neg = function
+  | Expr.Lt -> Expr.Geq
+  | Expr.Leq -> Expr.Gt
+  | Expr.Geq -> Expr.Lt
+  | Expr.Gt -> Expr.Leq
+  | _ -> assert false
+
+let dom_inter = function
+  | Expr.Lt -> Dom_interval.D.lt
+  | Expr.Leq -> Dom_interval.D.le
+  | Expr.Gt -> Dom_interval.D.gt
+  | Expr.Geq -> Dom_interval.D.ge
+  | _ -> assert false
+
+let build = function
+  | Expr.Lt -> LRA_build.lt
+  | Expr.Leq -> LRA_build.le
+  | Expr.Gt -> LRA_build.gt
+  | Expr.Geq -> LRA_build.ge
+  | _ -> assert false
 
 (** {2 Initialization} *)
 let converter d (f : Ground.t) =
   match Ground.sem f with
   | {
-   app = { builtin = Expr.Lt | Expr.Leq | Expr.Geq | Expr.Gt };
+   app = { builtin = (Expr.Lt | Expr.Leq | Expr.Geq | Expr.Gt) as builtin };
    tyargs = [];
    args;
   } ->
-      let attach n =
-        Dom_polynome.events_repr_change d ~node:n Daemon.enqueue;
-        Events.attach_repr d n Daemon.enqueue
+      let n, builtin =
+        let a, b = IArray.extract2_exn args in
+        (* Special case needed for avoiding infinite loop *)
+        if Node.equal a RealValue.zero then (b, ord_inv builtin)
+        else if Node.equal b RealValue.zero then (a, builtin)
+        else
+          let a, b, builtin =
+            if Node.compare a b < 0 then (a, b, builtin)
+            else (b, a, ord_inv builtin)
+          in
+          (LRA_build.sub d a b, builtin)
       in
-      IArray.iter ~f:attach args;
-      Events.attach_value d (Ground.node f) Boolean.dom (fun d n _ ->
-          Daemon.enqueue d n);
-      Datastructure.Push.push comparisons d f;
-      Events.new_pending_daemon d Daemon.key ();
+      Egraph.register d n;
+
+      let n' = build builtin d n RealValue.zero in
+      Egraph.register d n';
+      Egraph.merge d (Ground.node f) n';
+
+      Dom_polynome.events_repr_change d ~node:n DaemonSimplex.enqueue;
+      Events.attach_repr d n DaemonSimplex.enqueue;
+      Events.attach_dom d n Dom_interval.dom DaemonSimplex.enqueue;
+
+      Datastructure.Push.push comparisons d n;
+      Events.new_pending_daemon d DaemonSimplex.key ();
       Choice.register d f (Boolean.chobool (Ground.node f))
   | _ -> ()
 
diff --git a/src_colibri2/theories/LRA/stages/bound.ml b/src_colibri2/theories/LRA/stages/bound.ml
index b2169edcd..18540b121 100644
--- a/src_colibri2/theories/LRA/stages/bound.ml
+++ b/src_colibri2/theories/LRA/stages/bound.ml
@@ -1,30 +1,31 @@
 module T = struct
-  type t =
-    | Strict | Large
-  [@@deriving eq, ord, hash]
+  type t = Strict | Large [@@deriving eq, ord, hash]
 
   let pp fmt = function
     | Strict -> CCFormat.string fmt "<"
     | Large -> CCFormat.string fmt "≤"
-
 end
 
 include T
-include Colibri2_popop_lib.Popop_stdlib.MkDatatype(T)
+include Colibri2_popop_lib.Popop_stdlib.MkDatatype (T)
 
-let compare_inf b1 b2 = match b1,b2 with
+let compare_inf b1 b2 =
+  match (b1, b2) with
   | Large, Strict -> -1
   | Strict, Large -> 1
   | Large, Large | Strict, Strict -> 0
 
-let compare_inf_sup b1 b2 = match b1,b2 with
+let compare_inf_sup b1 b2 =
+  match (b1, b2) with
   | Large, Strict -> 1
   | Strict, Large -> 1
   | Large, Large -> 0
   | Strict, Strict -> 1
 
-let compare_sup b1 b2 = - (compare_inf b1 b2)
+let compare_sup b1 b2 = -compare_inf b1 b2
 
 let compare_bounds_inf = CCOrd.pair Q.compare compare_inf
+
 let compare_bounds_sup = CCOrd.pair Q.compare compare_sup
+
 let compare_bounds_inf_sup = CCOrd.pair Q.compare compare_inf_sup
diff --git a/src_colibri2/theories/LRA/stages/stage1/interval.ml b/src_colibri2/theories/LRA/stages/stage1/interval.ml
index febd3c046..fc23775b0 100644
--- a/src_colibri2/theories/LRA/stages/stage1/interval.ml
+++ b/src_colibri2/theories/LRA/stages/stage1/interval.ml
@@ -22,51 +22,77 @@ open Colibri2_popop_lib
 open Colibri2_stdlib.Std
 
 module Convexe = struct
-
   include Colibrics_lib.Interval.Convexe
 
-  include (struct
-    type bound = Colibrics_lib.Interval.Bound.t =
-      | Strict | Large
+  include struct
+    type bound = Colibrics_lib.Interval.Bound.t = Strict | Large
     [@@deriving eq, ord, hash]
 
-    type t = Colibrics_lib.Interval.Convexe.t = private
+    type t = Colibrics_lib.Interval.Convexe.t =
       | Finite of Q.t * bound * Q.t * bound
       | InfFinite of Q.t * bound
       | FiniteInf of Q.t * bound
       | Inf
-    [@@ deriving eq, ord, hash]
-  end)
+    [@@deriving eq, ord, hash]
+  end
 
   let pp fmt t =
     let print_bound_left fmt = function
-      | Large  -> Format.fprintf fmt "["
-      | Strict -> Format.fprintf fmt "]" in
+      | Large -> Format.fprintf fmt "["
+      | Strict -> Format.fprintf fmt "]"
+    in
     let print_bound_right fmt = function
-      | Large  -> Format.fprintf fmt "]"
-      | Strict -> Format.fprintf fmt "[" in
+      | Large -> Format.fprintf fmt "]"
+      | Strict -> Format.fprintf fmt "["
+    in
     match t with
-    | Finite (q1,b1,q2,b2) ->
-      Format.fprintf fmt "%a%a;%a%a"
-        print_bound_left b1 Q.pp q1
-        Q.pp q2 print_bound_right b2
-    | InfFinite (q1,b1) ->
-      Format.fprintf fmt "]-∞;%a%a"
-        Q.pp q1 print_bound_right b1
-    | FiniteInf(q1,b1) ->
-      Format.fprintf fmt "%a%a;+∞["
-        print_bound_left b1 Q.pp q1
-    | Inf ->
-      Format.fprintf fmt "]-∞;+∞["
+    | Finite (q1, b1, q2, b2) ->
+        Format.fprintf fmt "%a%a;%a%a" print_bound_left b1 Q.pp q1 Q.pp q2
+          print_bound_right b2
+    | InfFinite (q1, b1) ->
+        Format.fprintf fmt "]-∞;%a%a" Q.pp q1 print_bound_right b1
+    | FiniteInf (q1, b1) ->
+        Format.fprintf fmt "%a%a;+∞[" print_bound_left b1 Q.pp q1
+    | Inf -> Format.fprintf fmt "]-∞;+∞["
 
-  include Popop_stdlib.MkDatatype(struct
-      type nonrec t = t
-      let equal = equal let compare = compare
-      let hash = hash let hash_fold_t = hash_fold_t let pp = pp
-    end)
+  include Popop_stdlib.MkDatatype (struct
+    type nonrec t = t
+
+    let equal = equal
+
+    let compare = compare
+
+    let hash = hash
+
+    let hash_fold_t = hash_fold_t
+
+    let pp = pp
+  end)
 
   let invariant _ = true (* proved in why3 *)
 
+  let of_bound : Colibrics_lib.Interval.Bound.t -> Bound.t = function
+    | Strict -> Strict
+    | Large -> Large
+
+  let to_bound : Bound.t -> Colibrics_lib.Interval.Bound.t = function
+    | Strict -> Strict
+    | Large -> Large
+
+  let get_convexe_hull : t -> (Q.t * Bound.t) option * (Q.t * Bound.t) option =
+    function
+    | Finite (q1, b1, q2, b2) -> (Some (q1, of_bound b1), Some (q2, of_bound b2))
+    | InfFinite (q, b) -> (None, Some (q, of_bound b))
+    | FiniteInf (q, b) -> (Some (q, of_bound b), None)
+    | Inf -> (None, None)
+
+  let from_convexe_hull (c1, c2) : t =
+    match (c1, c2) with
+    | None, None -> Inf
+    | Some (q1, b1), None -> FiniteInf (q1, to_bound b1)
+    | None, Some (q1, b1) -> InfFinite (q1, to_bound b1)
+    | Some (q1, b1), Some (q2, b2) -> Finite (q1, to_bound b1, q2, to_bound b2)
+
   (* let nb_incr = 100
    * let z_nb_incr = Z.of_int nb_incr
    * let choose_rnd rnd = function
@@ -113,7 +139,6 @@ module Convexe = struct
    *     assert (invariant t);
    *     Some t
    *   end *)
-
 end
 (*
 module ConvexeWithDivider = struct
diff --git a/src_colibri2/theories/bool/equality.ml b/src_colibri2/theories/bool/equality.ml
index 43567cacd..7f1de41ec 100644
--- a/src_colibri2/theories/bool/equality.ml
+++ b/src_colibri2/theories/bool/equality.ml
@@ -473,38 +473,56 @@ let bool_init_diff_to_value d =
     ~already_registered:[ Boolean.value_true; Boolean.value_false ]
 
 (** {2 Interpretations} *)
-let init_check d =
-  Interp.Register.check d (fun d t ->
-      let interp n = Opt.get_exn Impossible (Egraph.get_value d n) in
-      let ( !> ) n =
-        Boolean.BoolValue.value (Boolean.BoolValue.coerce_nodevalue (interp n))
-      in
-      let check r =
-        Interp.check_of_bool (Value.equal r (interp (Ground.node t)))
-      in
-      let ( !< ) b =
-        let r = if b then Boolean.values_true else Boolean.values_false in
-        check r
-      in
-      match Ground.sem t with
-      | { app = { builtin = Expr.Equal }; tyargs = [ _ ]; args; ty = _ } ->
-          assert (IArray.not_empty args);
-          !<(IArray.for_alli_non_empty_exn ~init:interp
-               ~f:(fun _ a t -> Value.equal a (interp t))
-               args)
-      | { app = { builtin = Expr.Equiv }; tyargs = [ _ ]; args; ty = _ } ->
-          let a, b = IArray.extract2_exn args in
-          !<(Value.equal (interp a) (interp b))
-      | { app = { builtin = Expr.Distinct }; tyargs = [ _ ]; args; ty = _ } -> (
-          try
-            let fold acc v = Value.S.add_new Exit (interp v) acc in
-            let _ = IArray.fold ~f:fold ~init:Value.S.empty args in
-            check Boolean.values_true
-          with Exit -> check Boolean.values_false)
-      | { app = { builtin = Expr.Ite }; tyargs = [ _ ]; args; ty = _ } ->
-          let c, a, b = IArray.extract3_exn args in
-          check (if !>c then interp a else interp b)
-      | _ -> NA)
+module Check = struct
+  let interp d n = Opt.get_exn Impossible (Egraph.get_value d n)
+
+  let compute_ground d t =
+    let ( !> ) n =
+      Boolean.BoolValue.value (Boolean.BoolValue.coerce_nodevalue (interp d n))
+    in
+    let check r = Some r in
+    let ( !< ) b =
+      let r = if b then Boolean.values_true else Boolean.values_false in
+      check r
+    in
+    match Ground.sem t with
+    | { app = { builtin = Expr.Equal }; tyargs = [ _ ]; args; ty = _ } ->
+        assert (IArray.not_empty args);
+        !<(IArray.for_alli_non_empty_exn ~init:(interp d)
+             ~f:(fun _ a t -> Value.equal a (interp d t))
+             args)
+    | { app = { builtin = Expr.Equiv }; tyargs = [ _ ]; args; ty = _ } ->
+        let a, b = IArray.extract2_exn args in
+        !<(Value.equal (interp d a) (interp d b))
+    | { app = { builtin = Expr.Distinct }; tyargs = [ _ ]; args; ty = _ } -> (
+        try
+          let fold acc v = Value.S.add_new Exit (interp d v) acc in
+          let _ = IArray.fold ~f:fold ~init:Value.S.empty args in
+          check Boolean.values_true
+        with Exit -> check Boolean.values_false)
+    | { app = { builtin = Expr.Ite }; tyargs = [ _ ]; args; ty = _ } ->
+        let c, a, b = IArray.extract3_exn args in
+        check (if !>c then interp d a else interp d b)
+    | _ -> None
+
+  let init_check d =
+    Interp.Register.check d (fun d t ->
+        let check r =
+          Interp.check_of_bool (Value.equal r (interp d (Ground.node t)))
+        in
+        match compute_ground d t with None -> NA | Some v -> check v);
+    Interp.Register.compute d (fun d t ->
+        match compute_ground d t with None -> NA | Some v -> Value v)
+
+  let attach d g =
+    let f d g =
+      match compute_ground d g with
+      | None -> raise Impossible
+      | Some v -> Egraph.set_value d (Ground.node g) v
+    in
+
+    Interp.WatchArgs.create d f g
+end
 
 let converter d (t : Ground.t) =
   let res = Ground.node t in
@@ -522,15 +540,19 @@ let converter d (t : Ground.t) =
   in
   match Ground.sem t with
   | { app = { builtin = Expr.Equal }; tyargs = [ _ ]; args = l; _ } ->
+      Check.attach d t;
       IArray.iter ~f:reg l;
       reg_and_merge (equality_iter mark d (IArray.to_list l))
   | { app = { builtin = Expr.Equiv }; tyargs = [ _ ]; args; _ } ->
+      Check.attach d t;
       let a, b = IArray.extract2_exn args in
       reg_and_merge (equality_iter mark d [ of_term a; of_term b ])
   | { app = { builtin = Expr.Distinct }; tyargs = [ _ ]; args; _ } ->
+      Check.attach d t;
       IArray.iter ~f:reg args;
       reg_and_merge (disequality d (IArray.to_list args))
   | { app = { builtin = Expr.Ite }; tyargs = [ _ ]; args; _ } ->
+      Check.attach d t;
       let c, a, b = IArray.extract3_exn args in
       new_ite d res (of_term c) a b
   | { app = { builtin = Expr.Xor }; tyargs = []; args; _ } ->
@@ -542,7 +564,7 @@ let th_register env =
   attach_new_equalities env;
   Ground.register_converter env converter;
   bool_init_diff_to_value env;
-  init_check env;
+  Check.init_check env;
   ()
 
 let () = Init.add_default_theory th_register
diff --git a/src_colibri2/theories/quantifier/common.ml b/src_colibri2/theories/quantifier/common.ml
index 0bf08501f..78b5f18a5 100644
--- a/src_colibri2/theories/quantifier/common.ml
+++ b/src_colibri2/theories/quantifier/common.ml
@@ -26,13 +26,10 @@ let debug =
 let debug_full =
   Debug.register_flag ~desc:"Handling of quantifiers full" "quantifiers.full"
 
-let nb_instantiation = Debug.register_stats_int ~name:"instantiation" ~init:0
+let nb_instantiation = Debug.register_stats_int "instantiation"
 
-let nb_eager_instantiation =
-  Debug.register_stats_int ~name:"eager_instantiation" ~init:0
+let nb_eager_instantiation = Debug.register_stats_int "eager_instantiation"
 
-let nb_delayed_instantiation =
-  Debug.register_stats_int ~name:"delayed_instantiation" ~init:0
+let nb_delayed_instantiation = Debug.register_stats_int "delayed_instantiation"
 
-let nb_new_instantiation =
-  Debug.register_stats_int ~name:"new_instantiation" ~init:0
+let nb_new_instantiation = Debug.register_stats_int "new_instantiation"
-- 
GitLab