diff --git a/src_colibri2/theories/LRA/dom_polynome.ml b/src_colibri2/theories/LRA/dom_polynome.ml
index 4ee192231889c6f459e7dc0d7625bf69ca796f0b..73396151e323e93dc4f721a317d8306806b37be4 100644
--- a/src_colibri2/theories/LRA/dom_polynome.ml
+++ b/src_colibri2/theories/LRA/dom_polynome.ml
@@ -22,205 +22,85 @@ open Colibri2_popop_lib
 open Colibri2_core
 open Colibri2_stdlib.Std
 
-let debug = Debug.register_info_flag
-  ~desc:"for the arithmetic theory of polynome"
-  "LRA.polynome"
-
-let dom = Dom.Kind.create (module struct type t = Polynome.t let name = "ARITH_POLY" end)
+let debug =
+  Debug.register_info_flag ~desc:"for the arithmetic theory of polynome"
+    "LRA.polynome"
 
 module T = struct
   include Polynome
+
   let name = "SARITH_POLY"
 end
 
-module ThE = ThTerm.Register(T)
+module ThE = ThTerm.Register (T)
 
 let node_of_polynome t = ThE.node (ThE.index t)
 
-let used_in_poly : Node.t Bag.t Node.HC.t = Node.HC.create (Bag.pp Pp.semi Node.pp) "used_in_poly"
-
-let set_poly d cl p =
-  Egraph.set_dom d dom cl p;
-  match Polynome.is_one_node p with
-  | None -> Egraph.set_thterm d cl (ThE.thterm (ThE.index p))
-  | Some cl' -> Egraph.merge d cl cl'
-
-let add_used d cl' new_cl =
-  Node.M.iter (fun used _ ->
-      Node.HC.change (function
-          | Some b -> Some (Bag.append b cl')
-          | None ->
-            begin match Egraph.get_dom d dom used with
-              | None ->
-                (** If a used node have no polynome associated, we set it to
-                   itself. This allows to be warned when this node is merged. It
-                   is the reason why this module doesn't specifically wait for
-                   representative change *)
-                Egraph.set_dom d dom used (Polynome.monome Q.one used)
-              | Some p ->
-                assert (Polynome.equal (Polynome.monome Q.one used) p)
-            end;
-            Some (Bag.elt cl')
-        ) used_in_poly d used
-    ) new_cl
-
-let subst_doms d cl (p:Polynome.t) =
-  let b = match Node.HC.find used_in_poly d cl with
-    | exception Not_found -> Bag.empty
-    | b -> b
-  in
-  Bag.iter (fun cl' ->
-      match Egraph.get_dom d dom cl' with
-      | None -> assert false (** absurd: can't be used and absent *)
-      | Some q ->
-        let new_cl = Node.M.set_diff p.poly q.poly in
-        let q, _ = Polynome.subst q cl p in
-        add_used d cl' new_cl;
-        set_poly d cl' q
-    ) b;
-  add_used d cl p.poly;
-  set_poly d cl p
-
-module Th = struct
+include Pivot.Total (struct
   include Polynome
 
-  let merged v1 v2 =
-    match v1,v2 with
-    | None, None -> true
-    | Some v', Some v -> equal v' v
-    | _ -> false
-
-    let norm_dom cl = function
-      | None ->
-        let r = monome Q.one cl in
-        r
-      | Some p ->
-        p
-
-    let add_itself d cl norm =
-      add_used d cl norm.poly;
-      Egraph.set_dom d dom cl norm
-
-    let merge d ((p1o,cl1) as a1) ((p2o,cl2) as a2) inv =
-      assert (not (Egraph.is_equal d cl1 cl2));
-      assert (not (Option.is_none p1o && Option.is_none p2o));
-      let (pother, other), (prepr, repr) = if inv then a2,a1 else a1,a2 in
-      let other = Egraph.find d other in
-      let repr = Egraph.find d repr in
-      let p1 = norm_dom other pother in
-      let p2 = norm_dom repr prepr in
-      let diff = sub p1 p2 in
-      (* 0 = other - repr = p1 - p2 = diff *)
-      Debug.dprintf2 debug "[Arith] @[solve 0=%a@]" pp diff;
-      begin match Polynome.extract diff with
-      | Zero -> (** no new equality already equal *)
-        begin
-          match pother, prepr with
-          | Some _, Some _ | None, None ->
-            assert false (** absurd: no need of merge *)
-          | Some p, None ->
-            (** p = repr *)
-            add_itself d repr p
-          | None, Some p ->
-            (** p = other *)
-            add_itself d other p
-        end
-      | Cst c ->
-        (* 0 = cst <> 0 *)
-        Debug.dprintf6 Debug.contradiction
-          "[LRA/Poly] Found 0 = %a when merging %a and %a"
-          Q.pp c
-          Node.pp cl1 Node.pp cl2;
-        Egraph.contradiction d
-      | Var(q,x,p') ->
-        (** diff = qx + p' *)
-        assert ( not (Q.equal Q.zero q) );
-        Debug.dprintf2 debug "[Arith] @[pivot %a@]" Node.pp x;
-        let add_if_default n norm = function
-          | Some _ -> ()
-          | None ->
-            add_itself d n norm
-        in
-        add_if_default other p1 pother;
-        add_if_default repr p2 prepr;
-        subst_doms d x (Polynome.mult_cst (Q.div Q.one (Q.neg q)) p')
-      end;
-      assert (Option.compare Polynome.compare
-                (Egraph.get_dom d dom repr)
-                (Egraph.get_dom d dom other) = 0)
-
-    let solve_one d cl p1 =
-      match Egraph.get_dom d dom cl with
-      | None ->
-        subst_doms d cl p1
-      | Some p2 ->
-        let diff = Polynome.sub p1 p2 in
-        (* 0 = p1 - p2 = diff *)
-        Debug.dprintf8 debug "[Arith] @[solve in init %a 0=(%a)-(%a)=%a@]"
-          Node.pp cl Polynome.pp p1 Polynome.pp p2 Polynome.pp diff;
-        begin match Polynome.extract diff with
-          | Zero -> ()
-          | Cst c ->
-            (* 0 = cst <> 0 *)
-            Debug.dprintf4 Debug.contradiction
-              "[LRA/Poly] Found 0 = %a when updating %a"
-              Q.pp c
-              Node.pp cl;
-            Egraph.contradiction d
-          | Var(q,x,p') ->
-            (** diff = qx + p' *)
-            assert ( not (Q.equal Q.zero q) );
-            Debug.dprintf2 debug "[Arith] @[pivot %a@]" Node.pp x;
-            subst_doms d x (Polynome.mult_cst (Q.div Q.one (Q.neg q)) p')
-        end
-
-    let key = dom
-end
+  let name = "LRA.polynome"
+
+  type data = Q.t
 
-let () = Dom.register(module Th)
+  let nodes p = p.poly
 
-let norm d (p:Polynome.t) =
-  let add acc cl c =
-    let cl = Egraph.find_def d cl in
-    match Egraph.get_dom d dom cl with
-    | None -> Polynome.add acc (Polynome.monome c cl)
-    | Some p -> Polynome.x_p_cy acc c p
-  in
-  Polynome.fold add (Polynome.cst p.cst) p
+  let of_one_node n = monome Q.one n
 
-let assume_poly_equality d n (p:Polynome.t) =
-  (* Debug.dprintf4 debug "assume1: %a = %a" Node.pp n Polynome.pp p; *)
-  let n = Egraph.find_def d n in
-  let p = norm d p in
-  (* Debug.dprintf4 debug "assume2: %a = %a" Node.pp n Polynome.pp p; *)
-  Th.solve_one d n p
+  let subst p n q = fst (subst p n q)
+
+  let normalize p ~f =
+    let add acc cl c = Polynome.x_p_cy acc c (f cl) in
+    Polynome.fold add (Polynome.cst p.cst) p
+
+  let set d cl p =
+    match is_one_node p with
+    | None -> Egraph.set_thterm d cl (ThE.thterm (ThE.index p))
+    | Some cl' -> Egraph.merge d cl cl'
+
+  let solve p1 p2 =
+    let diff = sub p1 p2 in
+    (* 0 = other - repr = p1 - p2 = diff *)
+    Debug.dprintf2 debug "[Arith] @[solve 0=%a@]" pp diff;
+    match Polynome.extract diff with
+    | Zero -> Pivot.AlreadyEqual
+    | Cst c ->
+        (* 0 = cst <> 0 *)
+        Debug.dprintf2 Debug.contradiction
+          "[LRA/Poly] Found 0 = %a when merging" Q.pp c;
+        Contradiction
+    | Var (q, x, p') ->
+        assert (not (Q.equal Q.zero q));
+        (* diff = qx + p' *)
+        Subst (x, Polynome.mult_cst (Q.div Q.one (Q.neg q)) p')
+end)
 
 (** {2 Initialization} *)
-let converter d (f:Ground.t) =
-    let res = Ground.node f in
+let converter d (f : Ground.t) =
+  let res = Ground.node f in
   let reg n = Egraph.register d n in
   match Ground.sem f with
-  | { app = {builtin = Expr.Add}; tyargs = []; args; _ } ->
-    let a,b = IArray.extract2_exn args in
-    reg a; reg b;
-    assume_poly_equality d res (Polynome.of_list Q.zero [a,Q.one;b,Q.one])
-  | { app = {builtin = Expr.Sub}; tyargs = []; args; _ } ->
-    let a,b = IArray.extract2_exn args in
-    reg a; reg b;
-    assume_poly_equality d res (Polynome.of_list Q.zero [a,Q.one;b,Q.minus_one])
-  | { app = {builtin = Expr.Minus}; tyargs = []; args; _ } ->
-    let a = IArray.extract1_exn args in
-    reg a;
-    assume_poly_equality d res (Polynome.of_list Q.zero [a,Q.minus_one])
+  | { app = { builtin = Expr.Add }; tyargs = []; args; _ } ->
+      let a, b = IArray.extract2_exn args in
+      reg a;
+      reg b;
+      assume_equality d res (Polynome.of_list Q.zero [ (a, Q.one); (b, Q.one) ])
+  | { app = { builtin = Expr.Sub }; tyargs = []; args; _ } ->
+      let a, b = IArray.extract2_exn args in
+      reg a;
+      reg b;
+      assume_equality d res
+        (Polynome.of_list Q.zero [ (a, Q.one); (b, Q.minus_one) ])
+  | { app = { builtin = Expr.Minus }; tyargs = []; args; _ } ->
+      let a = IArray.extract1_exn args in
+      reg a;
+      assume_equality d res (Polynome.of_list Q.zero [ (a, Q.minus_one) ])
   | _ -> ()
 
 let init env =
-    Daemon.attach_reg_value env
-    RealValue.key
-    (fun d value ->
-       let v = RealValue.value value in
-       let cl = RealValue.node value in
-       let p1 = Polynome.of_list v [] in
-       assume_poly_equality d cl p1
-    );
-    Ground.register_converter env converter
+  Daemon.attach_reg_value env RealValue.key (fun d value ->
+      let v = RealValue.value value in
+      let cl = RealValue.node value in
+      let p1 = Polynome.of_list v [] in
+      assume_equality d cl p1);
+  Ground.register_converter env converter
diff --git a/src_colibri2/theories/LRA/dom_polynome.mli b/src_colibri2/theories/LRA/dom_polynome.mli
index ef3a5d7b07be91f7b2dd8afbc9f027b1f9a6804a..b88d42211bc103a39df7fcbca3e15592e400c2d7 100644
--- a/src_colibri2/theories/LRA/dom_polynome.mli
+++ b/src_colibri2/theories/LRA/dom_polynome.mli
@@ -18,12 +18,18 @@
 (*  for more details (enclosed in the file licenses/LGPLv2.1).           *)
 (*************************************************************************)
 
-val assume_poly_equality: Egraph.wt -> Node.t -> Polynome.t -> unit
+val assume_equality : Egraph.wt -> Node.t -> Polynome.t -> unit
 
-val dom: Polynome.t Dom.Kind.t
+val get_repr : _ Egraph.t -> Node.t -> Polynome.t option
 
-val init: Egraph.wt -> unit
+val attach_repr_change :
+  _ Egraph.t -> ?node:Node.t -> (Egraph.wt -> Node.t -> unit) -> unit
 
-val node_of_polynome: Polynome.t -> Node.t
+val events_repr_change :
+  _ Egraph.t -> ?node:Node.t -> (Egraph.rt -> Node.t -> Events.enqueue) -> unit
 
-val norm: _ Egraph.t -> Polynome.t -> Polynome.t
+val init : Egraph.wt -> unit
+
+val node_of_polynome : Polynome.t -> Node.t
+
+val normalize : _ Egraph.t -> Polynome.t -> Polynome.t
diff --git a/src_colibri2/theories/LRA/dom_product.ml b/src_colibri2/theories/LRA/dom_product.ml
index fbb41f95db08eb883e5f33e8f8a05c9029eebe44..63fd43051d9d8bf28a5be8b9b6666eb63af1b1c2 100644
--- a/src_colibri2/theories/LRA/dom_product.ml
+++ b/src_colibri2/theories/LRA/dom_product.ml
@@ -145,7 +145,7 @@ let factorize res a coef b d _ =
         Debug.dprintf10 debug "[Factorize] %a = %a + %a * %a into %a" Node.pp
           res Node.pp a Q.pp coef Node.pp b Polynome.pp p;
         Egraph.register d n;
-        Dom_polynome.assume_poly_equality d n p;
+        Dom_polynome.assume_equality d n p;
         assume_equality d res (Product.of_map (Node.M.add n Q.one common)))
   | _ -> ()
 
@@ -185,8 +185,8 @@ let converter d (f : Ground.t) =
 
 let init env =
   init env;
-  Daemon.attach_any_dom env Dom_polynome.dom (fun d n ->
-      match Egraph.get_dom d Dom_polynome.dom n with
+  Dom_polynome.attach_repr_change env (fun d n ->
+      match Dom_polynome.get_repr d n with
       | None -> ()
       | Some p ->
           if Q.equal p.cst Q.zero && Node.M.is_num_elt 1 p.poly then
@@ -204,5 +204,5 @@ let init env =
               let p1 = Polynome.of_list Q.zero [ (cl, Q.one) ] in
               Debug.dprintf4 debug "[Product->Polynome] propagate %a is %a"
                 Node.pp n Node.pp cl;
-              Dom_polynome.assume_poly_equality d n p1)));
+              Dom_polynome.assume_equality d n p1)));
   Ground.register_converter env converter
diff --git a/src_colibri2/theories/LRA/fourier.ml b/src_colibri2/theories/LRA/fourier.ml
index 672d8571a9ef125ad5420dba02ac143900cca103..0ff795e9cee0f7ea421ec19ef4cf7f0370dd61b6 100644
--- a/src_colibri2/theories/LRA/fourier.ml
+++ b/src_colibri2/theories/LRA/fourier.ml
@@ -22,218 +22,235 @@ open Colibri2_popop_lib
 open Colibri2_core
 
 let comparisons = Datastructure.Push.create Ground.pp "LRA.fourier.comparisons"
-let scheduled = Datastructure.Ref.create Base.Bool.pp "LRA.fourier.scheduled" false
 
-let debug = Debug.register_info_flag ~desc:"Reasoning about <= < in LRA" "fourier"
+let scheduled =
+  Datastructure.Ref.create Base.Bool.pp "LRA.fourier.scheduled" false
 
-let stats_run = Debug.register_stats_int ~name:"Fourier.run" ~init:0
+let debug =
+  Debug.register_info_flag ~desc:"Reasoning about <= < in LRA" "fourier"
 
+let stats_run = Debug.register_stats_int ~name:"Fourier.run" ~init:0
 
-type eq = { p: Polynome.t; bound: Bound.t; origins: Ground.S.t }
+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) =
+let divide d (p : Polynome.t) =
   try
-    begin match Polynome.is_cst p with
-      | None -> ()
-      | Some _ -> raise Exit
-    end;
+    (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.get_repr d e with
-        | None when Egraph.is_equal d RealValue.zero e -> acc
-        | None -> raise Exit
-        | Some p -> (p,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;
+    let l =
+      List.fold_left
+        (fun acc (e, q) ->
+          match Dom_product.get_repr d e with
+          | None when Egraph.is_equal d RealValue.zero e -> acc
+          | None -> raise Exit
+          | Some p -> (p, 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 (p,_) ->
-        Node.M.inter (fun _ a b -> if Q.equal a b then Some a else None) acc p.Product.poly)
-        hd.Product.poly l in
-    let common, pos = Node.M.fold_left
-        (fun (acc,pos) n v ->
-           if Dom_interval.is_strictly_positive d n
-           then (Node.M.add n v acc,pos)
-           else if Dom_interval.is_strictly_negative d n
-           then (Node.M.add n v acc,not pos)
-           else (acc,pos)
-        ) (Node.M.empty,true) common
-    in
-    if Node.M.is_empty common then raise Exit;
-    Debug.dprintf4 debug "[Fourier] found possible division: %a / %a"
-      Polynome.pp p (Node.M.pp Q.pp) common;
-    let (cst,l) = List.fold_left (fun (cst,acc) (p,v) ->
-        let p = Product.of_map (Node.M.set_diff p.Product.poly common) in
+    | (hd, _) :: _ ->
+        let common =
+          List.fold_left
+            (fun acc (p, _) ->
+              Node.M.inter
+                (fun _ a b -> if Q.equal a b then Some a else None)
+                acc p.Product.poly)
+            hd.Product.poly l
+        in
+        let common, pos =
+          Node.M.fold_left
+            (fun (acc, pos) n v ->
+              if Dom_interval.is_strictly_positive d n then
+                (Node.M.add n v acc, pos)
+              else if Dom_interval.is_strictly_negative d n then
+                (Node.M.add n v acc, not pos)
+              else (acc, pos))
+            (Node.M.empty, true) common
+        in
+        if Node.M.is_empty common then raise Exit;
+        Debug.dprintf4 debug "[Fourier] found possible division: %a / %a"
+          Polynome.pp p (Node.M.pp Q.pp) common;
+        let cst, l =
+          List.fold_left
+            (fun (cst, acc) (p, v) ->
+              let p = Product.of_map (Node.M.set_diff p.Product.poly common) in
 
-        let v = if pos then v else Q.neg v in
-        match Product.classify p with
-        | ONE ->
-          (Q.add v cst,acc)
-        | NODE n ->
-          (cst,(n,v)::acc)
-        | PRODUCT ->
-          let n = Dom_product.node_of_product p in
-          (Q.add Q.one cst,(n,v)::acc)
-      ) (Q.zero,[]) l in
-    Some (Polynome.of_list cst l,common,pos)
-  with Exit ->
-    None
+              let v = if pos then v else Q.neg v in
+              match Product.classify p with
+              | ONE -> (Q.add v cst, acc)
+              | NODE n -> (cst, (n, v) :: acc)
+              | PRODUCT ->
+                  let n = Dom_product.node_of_product p in
+                  (Q.add Q.one cst, (n, v) :: acc))
+            (Q.zero, []) l
+        in
+        Some (Polynome.of_list cst l, common, pos)
+  with Exit -> None
 
 let delta d eq =
   (* add info to delta *)
   if Node.M.is_num_elt 2 eq.p.poly then
     match Node.M.bindings eq.p.poly 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
-    | [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
+    | [ (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
+    | [ (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
     | _ -> ()
 
 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 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
   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
+  | { 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
+  match (Polynome.is_cst p, bound) with
   | None, _ ->
-    let eq = { p; bound; origins } in
-    delta d eq;
-    eq::eqs
+      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_poly_equality d a (Polynome.of_list Q.minus_one [b,Q.one])
-            | Large ->
-              Dom_polynome.assume_poly_equality d a (Polynome.monome Q.one b)
-            | Strict ->
-              assert false
-          )) origins;
-    eqs
+      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 "[Fourier] discard %a" Ground.S.pp origins;
-    eqs
+      Debug.dprintf2 debug "[Fourier] discard %a" Ground.S.pp origins;
+      eqs
   | Some p, bound ->
-    Debug.dprintf4 Debug.contradiction
-      "[LRA/Fourier] Found %a%a0"
-      Q.pp p Bound.pp bound;
-    Egraph.contradiction d
+      Debug.dprintf4 Debug.contradiction "[LRA/Fourier] Found %a%a0" Q.pp p
+        Bound.pp bound;
+      Egraph.contradiction d
 
 let mk_eq d bound a b =
-  let (!) n =
-    match Egraph.get_dom d Dom_polynome.dom n with
+  let ( ! ) n =
+    match Dom_polynome.get_repr d n with
     | None -> Polynome.monome Q.one (Egraph.find_def d n)
-    | Some p -> p in
-  let p,bound' =
+    | Some p -> p
+  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
+    | 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)
   in
-  p,bound',
-  Polynome.sub (Polynome.monome Q.one a) (Polynome.monome Q.one b), bound
+  ( p,
+    bound',
+    Polynome.sub (Polynome.monome Q.one a) (Polynome.monome Q.one b),
+    bound )
 
-let make_equations d (eqs,vars) g =
+let make_equations d (eqs, vars) g =
   Debug.dprintf2 debug "[Fourier] %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 "[Fourier] %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.norm 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
+  | 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 "[Fourier] %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))
 
-let fm (d:Egraph.wt) =
+let fm (d : Egraph.wt) =
   Debug.dprintf0 debug "[Fourier]";
   Debug.incr stats_run;
   Datastructure.Ref.set scheduled d false;
-  let eqs, vars = Datastructure.Push.fold comparisons d
-      ~f:(make_equations d) ~init:([],Node.S.empty)
+  let eqs, vars =
+    Datastructure.Push.fold comparisons d ~f:(make_equations d)
+      ~init:([], Node.S.empty)
   in
   let rec split n positive negative absent = function
-    | [] -> (positive,negative,absent)
-    | eq::l ->
-      match Node.M.find_opt n eq.p.poly with
-      | None -> split n positive negative (eq::absent) l
-      | Some q ->
-        if Q.sign q > 0 then
-          split n ((q,eq)::positive) negative absent l
-        else split n positive ((q,eq)::negative) absent l
+    | [] -> (positive, negative, absent)
+    | eq :: l -> (
+        match Node.M.find_opt n eq.p.poly with
+        | None -> split n positive negative (eq :: absent) l
+        | Some q ->
+            if Q.sign q > 0 then split n ((q, eq) :: positive) negative absent l
+            else split n positive ((q, eq) :: negative) absent l)
   in
   let rec aux eqs vars =
     match Node.S.choose vars with
     | exception Not_found -> ()
-    | n ->
-      let vars = Node.S.remove n vars in
-      let positive, negative, absent = split n [] [] [] eqs in
-      match positive, negative with
-      | [], _ | _ , [] -> aux absent vars
-      | _, _ ->
-        let eqs =
-          Lists.fold_product (fun eqs (pq,peq) (nq,neq) ->
-              let q = Q.div pq (Q.neg nq) in
-              let p = Polynome.cx_p_cy Q.one peq.p q neq.p in
-              let bound =
-                match peq.bound, neq.bound with
-                | Large, Large -> Bound.Large
-                | _ -> Bound.Strict in
-              add_eq d eqs p bound (Ground.S.union peq.origins neq.origins)
-            ) absent positive negative
-        in
-        aux eqs vars
+    | n -> (
+        let vars = Node.S.remove n vars in
+        let positive, negative, absent = split n [] [] [] eqs in
+        match (positive, negative) with
+        | [], _ | _, [] -> aux absent vars
+        | _, _ ->
+            let eqs =
+              Lists.fold_product
+                (fun eqs (pq, peq) (nq, neq) ->
+                  let q = Q.div pq (Q.neg nq) in
+                  let p = Polynome.cx_p_cy Q.one peq.p q neq.p in
+                  let bound =
+                    match (peq.bound, neq.bound) with
+                    | Large, Large -> Bound.Large
+                    | _ -> Bound.Strict
+                  in
+                  add_eq d eqs p bound (Ground.S.union peq.origins neq.origins))
+                absent positive negative
+            in
+            aux eqs vars)
   in
   aux eqs vars
 
 module Daemon = struct
   let key =
     Events.Dem.create
-      ( module struct
+      (module struct
         type t = unit
+
         let name = "LRA.fourier"
-      end )
+      end)
 
   let enqueue d _ =
-    if Datastructure.Ref.get scheduled d
-    then Events.EnqAlready
-    else begin
+    if Datastructure.Ref.get scheduled d then Events.EnqAlready
+    else (
       Datastructure.Ref.set scheduled d true;
-      Events.EnqRun (key,(),None)
-    end
+      Events.EnqRun (key, (), None))
 
   let delay = Events.Delayed_by 64
 
@@ -246,21 +263,24 @@ end
 
 let () = Events.register (module Daemon)
 
-
 (** {2 Initialization} *)
-let converter d (f:Ground.t) =
+let converter d (f : Ground.t) =
   match Ground.sem f with
-  | { app = {builtin = (Expr.Lt|Expr.Leq|Expr.Geq|Expr.Gt)}; tyargs = []; args } ->
-    let attach n =
-      Events.attach_dom d n Dom_polynome.dom Daemon.enqueue;
-      Events.attach_repr d n Daemon.enqueue;
-    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 ();
-    Choice.register d (Boolean.chobool (Ground.node f))
+  | {
+   app = { builtin = Expr.Lt | Expr.Leq | Expr.Geq | Expr.Gt };
+   tyargs = [];
+   args;
+  } ->
+      let attach n =
+        Dom_polynome.events_repr_change d ~node:n Daemon.enqueue;
+        Events.attach_repr d n Daemon.enqueue
+      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 ();
+      Choice.register d (Boolean.chobool (Ground.node f))
   | _ -> ()
 
-let init env =
-  Ground.register_converter env converter
+let init env = Ground.register_converter env converter
diff --git a/src_colibri2/theories/LRA/mul.ml b/src_colibri2/theories/LRA/mul.ml
index 2d078b586480accbd5f4adbdb065c903fa07151e..c59c0dfc175cd9379b0e9e4aa17ce40126b72d20 100644
--- a/src_colibri2/theories/LRA/mul.ml
+++ b/src_colibri2/theories/LRA/mul.ml
@@ -18,23 +18,20 @@
 (*  for more details (enclosed in the file licenses/LGPLv2.1).           *)
 (*************************************************************************)
 
-let attach d n (mul,other) =
-  Daemon.attach_value d n
-    RealValue.key
-    (fun d _ q ->
-       Dom_polynome.assume_poly_equality d mul (Polynome.monome (RealValue.value q) other)
-    )
+let attach d n (mul, other) =
+  Daemon.attach_value d n RealValue.key (fun d _ q ->
+      Dom_polynome.assume_equality d mul
+        (Polynome.monome (RealValue.value q) other))
 
-let converter d (f:Ground.t) =
+let converter d (f : Ground.t) =
   let res = Ground.node f in
   match Ground.sem f with
-  | { app = {builtin = Expr.Mul}; tyargs = []; args; _ } -> begin
-      let arg1,arg2 = Colibri2_popop_lib.IArray.extract2_exn args in
-      Egraph.register d arg1; Egraph.register d arg2;
-      attach d arg1 (res,arg2);
-      attach d arg2 (res,arg1)
-    end
+  | { app = { builtin = Expr.Mul }; tyargs = []; args; _ } ->
+      let arg1, arg2 = Colibri2_popop_lib.IArray.extract2_exn args in
+      Egraph.register d arg1;
+      Egraph.register d arg2;
+      attach d arg1 (res, arg2);
+      attach d arg2 (res, arg1)
   | _ -> ()
 
-let init env =
-  Ground.register_converter env converter
+let init env = Ground.register_converter env converter
diff --git a/src_colibri2/theories/LRA/pivot.ml b/src_colibri2/theories/LRA/pivot.ml
index 0c97ad4837562a0fc7b37f9fdcd2538f42e8cb26..76b0c8f419f80081ed2f55b85b32e86225e6ba2a 100644
--- a/src_colibri2/theories/LRA/pivot.ml
+++ b/src_colibri2/theories/LRA/pivot.ml
@@ -198,7 +198,7 @@ end = struct
                  (part d (p2.repr :: P.S.elements p2.eqs)))
           with
           | `Solved ->
-              (* The domains have been subsituted, and possibly recursively *)
+              (* The domains have been substituted, and possibly recursively *)
               merge_aux d cl1 cl2
           | `Not_solved ->
               (* nothing to solve *)
@@ -347,3 +347,198 @@ end = struct
 
   let attach_repr_change = attach_eqs_change
 end
+
+type 'a solve_total = AlreadyEqual | Contradiction | Subst of Node.t * 'a
+
+module Total (P : sig
+  type t
+
+  val name : string
+
+  include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
+
+  val of_one_node : Node.t -> t
+
+  val is_one_node : t -> Node.t option
+
+  val subst : t -> Node.t -> t -> t
+
+  val normalize : t -> f:(Node.t -> t) -> t
+
+  type data
+
+  val nodes : t -> data Node.M.t
+
+  val solve : t -> t -> t solve_total
+
+  val set : Egraph.wt -> Node.t -> t -> unit
+end) : sig
+  val assume_equality : Egraph.wt -> Node.t -> P.t -> unit
+
+  val init : Egraph.wt -> unit
+
+  val get_repr : _ Egraph.t -> Node.t -> P.t option
+
+  val attach_repr_change :
+    _ Egraph.t -> ?node:Node.t -> (Egraph.wt -> Node.t -> unit) -> unit
+
+  val events_repr_change :
+    _ Egraph.t ->
+    ?node:Node.t ->
+    (Egraph.rt -> Node.t -> Events.enqueue) ->
+    unit
+
+  val normalize : _ Egraph.t -> P.t -> P.t
+end = struct
+  open Colibri2_popop_lib
+
+  let dom =
+    Dom.Kind.create
+      (module struct
+        type t = P.t
+
+        let name = P.name
+      end)
+
+  let used_in_poly : Node.t Bag.t Node.HC.t =
+    Node.HC.create (Bag.pp Pp.semi Node.pp) "used_in_poly"
+
+  let set_poly d cl p =
+    Egraph.set_dom d dom cl p;
+    P.set d cl p
+
+  let add_used d cl' new_cl =
+    Node.M.iter
+      (fun used _ ->
+        Node.HC.change
+          (function
+            | Some b -> Some (Bag.append b cl')
+            | None ->
+                (match Egraph.get_dom d dom used with
+                | None ->
+                    (* If a used node have no polynome associated, we set it to
+                       itself. This allows to be warned when this node is merged. It
+                       is the reason why this module doesn't specifically wait for
+                       representative change *)
+                    Egraph.set_dom d dom used (P.of_one_node used)
+                | Some p -> assert (P.equal (P.of_one_node used) p));
+                Some (Bag.elt cl'))
+          used_in_poly d used)
+      new_cl
+
+  let subst_doms d cl (p : P.t) =
+    let b =
+      match Node.HC.find used_in_poly d cl with
+      | exception Not_found -> Bag.empty
+      | b -> b
+    in
+    Bag.iter
+      (fun cl' ->
+        match Egraph.get_dom d dom cl' with
+        | None -> assert false (* absurd: can't be used and absent *)
+        | Some q ->
+            let new_cl = Node.M.set_diff (P.nodes p) (P.nodes q) in
+            let q = P.subst q cl p in
+            add_used d cl' new_cl;
+            set_poly d cl' q)
+      b;
+    add_used d cl (P.nodes p);
+    set_poly d cl p
+
+  module Th = struct
+    include P
+
+    let merged v1 v2 =
+      match (v1, v2) with
+      | None, None -> true
+      | Some v', Some v -> equal v' v
+      | _ -> false
+
+    let norm_dom cl = function
+      | None ->
+          let r = P.of_one_node cl in
+          r
+      | Some p -> p
+
+    let add_itself d cl norm =
+      add_used d cl (P.nodes norm);
+      Egraph.set_dom d dom cl norm
+
+    let merge d ((p1o, cl1) as a1) ((p2o, cl2) as a2) inv =
+      assert (not (Egraph.is_equal d cl1 cl2));
+      assert (not (Option.is_none p1o && Option.is_none p2o));
+      let (pother, other), (prepr, repr) = if inv then (a2, a1) else (a1, a2) in
+      let other = Egraph.find d other in
+      let repr = Egraph.find d repr in
+      let p1 = norm_dom other pother in
+      let p2 = norm_dom repr prepr in
+      (match P.solve p1 p2 with
+      | AlreadyEqual -> (
+          (* no new equality already equal *)
+          match (pother, prepr) with
+          | Some _, Some _ | None, None ->
+              assert false (* absurd: no need of merge *)
+          | Some p, None ->
+              (* p = repr *)
+              add_itself d repr p
+          | None, Some p ->
+              (* p = other *)
+              add_itself d other p)
+      | Contradiction -> Egraph.contradiction d
+      | Subst (x, p) ->
+          Debug.dprintf2 debug "[Arith] @[pivot %a@]" Node.pp x;
+          let add_if_default n norm = function
+            | Some _ -> ()
+            | None -> add_itself d n norm
+          in
+          add_if_default other p1 pother;
+          add_if_default repr p2 prepr;
+          subst_doms d x p);
+      assert (
+        Option.compare P.compare
+          (Egraph.get_dom d dom repr)
+          (Egraph.get_dom d dom other)
+        = 0)
+
+    let solve_one d cl p1 =
+      match Egraph.get_dom d dom cl with
+      | None -> subst_doms d cl p1
+      | Some p2 -> (
+          match P.solve p1 p2 with
+          | AlreadyEqual -> ()
+          | Contradiction -> Egraph.contradiction d
+          | Subst (x, p) ->
+              Debug.dprintf2 debug "[Arith] @[pivot %a@]" Node.pp x;
+              subst_doms d x p)
+
+    let key = dom
+  end
+
+  let () = Dom.register (module Th)
+
+  let normalize d (p : P.t) =
+    P.normalize p ~f:(fun cl ->
+        let cl = Egraph.find_def d cl in
+        match Egraph.get_dom d dom cl with
+        | None -> P.of_one_node cl
+        | Some p -> p)
+
+  let assume_equality d n (p : P.t) =
+    let n = Egraph.find_def d n in
+    let p = normalize d p in
+    Th.solve_one d n p
+
+  let get_repr d cl = Egraph.get_dom d dom cl
+
+  let attach_repr_change d ?node f =
+    match node with
+    | Some x -> Daemon.attach_dom d x dom f
+    | None -> Daemon.attach_any_dom d dom f
+
+  let events_repr_change d ?node f =
+    match node with
+    | Some x -> Events.attach_dom d x dom f
+    | None -> Events.attach_any_dom d dom f
+
+  let init _ = ()
+end
diff --git a/src_colibri2/theories/LRA/pivot.mli b/src_colibri2/theories/LRA/pivot.mli
index 0012b6968ab2e6caf3a16d10ac00a76c807125b3..72186b9c1736954ff30113ededa139fe7080a552 100644
--- a/src_colibri2/theories/LRA/pivot.mli
+++ b/src_colibri2/theories/LRA/pivot.mli
@@ -83,49 +83,59 @@ end) : sig
     _ Egraph.t -> ?node:Node.t -> (Egraph.wt -> Node.t -> unit) -> unit
 end
 
-(*
-module Total(P:sig
+type 'a solve_total = AlreadyEqual | Contradiction | Subst of Node.t * 'a
 
-    type t
-
-    include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
+module Total (P : sig
+  type t
+  (** Normalizable terms *)
 
-    val of_one_node: Node.t -> t
-    (** build a value that represent one node *)
+  val name : string
 
-    val is_one_node: t -> Node.t option
-    (** test if a value represents one node *)
+  include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
 
-    val subst: t -> Node.t -> t -> t option
-    (** [subst p n q] substitute [n] by [q] in [p], if [n] is not in [p] None is
-        returned, otherwise the substitution is returned *)
+  val of_one_node : Node.t -> t
+  (** Build a value that represent one node *)
 
-    val normalize: t -> f:(Node.t -> t option) -> t
-    (** [norm p ~f] normalize [p] using [f] *)
+  val is_one_node : t -> Node.t option
+  (** Test if a value represents one node *)
 
-    type data
+  val subst : t -> Node.t -> t -> t
+  (** [subst p n q] return the result of the substitution of [n] by [q] in [p] *)
 
-    val nodes: t -> data Node.M.t
+  val normalize : t -> f:(Node.t -> t) -> t
+  (** [norm p ~f] normalize [p] using [f] *)
 
-    type info
+  type data
+  (** An abstract type to avoid translating the map to sets in {!nodes} *)
 
-    val info: Egraph.ro -> t -> info
+  val nodes : t -> data Node.M.t
+  (** [nodes t] returns the node which are present in [t] *)
 
-    val solve: info -> info -> (Node.t * t)
+  val solve : t -> t -> t solve_total
+  (** [solve t1 t2] solve the equation [t1 = t2] by returning a substitution. *)
 
-    val attach_info_change: (Egraph.rw -> Node.t -> Events.enqueue) -> unit
+  val set : Egraph.wt -> Node.t -> t -> unit
+  (** Called a new term equal to this node is found *)
+end) : sig
+  val assume_equality : Egraph.wt -> Node.t -> P.t -> unit
+  (** [assume_equality d n p] assumes the equality [n = p] *)
 
-    val set: Egraph.rw -> Node.t -> t -> unit
+  val init : Egraph.wt -> unit
+  (** Initialize the data-structure needed. *)
 
-  end): sig
+  val get_repr : _ Egraph.t -> Node.t -> P.t option
 
-  include Dom.S with type t = P.t
+  val attach_repr_change :
+    _ Egraph.t -> ?node:Node.t -> (Egraph.wt -> Node.t -> unit) -> unit
 
-  val assume_equality: Egraph.rw -> Node.t -> P.t -> unit
+  val events_repr_change :
+    _ Egraph.t ->
+    ?node:Node.t ->
+    (Egraph.rt -> Node.t -> Events.enqueue) ->
+    unit
 
-  val init: Egraph.rw -> unit
+  val normalize : _ Egraph.t -> P.t -> P.t
 end
-*)
 
 (*  LocalWords:  Normalizable
  *)