diff --git a/src_colibri2/core/ground.ml b/src_colibri2/core/ground.ml
index 62762e173b643e93f871b418d9663d621d37ee0d..26a9f958eafe4e1e14b440b38152b48146bf1d47 100644
--- a/src_colibri2/core/ground.ml
+++ b/src_colibri2/core/ground.ml
@@ -39,6 +39,17 @@ module Subst = struct
   include Colibri2_popop_lib.Popop_stdlib.MkDatatype(T)
 
   let empty = { term = Expr.Term.Var.M.empty; ty = Expr.Ty.Var.M.empty}
+
+  let distinct_union subst1 subst2 =
+    {
+      ty =
+        Expr.Ty.Var.M.union (fun _ _ -> assert false) subst1.ty subst2.ty;
+      term =
+        Expr.Term.Var.M.union
+          (fun _ _ -> assert false)
+          subst1.term subst2.term;
+    }
+
 end
 
 
diff --git a/src_colibri2/core/ground.mli b/src_colibri2/core/ground.mli
index aa66b0730f6780af04b6c2982f4ad8632a082adf..0466e1e3b493b93682f4e0acdfbc3556c900239e 100644
--- a/src_colibri2/core/ground.mli
+++ b/src_colibri2/core/ground.mli
@@ -29,6 +29,8 @@ module Subst: sig
   include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
 
   val empty: t
+
+  val distinct_union: t -> t -> t
 end
 
 module Ty: sig
diff --git a/src_colibri2/tests/generate_tests/generate_dune_tests.ml b/src_colibri2/tests/generate_tests/generate_dune_tests.ml
index 7155d1b705b7442f4ba553530501407ac8e74d09..0c848087852ca710ad7909a1f5467b02ec55f71b 100644
--- a/src_colibri2/tests/generate_tests/generate_dune_tests.ml
+++ b/src_colibri2/tests/generate_tests/generate_dune_tests.ml
@@ -13,7 +13,7 @@ let () =
   let files = Sys.readdir dir in
   Array.sort String.compare files;
   let files = Array.to_list files in
-  let files = List.filter (fun f -> Filename.check_suffix f "cnf" || Filename.check_suffix f ".smt2") files in
+  let files = List.filter (fun f -> List.exists (Filename.check_suffix f) ["cnf";".smt2";".psmt2"]) files in
   let cout = open_out (Filename.concat dir "dune.inc") in
     Printf.fprintf cout
     "(rule (action (with-stdout-to oracle (echo %S))))\n"
diff --git a/src_colibri2/tests/solve/all/steplimitreached/bag-BagImpl-addqtvc.psmt2 b/src_colibri2/tests/solve/all/steplimitreached/bag-BagImpl-addqtvc.psmt2
new file mode 100644
index 0000000000000000000000000000000000000000..b08480453a0ef400f9563741b34eaddf709247ab
--- /dev/null
+++ b/src_colibri2/tests/solve/all/steplimitreached/bag-BagImpl-addqtvc.psmt2
@@ -0,0 +1,245 @@
+;; produced by local colibri2.drv ;;
+(set-logic ALL)
+(set-info :smt-lib-version 2.6)
+;;; SMT-LIB2: integer arithmetic
+(declare-sort string 0)
+
+(declare-datatypes ((tuple0 0))
+(((Tuple0))))
+(declare-sort infix_mngt 2)
+
+(declare-fun infix_at (par (a b) ((infix_mngt a b) a) b))
+
+(define-fun infix_eqeq (par (a1) ((b1 (infix_mngt a1 Int)) (b2 (infix_mngt a1
+  Int))) Bool (forall ((x a1)) (= (infix_at b1 x) (infix_at b2 x)))))
+
+(declare-fun add (par (a1) (a1 (infix_mngt a1 Int)) (infix_mngt a1 Int)))
+
+;; add'def
+  (assert (par (a1)
+  (forall ((e a1) (b1 (infix_mngt a1 Int)) (x a1))
+  (= (infix_at (add e b1) x) (ite (= x e) (+ (infix_at b1 x) 1)
+                             (infix_at b1 x))))))
+
+(declare-fun remove (par (a1) (a1 (infix_mngt a1 Int)) (infix_mngt a1 Int)))
+
+;; remove'def
+  (assert (par (a1)
+  (forall ((e a1) (b1 (infix_mngt a1 Int)) (x a1))
+  (= (infix_at (remove e b1) x) (ite (= x e) (- (infix_at b1 x) 1)
+                                (infix_at b1 x))))))
+
+(declare-fun set (par (a1 b1) ((infix_mngt a1 b1) a1 b1) (infix_mngt a1 b1)))
+
+;; set'def
+  (assert (par (a1 b1)
+  (forall ((f (infix_mngt a1 b1)) (x a1) (v b1) (y a1))
+  (= (infix_at (set f x v) y) (ite (= y x) v (infix_at f y))))))
+
+(declare-fun const1 (par (a1 b1) (b1) (infix_mngt a1 b1)))
+
+;; const'def
+  (assert (par (a1 b1)
+  (forall ((v b1) (us a1)) (= (infix_at (const1 v) us) v))))
+
+(declare-sort rarray 1)
+
+(declare-fun len (par (a1) ((rarray a1)) Int))
+
+(declare-fun data (par (a1) ((rarray a1)) (infix_mngt Int a1)))
+
+;; rarray'invariant
+  (assert (par (a1)
+  (forall ((self (rarray a1))) (! (<= 0 (len self)) :pattern ((len self)) ))))
+
+(define-fun mixfix_lbrb (par (a1) ((r (rarray a1))
+  (i Int)) a1 (infix_at (data r) i)))
+
+(declare-fun mixfix_lblsmnrb (par (a1) ((rarray a1) Int a1) (rarray a1)))
+
+;; mixfix [<-]'spec
+  (assert (par (a1)
+  (forall ((r (rarray a1)) (i Int) (v a1))
+  (and (= (len (mixfix_lblsmnrb r i v)) (len r))
+  (= (data (mixfix_lblsmnrb r i v)) (set (data r) i v))))))
+
+(declare-fun numof ((infix_mngt Int Bool) Int Int) Int)
+
+;; numof'def
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (ite (<= b2 a2) (= (numof p a2 b2) 0)
+  (ite (= (infix_at p (- b2 1)) true)
+  (= (numof p a2 b2) (+ 1 (numof p a2 (- b2 1))))
+  (= (numof p a2 b2) (numof p a2 (- b2 1)))))))
+
+;; Numof_bounds
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (< a2 b2) (and (<= 0 (numof p a2 b2)) (<= (numof p a2 b2) (- b2 a2))))))
+
+;; Numof_append
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int) (c Int))
+  (=> (and (<= a2 b2) (<= b2 c))
+  (= (numof p a2 c) (+ (numof p a2 b2) (numof p b2 c))))))
+
+;; Numof_left_no_add
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (< a2 b2)
+  (=> (not (= (infix_at p a2) true))
+  (= (numof p a2 b2) (numof p (+ a2 1) b2))))))
+
+;; Numof_left_add
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (< a2 b2)
+  (=> (= (infix_at p a2) true)
+  (= (numof p a2 b2) (+ 1 (numof p (+ a2 1) b2)))))))
+
+;; Empty
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=>
+  (forall ((n Int))
+  (=> (and (<= a2 n) (< n b2)) (not (= (infix_at p n) true))))
+  (= (numof p a2 b2) 0))))
+
+;; Full
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (<= a2 b2)
+  (=>
+  (forall ((n Int)) (=> (and (<= a2 n) (< n b2)) (= (infix_at p n) true)))
+  (= (numof p a2 b2) (- b2 a2))))))
+
+;; numof_increasing
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (i Int) (j Int) (k Int))
+  (=> (and (<= i j) (<= j k)) (<= (numof p i j) (numof p i k)))))
+
+;; numof_strictly_increasing
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (i Int) (j Int) (k Int) (l Int))
+  (=> (and (<= i j) (and (<= j k) (< k l)))
+  (=> (= (infix_at p k) true) (< (numof p i j) (numof p i l))))))
+
+;; numof_change_any
+  (assert
+  (forall ((p1 (infix_mngt Int Bool)) (p2 (infix_mngt Int Bool)) (a2 Int)
+  (b2 Int))
+  (=>
+  (forall ((j Int))
+  (=> (and (<= a2 j) (< j b2))
+  (=> (= (infix_at p1 j) true) (= (infix_at p2 j) true))))
+  (>= (numof p2 a2 b2) (numof p1 a2 b2)))))
+
+;; numof_change_some
+  (assert
+  (forall ((p1 (infix_mngt Int Bool)) (p2 (infix_mngt Int Bool)) (a2 Int)
+  (b2 Int) (i Int))
+  (=> (and (<= a2 i) (< i b2))
+  (=>
+  (forall ((j Int))
+  (=> (and (<= a2 j) (< j b2))
+  (=> (= (infix_at p1 j) true) (= (infix_at p2 j) true))))
+  (=> (not (= (infix_at p1 i) true))
+  (=> (= (infix_at p2 i) true) (> (numof p2 a2 b2) (numof p1 a2 b2))))))))
+
+;; numof_change_equiv
+  (assert
+  (forall ((p1 (infix_mngt Int Bool)) (p2 (infix_mngt Int Bool)) (a2 Int)
+  (b2 Int))
+  (=>
+  (forall ((j Int))
+  (=> (and (<= a2 j) (< j b2))
+  (= (= (infix_at p1 j) true) (= (infix_at p2 j) true))))
+  (= (numof p2 a2 b2) (numof p1 a2 b2)))))
+
+(declare-datatypes ((t_model 1))
+((par (a1) ((Null (Null_proj_1 Int)) (Value (Value_proj_1 a1))))))
+(declare-sort t 1)
+
+(declare-fun v (par (a1) ((t a1)) (t_model a1)))
+
+(define-fun is_null (par (a1)
+  ((t1 (t a1))) Bool (let ((subject (v t1)))
+                     (ite ((_ is Null) subject) true false))))
+
+(declare-fun fc (par (a1) ((rarray (t a1)) a1) (infix_mngt Int Bool)))
+
+;; fc'def
+  (assert (par (a1)
+  (forall ((r (rarray (t a1))) (x a1) (i Int))
+  (= (= (infix_at (fc r x) i) true) (= (v (infix_at (data r) i)) (Value x))))))
+
+(define-fun numof1 (par (a1) ((r (rarray (t a1))) (x a1) (l Int)
+  (u Int)) Int (numof (fc r x) l u)))
+
+(declare-sort t1 1)
+
+(declare-fun size (par (a1) ((t1 a1)) Int))
+
+(declare-fun data1 (par (a1) ((t1 a1)) (rarray (t a1))))
+
+(declare-fun contents (par (a1) ((t1 a1)) (infix_mngt a1 Int)))
+
+;; t'invariant
+  (assert (par (a1)
+  (forall ((self (t1 a1)))
+  (! (and (and (<= 0 (size self)) (= (size self) (len (data1 self))))
+     (and
+     (forall ((i Int))
+     (=> (and (<= 0 i) (< i (size self)))
+     (not (is_null (mixfix_lbrb (data1 self) i)))))
+     (forall ((x a1))
+     (= (infix_at (contents self) x) (numof1 (data1 self) x 0 (size self)))))) :pattern (
+  (contents self)) :pattern ((data1 self)) :pattern ((size self)) ))))
+
+(declare-sort a2 0)
+
+(assert
+;; addqtvc
+ ;; File "why3/examples/bag.mlw", line 128, characters 6-9
+  (not
+  (forall ((t2 (t1 a2)) (x a2))
+  (let ((n (size t2)))
+  (let ((o (+ n 1)))
+  (and (<= 0 o)
+  (forall ((t_data (rarray (t a2))))
+  (=>
+  (and (= (len t_data) o)
+  (forall ((i Int))
+  (=> (and (<= 0 i) (< i (len (data1 t2))))
+  (=> (< i o) (= (mixfix_lbrb t_data i) (mixfix_lbrb (data1 t2) i))))))
+  (and
+  (forall ((v1 a2)) (= (infix_at (contents t2) v1) (numof1 t_data v1 0 n)))
+  (forall ((result (t a2)))
+  (=> (= (v result) (Value x))
+  (and (and (<= 0 n) (< n (len t_data)))
+  (=> (= (len (mixfix_lblsmnrb t_data n result)) (len t_data))
+  (=> (= (len (mixfix_lblsmnrb t_data n result)) (len t_data))
+  (and
+  (forall ((v1 a2))
+  (= (infix_at (contents t2) v1) (numof1 (mixfix_lblsmnrb t_data n result) v1
+                                 0 n)))
+  (forall ((t3 (t1 a2)))
+  (and
+  (and
+  (and (<= 0 (+ n 1)) (= (+ n 1) (len (mixfix_lblsmnrb t_data n result))))
+  (and
+  (forall ((i Int))
+  (=> (and (<= 0 i) (< i (+ n 1)))
+  (not (is_null (mixfix_lbrb (mixfix_lblsmnrb t_data n result) i)))))
+  (forall ((x1 a2))
+  (= (infix_at (add x (contents t2)) x1) (numof1
+                                         (mixfix_lblsmnrb t_data n result) x1
+                                         0 (+ n 1))))))
+  (=>
+  (and (= (add x (contents t2)) (contents t3))
+  (and (= (mixfix_lblsmnrb t_data n result) (data1 t3))
+  (= (+ n 1) (size t3))))
+  (and (= (size t3) (+ (size t2) 1)) (infix_eqeq (contents t3)
+  (add x (contents t2))))))))))))))))))))))
+(check-sat)
diff --git a/src_colibri2/tests/solve/all/steplimitreached/dune b/src_colibri2/tests/solve/all/steplimitreached/dune
new file mode 100644
index 0000000000000000000000000000000000000000..39efa26de274dd57baab7e1bac879e0503838bd5
--- /dev/null
+++ b/src_colibri2/tests/solve/all/steplimitreached/dune
@@ -0,0 +1,13 @@
+(include dune.inc)
+
+(rule
+ (alias runtest)
+ (deps
+  (glob_files *.cnf)
+  (glob_files *.smt2)
+  (glob_files *.psmt2))
+ (action
+  (with-stdout-to
+   dune.inc
+   (run %{exe:../../../generate_tests/generate_dune_tests.exe} . steplimitreached)))
+ (mode promote))
diff --git a/src_colibri2/tests/solve/all/steplimitreached/dune.inc b/src_colibri2/tests/solve/all/steplimitreached/dune.inc
new file mode 100644
index 0000000000000000000000000000000000000000..7a7c228cac804f431679042072eb142be13719db
--- /dev/null
+++ b/src_colibri2/tests/solve/all/steplimitreached/dune.inc
@@ -0,0 +1,3 @@
+(rule (action (with-stdout-to oracle (echo "steplimitreached\n"))))
+(rule (action (with-stdout-to bag-BagImpl-addqtvc.psmt2.res (run %{bin:colibri2} --max-step 3000 %{dep:bag-BagImpl-addqtvc.psmt2}))))
+(rule (alias runtest) (action (diff oracle bag-BagImpl-addqtvc.psmt2.res)))
diff --git a/src_colibri2/tests/solve/all/unsat/bag-BagImpl-createqtvc.psmt2 b/src_colibri2/tests/solve/all/unsat/bag-BagImpl-createqtvc.psmt2
new file mode 100644
index 0000000000000000000000000000000000000000..cea398b9b343bd2244529c5bc8a78445cc54847a
--- /dev/null
+++ b/src_colibri2/tests/solve/all/unsat/bag-BagImpl-createqtvc.psmt2
@@ -0,0 +1,233 @@
+;; produced by local colibri2.drv ;;
+(set-logic ALL)
+(set-info :smt-lib-version 2.6)
+;;; SMT-LIB2: integer arithmetic
+(declare-sort string 0)
+
+(declare-datatypes ((tuple0 0))
+(((Tuple0))))
+(declare-sort infix_mngt 2)
+
+(declare-fun infix_at (par (a b) ((infix_mngt a b) a) b))
+
+(define-fun infix_eqeq (par (a1) ((b1 (infix_mngt a1 Int)) (b2 (infix_mngt a1
+  Int))) Bool (forall ((x a1)) (= (infix_at b1 x) (infix_at b2 x)))))
+
+(declare-fun add (par (a1) (a1 (infix_mngt a1 Int)) (infix_mngt a1 Int)))
+
+;; add'def
+  (assert (par (a1)
+  (forall ((e a1) (b1 (infix_mngt a1 Int)) (x a1))
+  (= (infix_at (add e b1) x) (ite (= x e) (+ (infix_at b1 x) 1)
+                             (infix_at b1 x))))))
+
+(declare-fun remove (par (a1) (a1 (infix_mngt a1 Int)) (infix_mngt a1 Int)))
+
+;; remove'def
+  (assert (par (a1)
+  (forall ((e a1) (b1 (infix_mngt a1 Int)) (x a1))
+  (= (infix_at (remove e b1) x) (ite (= x e) (- (infix_at b1 x) 1)
+                                (infix_at b1 x))))))
+
+(declare-fun set (par (a1 b1) ((infix_mngt a1 b1) a1 b1) (infix_mngt a1 b1)))
+
+;; set'def
+  (assert (par (a1 b1)
+  (forall ((f (infix_mngt a1 b1)) (x a1) (v b1) (y a1))
+  (= (infix_at (set f x v) y) (ite (= y x) v (infix_at f y))))))
+
+(declare-fun const1 (par (a1 b1) (b1) (infix_mngt a1 b1)))
+
+;; const'def
+  (assert (par (a1 b1)
+  (forall ((v b1) (us a1)) (= (infix_at (const1 v) us) v))))
+
+(declare-sort rarray 1)
+
+(declare-fun len (par (a1) ((rarray a1)) Int))
+
+(declare-fun data (par (a1) ((rarray a1)) (infix_mngt Int a1)))
+
+;; rarray'invariant
+  (assert (par (a1)
+  (forall ((self (rarray a1))) (! (<= 0 (len self)) :pattern ((len self)) ))))
+
+(define-fun mixfix_lbrb (par (a1) ((r (rarray a1))
+  (i Int)) a1 (infix_at (data r) i)))
+
+(declare-fun mixfix_lblsmnrb (par (a1) ((rarray a1) Int a1) (rarray a1)))
+
+;; mixfix [<-]'spec
+  (assert (par (a1)
+  (forall ((r (rarray a1)) (i Int) (v a1))
+  (and (= (len (mixfix_lblsmnrb r i v)) (len r))
+  (= (data (mixfix_lblsmnrb r i v)) (set (data r) i v))))))
+
+(declare-fun numof ((infix_mngt Int Bool) Int Int) Int)
+
+;; numof'def
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (ite (<= b2 a2) (= (numof p a2 b2) 0)
+  (ite (= (infix_at p (- b2 1)) true)
+  (= (numof p a2 b2) (+ 1 (numof p a2 (- b2 1))))
+  (= (numof p a2 b2) (numof p a2 (- b2 1)))))))
+
+;; Numof_bounds
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (< a2 b2) (and (<= 0 (numof p a2 b2)) (<= (numof p a2 b2) (- b2 a2))))))
+
+;; Numof_append
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int) (c Int))
+  (=> (and (<= a2 b2) (<= b2 c))
+  (= (numof p a2 c) (+ (numof p a2 b2) (numof p b2 c))))))
+
+;; Numof_left_no_add
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (< a2 b2)
+  (=> (not (= (infix_at p a2) true))
+  (= (numof p a2 b2) (numof p (+ a2 1) b2))))))
+
+;; Numof_left_add
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (< a2 b2)
+  (=> (= (infix_at p a2) true)
+  (= (numof p a2 b2) (+ 1 (numof p (+ a2 1) b2)))))))
+
+;; Empty
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=>
+  (forall ((n Int))
+  (=> (and (<= a2 n) (< n b2)) (not (= (infix_at p n) true))))
+  (= (numof p a2 b2) 0))))
+
+;; Full
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (a2 Int) (b2 Int))
+  (=> (<= a2 b2)
+  (=>
+  (forall ((n Int)) (=> (and (<= a2 n) (< n b2)) (= (infix_at p n) true)))
+  (= (numof p a2 b2) (- b2 a2))))))
+
+;; numof_increasing
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (i Int) (j Int) (k Int))
+  (=> (and (<= i j) (<= j k)) (<= (numof p i j) (numof p i k)))))
+
+;; numof_strictly_increasing
+  (assert
+  (forall ((p (infix_mngt Int Bool)) (i Int) (j Int) (k Int) (l Int))
+  (=> (and (<= i j) (and (<= j k) (< k l)))
+  (=> (= (infix_at p k) true) (< (numof p i j) (numof p i l))))))
+
+;; numof_change_any
+  (assert
+  (forall ((p1 (infix_mngt Int Bool)) (p2 (infix_mngt Int Bool)) (a2 Int)
+  (b2 Int))
+  (=>
+  (forall ((j Int))
+  (=> (and (<= a2 j) (< j b2))
+  (=> (= (infix_at p1 j) true) (= (infix_at p2 j) true))))
+  (>= (numof p2 a2 b2) (numof p1 a2 b2)))))
+
+;; numof_change_some
+  (assert
+  (forall ((p1 (infix_mngt Int Bool)) (p2 (infix_mngt Int Bool)) (a2 Int)
+  (b2 Int) (i Int))
+  (=> (and (<= a2 i) (< i b2))
+  (=>
+  (forall ((j Int))
+  (=> (and (<= a2 j) (< j b2))
+  (=> (= (infix_at p1 j) true) (= (infix_at p2 j) true))))
+  (=> (not (= (infix_at p1 i) true))
+  (=> (= (infix_at p2 i) true) (> (numof p2 a2 b2) (numof p1 a2 b2))))))))
+
+;; numof_change_equiv
+  (assert
+  (forall ((p1 (infix_mngt Int Bool)) (p2 (infix_mngt Int Bool)) (a2 Int)
+  (b2 Int))
+  (=>
+  (forall ((j Int))
+  (=> (and (<= a2 j) (< j b2))
+  (= (= (infix_at p1 j) true) (= (infix_at p2 j) true))))
+  (= (numof p2 a2 b2) (numof p1 a2 b2)))))
+
+(declare-datatypes ((t_model 1))
+((par (a1) ((Null (Null_proj_1 Int)) (Value (Value_proj_1 a1))))))
+(declare-sort t 1)
+
+(declare-fun v (par (a1) ((t a1)) (t_model a1)))
+
+(define-fun is_null (par (a1)
+  ((t1 (t a1))) Bool (let ((subject (v t1)))
+                     (ite ((_ is Null) subject) true false))))
+
+(declare-fun fc (par (a1) ((rarray (t a1)) a1) (infix_mngt Int Bool)))
+
+;; fc'def
+  (assert (par (a1)
+  (forall ((r (rarray (t a1))) (x a1) (i Int))
+  (= (= (infix_at (fc r x) i) true) (= (v (infix_at (data r) i)) (Value x))))))
+
+(define-fun numof1 (par (a1) ((r (rarray (t a1))) (x a1) (l Int)
+  (u Int)) Int (numof (fc r x) l u)))
+
+(declare-sort t1 1)
+
+(declare-fun size (par (a1) ((t1 a1)) Int))
+
+(declare-fun data1 (par (a1) ((t1 a1)) (rarray (t a1))))
+
+(declare-fun contents (par (a1) ((t1 a1)) (infix_mngt a1 Int)))
+
+;; t'invariant
+  (assert (par (a1)
+  (forall ((self (t1 a1)))
+  (! (and (and (<= 0 (size self)) (= (size self) (len (data1 self))))
+     (and
+     (forall ((i Int))
+     (=> (and (<= 0 i) (< i (size self)))
+     (not (is_null (mixfix_lbrb (data1 self) i)))))
+     (forall ((x a1))
+     (= (infix_at (contents self) x) (numof1 (data1 self) x 0 (size self)))))) :pattern (
+  (contents self)) :pattern ((data1 self)) :pattern ((size self)) ))))
+
+(declare-sort a2 0)
+
+(declare-fun fc1 () (infix_mngt a2 Int))
+
+(declare-fun fc2 () (infix_mngt a2 Int))
+
+;; fc'def
+  (assert (forall ((y0 a2)) (= (infix_at fc1 y0) 0)))
+
+;; fc'def
+  (assert (forall ((y0 a2)) (= (infix_at fc2 y0) 0)))
+
+(assert
+;; createqtvc
+ ;; File "why3/examples/bag.mlw", line 113, characters 6-12
+  (not
+  (forall ((null (t a2)))
+  (=> (is_null null)
+  (let ((result fc1))
+  (and (<= 0 0)
+  (forall ((o (rarray (t a2))))
+  (=> (and (= (len o) 0) (= (data o) (const1 null)))
+  (and
+  (and (and (<= 0 0) (= 0 (len o)))
+  (and
+  (forall ((i Int))
+  (=> (and (<= 0 i) (< i 0)) (not (is_null (mixfix_lbrb o i)))))
+  (forall ((x a2)) (= (infix_at result x) (numof1 o x 0 0)))))
+  (forall ((result1 (t1 a2)))
+  (=>
+  (and (= (size result1) 0)
+  (and (= (data1 result1) o) (= (contents result1) result)))
+  (and (= (size result1) 0) (infix_eqeq (contents result1) fc2)))))))))))))
+(check-sat)
diff --git a/src_colibri2/tests/solve/all/unsat/dune b/src_colibri2/tests/solve/all/unsat/dune
new file mode 100644
index 0000000000000000000000000000000000000000..6d9fe5883f08bc15b92dc715d3c58c193023dbb5
--- /dev/null
+++ b/src_colibri2/tests/solve/all/unsat/dune
@@ -0,0 +1,13 @@
+(include dune.inc)
+
+(rule
+ (alias runtest)
+ (deps
+  (glob_files *.cnf)
+  (glob_files *.smt2)
+  (glob_files *.psmt2))
+ (action
+  (with-stdout-to
+   dune.inc
+   (run %{exe:../../../generate_tests/generate_dune_tests.exe} . unsat)))
+ (mode promote))
diff --git a/src_colibri2/tests/solve/all/unsat/dune.inc b/src_colibri2/tests/solve/all/unsat/dune.inc
new file mode 100644
index 0000000000000000000000000000000000000000..5120eabbe923927229d66a7af3975baf6e9dc60c
--- /dev/null
+++ b/src_colibri2/tests/solve/all/unsat/dune.inc
@@ -0,0 +1,5 @@
+(rule (action (with-stdout-to oracle (echo "unsat\n"))))
+(rule (action (with-stdout-to bag-BagImpl-createqtvc.psmt2.res (run %{bin:colibri2} --max-step 3000 %{dep:bag-BagImpl-createqtvc.psmt2}))))
+(rule (alias runtest) (action (diff oracle bag-BagImpl-createqtvc.psmt2.res)))
+(rule (action (with-stdout-to fact-FactRecursive-fact_recqtvc.psmt2.res (run %{bin:colibri2} --max-step 3000 %{dep:fact-FactRecursive-fact_recqtvc.psmt2}))))
+(rule (alias runtest) (action (diff oracle fact-FactRecursive-fact_recqtvc.psmt2.res)))
diff --git a/src_colibri2/tests/solve/all/unsat/fact-FactRecursive-fact_recqtvc.psmt2 b/src_colibri2/tests/solve/all/unsat/fact-FactRecursive-fact_recqtvc.psmt2
new file mode 100644
index 0000000000000000000000000000000000000000..aeb3c6c3d5a21ad84f9b4db2c80e4f95aa5d0dc0
--- /dev/null
+++ b/src_colibri2/tests/solve/all/unsat/fact-FactRecursive-fact_recqtvc.psmt2
@@ -0,0 +1,27 @@
+;; produced by local colibri2.drv ;;
+(set-logic ALL)
+(set-info :smt-lib-version 2.6)
+
+(declare-fun fact (Int) Int)
+
+;; fact'def
+  (assert
+  (forall ((n Int))
+  (=> (<= 0 n)
+  (ite (= n 0) (= (fact n) 1) (= (fact n) (* n (fact (- n 1))))))))
+
+(assert
+;; fact_recqtvc
+ ;; File "examples/fact.mlw", line 9, characters 10-18
+  (not
+  (forall ((x Int))
+  (=> (<= 0 x)
+  (and
+    (=> (not (= x 0))
+     (let ((o (- x 1))) (and (and (<= 0 x) (< o x)) (<= 0 o))))
+    (forall ((result Int))
+      (=>
+       (ite (= x 0) (= result 1) (= result (* x (fact (- x 1)))))
+       (= result (fact x))))
+  )))))
+(check-sat)
diff --git a/src_colibri2/tests/solve/smt_adt/unsat/dune.inc b/src_colibri2/tests/solve/smt_adt/unsat/dune.inc
index 9c4a2ebd51587acbfe31ccf0a461a9757b1d4c06..55451ca3a29b790a96c17ab2251b3c0144b8e89d 100644
--- a/src_colibri2/tests/solve/smt_adt/unsat/dune.inc
+++ b/src_colibri2/tests/solve/smt_adt/unsat/dune.inc
@@ -13,3 +13,5 @@
 (rule (alias runtest) (action (diff oracle list3.smt2.res)))
 (rule (action (with-stdout-to list4.smt2.res (run %{bin:colibri2} --max-step 3000 %{dep:list4.smt2}))))
 (rule (alias runtest) (action (diff oracle list4.smt2.res)))
+(rule (action (with-stdout-to parlist0.psmt2.res (run %{bin:colibri2} --max-step 3000 %{dep:parlist0.psmt2}))))
+(rule (alias runtest) (action (diff oracle parlist0.psmt2.res)))
diff --git a/src_colibri2/tests/solve/smt_adt/unsat/parlist0.psmt2 b/src_colibri2/tests/solve/smt_adt/unsat/parlist0.psmt2
index 66fa9e1701c3446237a0445ec4caba30c10f51f2..9b0f6334bc083def5576b4e975eb99b81815c9d3 100644
--- a/src_colibri2/tests/solve/smt_adt/unsat/parlist0.psmt2
+++ b/src_colibri2/tests/solve/smt_adt/unsat/parlist0.psmt2
@@ -7,6 +7,6 @@
 
 (define-fun singleton (par (a) ((x a)) (list a) (cons x nil)))
 
-(assert (not (= nil (singleton 1))))
+(assert (= nil (singleton 1)))
 
 (check-sat)
diff --git a/src_colibri2/theories/LRA/fourier.ml b/src_colibri2/theories/LRA/fourier.ml
index 23aad9c4b967f78ffbfd4ed8dc850f8297f65edd..f6e37854985117f4c7366e2ff107401ca448ae1e 100644
--- a/src_colibri2/theories/LRA/fourier.ml
+++ b/src_colibri2/theories/LRA/fourier.ml
@@ -6,6 +6,9 @@ let scheduled = Datastructure.Ref.create Bool.pp "LRA.fourier.scheduled" false
 
 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: Interval.Bound.t; origins: Ground.S.t }
 [@@deriving show]
 (** p <= 0 or p < 0 *)
@@ -70,29 +73,65 @@ let delta d eq =
       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 | Interval.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 Interval.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, Interval.Bound.Large when Q.sign p <= 0 ->
-    Debug.dprintf2 debug "[Fourier] discard %a" Ground.S.pp origins;
+  | Some p, Interval.Bound.Large when Q.sign p = 0 ->
+    Ground.S.iter (fun g ->
+        let truth = Option.get_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
-  | Some p, Interval.Bound.Strict when Q.sign p < 0 ->
+  | Some p, _ when Q.sign p < 0 ->
     Debug.dprintf2 debug "[Fourier] discard %a" Ground.S.pp origins;
     eqs
   | _ -> Egraph.contradiction d
 
-let mk_eq d bound truth a b =
+let mk_eq d bound a b =
   let (!) n =
     match Egraph.get_dom d Dom_polynome.dom n with
     | None -> Polynome.monome Q.one (Egraph.find_def d n)
     | Some p -> p in
-  let bound = if truth then bound else match bound with | Interval.Bound.Large -> Strict | Strict -> Large in
-  let a,b = if truth then a,b else b,a in
-  Polynome.sub (!a) (!b), bound,
-  Polynome.sub (Polynome.monome Q.one a) (Polynome.monome Q.one b)
+  let p,bound' =
+    match bound with
+    | Interval.Bound.Strict when Dom_interval.is_integer d a && Dom_interval.is_integer d b ->
+      Polynome.add_cst (Polynome.sub (!a) (!b)) Q.one, Interval.Bound.Large
+    | _ -> Polynome.sub (!a) (!b), bound
+  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 "[Fourier] %a" Ground.pp g;
@@ -100,23 +139,10 @@ let make_equations d (eqs,vars) g =
   | None ->
     (eqs,vars)
   | Some truth ->
-    let p,bound,p_non_norm =
-      match Ground.sem g with
-      | { app = {builtin = Expr.Lt}; tyargs = []; args; _ } ->
-        let a,b = IArray.extract2_exn args in
-        mk_eq d Strict truth a b
-      | { app = {builtin = Expr.Leq}; tyargs = []; args; _ } ->
-        let a,b = IArray.extract2_exn args in
-        mk_eq d Large truth a b
-      | { app = {builtin = Expr.Geq}; tyargs = []; args; _ } ->
-        let a,b = IArray.extract2_exn args in
-        mk_eq d Large truth b a
-      | { app = {builtin = Expr.Gt}; tyargs = []; args; _ } ->
-        let a,b = IArray.extract2_exn args in
-        mk_eq d Strict truth b a
-      | _ -> assert false
+    let p,bound,p_non_norm,bound_non_norm =
+      apply d ~f:mk_eq truth g
     in
-    Debug.dprintf5 debug "[Fourier] %b %a(%a)" truth Polynome.pp p Polynome.pp p_non_norm;
+    Debug.dprintf6 debug "[Fourier] %a(%a)%a0" Polynome.pp p Polynome.pp p_non_norm Interval.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)
@@ -124,12 +150,13 @@ let make_equations d (eqs,vars) g =
     match divide d p_non_norm with
     | Some (p',_,_) ->
       let p' = Dom_polynome.norm d p' in
-      (add_eq d eqs p' bound Ground.S.empty,
+      (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.t) =
   Debug.dprintf0 debug "[Fourier]";
+  Debug.incr stats_run;
   Datastructure.Ref.set scheduled d false;
   let eqs, vars = Datastructure.Queue.fold comparisons d
       ~f:(make_equations d) ~init:([],Node.S.empty)
diff --git a/src_colibri2/theories/LRA/integer_sign_domain.ml b/src_colibri2/theories/LRA/integer_sign_domain.ml
index ad8e9a52c18bd06b4322f2cba83578616b3e5b9e..e521eea13acec1a1ce79c4b391ae0a8f2ae6f5ef 100644
--- a/src_colibri2/theories/LRA/integer_sign_domain.ml
+++ b/src_colibri2/theories/LRA/integer_sign_domain.ml
@@ -33,7 +33,7 @@ module T = struct
 
 
   let pp fmt t =
-    let prefix s = if t.noninteger then "â„•"^s else s in
+    let prefix s = if t.noninteger then s else "â„•"^s in
     Fmt.string fmt @@
     match t.sign.neg, t.sign.zero, t.sign.pos with
     | true, true, true  -> if t.noninteger then "ℝ" else "ℕ"
diff --git a/src_colibri2/theories/LRA/polynome.ml b/src_colibri2/theories/LRA/polynome.ml
index 88a42251db1009db22ff6390e7eb71e998dfcaf5..8a7900a48fd398cc9f0e3dc200fbda5543a9b46c 100644
--- a/src_colibri2/theories/LRA/polynome.ml
+++ b/src_colibri2/theories/LRA/polynome.ml
@@ -97,6 +97,7 @@ let is_one_node p = (** cst = 0 and one empty monome *)
   else None
 
 let sub_cst p q = {p with cst = Q.sub p.cst q}
+let add_cst p q = {p with cst = Q.add p.cst q}
 
 let mult_cst c p1 =
   if Q.equal Q.one c then p1
diff --git a/src_colibri2/theories/LRA/polynome.mli b/src_colibri2/theories/LRA/polynome.mli
index 3f5a6f05386cd495818858b207055f75e082d054..bd9ada2d33d7ad18311f08858158832a6b6a9605 100644
--- a/src_colibri2/theories/LRA/polynome.mli
+++ b/src_colibri2/theories/LRA/polynome.mli
@@ -46,6 +46,7 @@ type kind = | ZERO | CST | VAR
 val classify : t -> kind
 
 val sub_cst: t -> Q.t -> t
+val add_cst: t -> Q.t -> t
 val mult_cst: Q.t -> t -> t
 
 val add: t -> t -> t
diff --git a/src_colibri2/theories/bool/boolean.ml b/src_colibri2/theories/bool/boolean.ml
index b3ee59315080ca2e4b318110d7a5a3bf3a55b9b2..7b8ce5a3032c9b12f4cf78d376cf1ccf5c29e9e8 100644
--- a/src_colibri2/theories/bool/boolean.ml
+++ b/src_colibri2/theories/bool/boolean.ml
@@ -189,32 +189,37 @@ module TwoWatchLiteral = struct
       let args = (Ground.sem r.ground).args in
       if o_from_start = o_from_end then Events.Wait.EnqStopped
       else
-        let n_end = IArray.get args o_from_end in
-        match check_if_equal_or_set d args r.absorbent (+1) o_from_end o_from_start
-                n_end with
-        | LimitReached ->
-          Context.Ref.set r.from_start o_from_end;
-          Events.Wait.EnqLast(fun d -> Egraph.merge d n_end (Ground.node r.ground))
-        | Absorbent ->
-          Events.Wait.EnqLast(fun d -> Egraph.set_value d (Ground.node r.ground) r.absorbent)
-        | ToWatch from_start ->
-          if from_start <> o_from_start then (
-            Context.Ref.set r.from_start from_start;
-            attach d r from_start;
-          );
-          let n_start = IArray.get args from_start in
-          match check_if_equal_or_set d args r.absorbent (-1) from_start o_from_end n_start with
+        match Egraph.Ro.get_value d (Ground.node r.ground) with
+        | Some v when not (Value.equal v r.absorbent) ->
+          Context.Ref.set r.from_end o_from_start;
+          Events.Wait.EnqLast(fun d -> IArray.iter ~f:(fun a -> Egraph.set_value d a v) args)
+        | _ ->
+          let n_end = IArray.get args o_from_end in
+          match check_if_equal_or_set d args r.absorbent (+1) o_from_end o_from_start
+                  n_end with
           | LimitReached ->
-            Context.Ref.set r.from_end from_start;
-            Events.Wait.EnqLast (fun d -> Egraph.merge d n_start (Ground.node r.ground))
+            Context.Ref.set r.from_start o_from_end;
+            Events.Wait.EnqLast(fun d -> Egraph.merge d n_end (Ground.node r.ground))
           | Absorbent ->
-            Events.Wait.EnqLast (fun d -> Egraph.set_value d (Ground.node r.ground) r.absorbent)
-          | ToWatch from_end when from_end = o_from_end ->
-            Events.Wait.EnqAlready
-          | ToWatch from_end ->
-            Context.Ref.set r.from_end from_end;
-            attach d r from_end;
-            Events.Wait.EnqAlready
+            Events.Wait.EnqLast(fun d -> Egraph.set_value d (Ground.node r.ground) r.absorbent)
+          | ToWatch from_start ->
+            if from_start <> o_from_start then (
+              Context.Ref.set r.from_start from_start;
+              attach d r from_start;
+            );
+            let n_start = IArray.get args from_start in
+            match check_if_equal_or_set d args r.absorbent (-1) from_start o_from_end n_start with
+            | LimitReached ->
+              Context.Ref.set r.from_end from_start;
+              Events.Wait.EnqLast (fun d -> Egraph.merge d n_start (Ground.node r.ground))
+            | Absorbent ->
+              Events.Wait.EnqLast (fun d -> Egraph.set_value d (Ground.node r.ground) r.absorbent)
+            | ToWatch from_end when from_end = o_from_end ->
+              Events.Wait.EnqAlready
+            | ToWatch from_end ->
+              Context.Ref.set r.from_end from_end;
+              attach d r from_end;
+              Events.Wait.EnqAlready
 
     let enqueue d event =
       match event with
@@ -229,34 +234,38 @@ module TwoWatchLiteral = struct
       if IArray.length args = 1 then
         Egraph.merge d (IArray.get args 0) (Ground.node ground)
       else
-      let r from_start from_end =
-        assert (from_start < from_end);
-        Debug.dprintf4 debug "Bool create %a (%i-%i)" Ground.pp ground
-          from_start from_end;
-        {
-          ground; absorbent;
-          from_start = Context.Ref.create (Egraph.context d) from_start;
-          from_end = Context.Ref.create (Egraph.context d) from_end;
-        }
-      in
-      match check_if_set (d :> Egraph.Ro.t) args absorbent 1 (IArray.length args - 1) 0 with
-      | LimitReached ->
-        Egraph.merge d (IArray.get args (IArray.length args - 1)) (Ground.node ground)
-      | Absorbent ->
-        Egraph.set_value d (Ground.node ground) absorbent
-      | ToWatch from_start ->
-        let n_start = IArray.get args from_start in
-        match
-          check_if_equal_or_set (d :> Egraph.Ro.t) args absorbent (-1) from_start (IArray.length args - 1) n_start
-        with
-        | LimitReached ->
-          Egraph.merge d n_start (Ground.node ground)
-        | Absorbent ->
-          Egraph.set_value d (Ground.node ground) absorbent
-        | ToWatch from_end ->
-          let r = r from_start from_end in
-          attach (d :> Egraph.Ro.t) r from_start;
-          attach (d :> Egraph.Ro.t) r from_end
+        match Egraph.get_value d (Ground.node ground) with
+        | Some v when not (Value.equal v absorbent) ->
+          IArray.iter ~f:(fun a -> Egraph.set_value d a v) args
+        | _ ->
+          let r from_start from_end =
+            assert (from_start < from_end);
+            Debug.dprintf4 debug "Bool create %a (%i-%i)" Ground.pp ground
+              from_start from_end;
+            {
+              ground; absorbent;
+              from_start = Context.Ref.create (Egraph.context d) from_start;
+              from_end = Context.Ref.create (Egraph.context d) from_end;
+            }
+          in
+          match check_if_set (d :> Egraph.Ro.t) args absorbent 1 (IArray.length args - 1) 0 with
+          | LimitReached ->
+            Egraph.merge d (IArray.get args (IArray.length args - 1)) (Ground.node ground)
+          | Absorbent ->
+            Egraph.set_value d (Ground.node ground) absorbent
+          | ToWatch from_start ->
+            let n_start = IArray.get args from_start in
+            match
+              check_if_equal_or_set (d :> Egraph.Ro.t) args absorbent (-1) from_start (IArray.length args - 1) n_start
+            with
+            | LimitReached ->
+              Egraph.merge d n_start (Ground.node ground)
+            | Absorbent ->
+              Egraph.set_value d (Ground.node ground) absorbent
+            | ToWatch from_end ->
+              let r = r from_start from_end in
+              attach (d :> Egraph.Ro.t) r from_start;
+              attach (d :> Egraph.Ro.t) r from_end
   end
 
   let () = Egraph.Wait.register_dem (module Daemon)
@@ -314,6 +323,9 @@ let converter d (f:Ground.t) =
   | { app = {builtin = Expr.False}; tyargs = []; args; _ } ->
     assert ( IArray.is_empty args);
     reg _false
+  | { ty; _ }
+    when Ground.Ty.equal ty Ground.Ty.bool  ->
+    Egraph.register_decision d (chobool res)
   | _ -> ()
 
 
diff --git a/src_colibri2/theories/quantifier/InvertedPath.ml b/src_colibri2/theories/quantifier/InvertedPath.ml
index 61c29b4c0cfbb75fe92fc644734344cb0134c3db..7cfbd188b89b626c02b7e1cac3c19b2f12fde9d5 100644
--- a/src_colibri2/theories/quantifier/InvertedPath.ml
+++ b/src_colibri2/theories/quantifier/InvertedPath.ml
@@ -129,7 +129,7 @@ let insert_pattern d (trigger : Trigger.t) =
   let rec aux (fp : F_Pos.t) pp_vars p =
     match p with
     | Pattern.Var v ->
-        let pairs = Expr.Term.Var.M.find_def [] v pp_vars in
+        let pairs = Expr.Term.Var.M.find_def [] v.var pp_vars in
         let pairs =
           List.filter
             (fun pp ->
diff --git a/src_colibri2/theories/quantifier/InvertedPath.mli b/src_colibri2/theories/quantifier/InvertedPath.mli
index c2551744b710523b0c167020b69b9811f1c9994f..9c0eda7e93c61d16d63c13b49ebd8603892dd1d7 100644
--- a/src_colibri2/theories/quantifier/InvertedPath.mli
+++ b/src_colibri2/theories/quantifier/InvertedPath.mli
@@ -31,7 +31,7 @@ val pc_ips : t HPC.t
 
 module HPT : Datastructure.Memo with type key := F_Pos.t
 
-val pt_ips : (Ty.t * t) Context.Queue.t HPT.t
+val pt_ips : (Expr.Ty.t * t) Context.Queue.t HPT.t
 (** The database of inverted path for each parent-type, needed for polymorphic
     variables *)
 
diff --git a/src_colibri2/theories/quantifier/pattern.ml b/src_colibri2/theories/quantifier/pattern.ml
index 26af230dfaba9d4a79cc8cd93e8a02dc4a1e9bda..3412d3f4e3665d485736a329adc11c464131ca39 100644
--- a/src_colibri2/theories/quantifier/pattern.ml
+++ b/src_colibri2/theories/quantifier/pattern.ml
@@ -7,13 +7,15 @@ module T = struct
   open Base
 
   type t =
-    | Var of Expr.Term.Var.t
+    | Var of { var : Expr.Term.Var.t; ty : Expr.Ty.t }
     | App of F.t * Expr.Ty.t list * t IArray.t
     | Node of Node.t
   [@@deriving eq, ord, hash]
 
   let rec pp fmt = function
-    | Var v -> Expr.Term.Var.pp fmt v
+    | Var v ->
+        if Expr.Ty.equal v.var.ty v.ty then Expr.Term.Var.pp fmt v.var
+        else Fmt.pf fmt "(%a:%a)" Expr.Term.Var.pp v.var Expr.Ty.pp v.ty
     | App (f, tyl, tl) ->
         Fmt.pf fmt "%a[%a](%a)" F.pp f
           Fmt.(list ~sep:comma Expr.Ty.pp)
@@ -26,16 +28,29 @@ end
 include T
 include Popop_stdlib.MkDatatype (T)
 
+let rec convert_back (ty : Ground.Ty.t) =
+  Expr.Ty.apply ty.app (List.map convert_back ty.args)
+
+let rec subst_ty subst (ty : Expr.Ty.t) =
+  match ty.descr with
+  | Var v -> (
+      match Expr.Ty.Var.M.find_opt v subst with
+      | Some ty -> convert_back ty
+      | None -> ty)
+  | App (f, args) -> Expr.Ty.apply f (List.map (subst_ty subst) args)
+
 let rec of_term ~subst (t : Expr.Term.t) =
   match t.descr with
   | Var v -> (
       match Expr.Term.Var.M.find v subst.Ground.Subst.term with
-      | exception Not_found -> Var v (* todo type substitution *)
+      | exception Not_found -> Var { var = v; ty = subst_ty subst.ty v.ty }
       | n -> Node n)
   | App ({ builtin = Expr.Coercion; _ }, _, [ a ]) -> of_term ~subst a
   | App (f, tys, tl) ->
-      (* todo type substitution *)
-      App (f, tys, IArray.of_list_map ~f:(of_term ~subst) tl)
+      App
+        ( f,
+          List.map (subst_ty subst.ty) tys,
+          IArray.of_list_map ~f:(of_term ~subst) tl )
   | _ -> (* absurd *) assert false
 
 let init = Ground.Subst.S.singleton Ground.Subst.empty
@@ -76,10 +91,10 @@ let rec match_term d (substs : Ground.Subst.S.t) (n : Node.t) (p : t) :
         in
         Ground.Subst.S.fold_left
           (fun acc subst ->
-            match Expr.Term.Var.M.find v subst.term with
+            match Expr.Term.Var.M.find v.var subst.term with
             | exception Not_found ->
                 Ground.Subst.S.add
-                  { subst with term = Expr.Term.Var.M.add v n subst.term }
+                  { subst with term = Expr.Term.Var.M.add v.var n subst.term }
                   acc
             | n' ->
                 if Egraph.is_equal d n n' then Ground.Subst.S.add subst acc
@@ -123,7 +138,7 @@ let rec check_term d (subst : Ground.Subst.t) (n : Node.t) (p : t) : bool =
   | Var v -> (
       Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty) (Ground.tys d n)
       &&
-      match Expr.Term.Var.M.find v subst.term with
+      match Expr.Term.Var.M.find v.var subst.term with
       | exception Not_found -> false
       | n' -> Egraph.is_equal d n n')
   | App (pf, ptyl, pargs) ->
@@ -150,7 +165,7 @@ exception IncompleteSubstitution
 let rec check_term_exists_exn d (subst : Ground.Subst.t) (p : t) : Node.S.t =
   match p with
   | Var v -> (
-      match Expr.Term.Var.M.find v subst.term with
+      match Expr.Term.Var.M.find v.var subst.term with
       | exception Not_found -> raise IncompleteSubstitution
       | n ->
           if
@@ -204,7 +219,8 @@ let check_term_exists d (subst : Ground.Subst.t) (p : t) : Node.S.t =
 let get_pps pattern =
   let rec aux acc fp p =
     match p with
-    | Var v -> Expr.Term.Var.M.add_change F_Pos.S.singleton F_Pos.S.add v fp acc
+    | Var v ->
+        Expr.Term.Var.M.add_change F_Pos.S.singleton F_Pos.S.add v.var fp acc
     | App (f, _, tl) ->
         IArray.foldi
           ~f:(fun pos acc p -> aux acc F_Pos.{ f; pos } p)
@@ -253,7 +269,10 @@ let match_any_term d (p : t) : Ground.Subst.S.t =
                 Context.Queue.fold
                   (fun acc n ->
                     Ground.Subst.S.add
-                      { subst with term = Expr.Term.Var.M.add v n subst.term }
+                      {
+                        subst with
+                        term = Expr.Term.Var.M.add v.var n subst.term;
+                      }
                       acc)
                   acc q)
               acc substs)
diff --git a/src_colibri2/theories/quantifier/pattern.mli b/src_colibri2/theories/quantifier/pattern.mli
index a545681a16217f9c9076b5f07d626883ae2fce6c..b820c35f9c8544e239cdd913d32398f0b3a68c07 100644
--- a/src_colibri2/theories/quantifier/pattern.mli
+++ b/src_colibri2/theories/quantifier/pattern.mli
@@ -1,8 +1,9 @@
 open Colibri2_popop_lib
+
 (** Pattern *)
 
 type t =
-  | Var of Expr.Term.Var.t
+  | Var of { var : Expr.Term.Var.t; ty : Expr.Ty.t }
   | App of F.t * Expr.Ty.t list * t IArray.t
   | Node of Node.t
 
diff --git a/src_colibri2/theories/quantifier/quantifier.ml b/src_colibri2/theories/quantifier/quantifier.ml
index aab7a0b9df825935ef1d1926fd23270aefe8a0cc..aff0944fd316b4c6e51e3b22add75dba38c18ddf 100644
--- a/src_colibri2/theories/quantifier/quantifier.ml
+++ b/src_colibri2/theories/quantifier/quantifier.ml
@@ -187,14 +187,11 @@ let skolemize d (e : Ground.ClosedQuantifier.t) =
       (fun acc v ->
         let id = Expr.Term.Const.mk v.Expr.name [] [] v.ty in
         let t = Expr.Term.apply id [] [] in
-        Expr.Term.Var.M.add v (Ground.convert t) acc)
+        Expr.Term.Var.M.add v (Ground.convert ~subst:e.subst t) acc)
       e.subst.term e.term_vars
   in
-  let n =
-    Ground.convert
-      ~subst:{ Ground.Subst.ty = e.subst.ty; term = subst_term }
-      e.body
-  in
+  let subst = { Ground.Subst.ty = e.subst.ty; term = subst_term } in
+  let n = Ground.convert ~subst e.body in
   Egraph.register d n;
   n
 
@@ -205,6 +202,8 @@ let init, attach =
       match (Ground.ThClosedQuantifier.sem th, b) with
       | ({ binder = Exists; _ } as e), true
       | ({ binder = Forall; _ } as e), false ->
+          Debug.dprintf2 debug "[Quant] Skolemize %a"
+            Ground.ThClosedQuantifier.pp th;
           Egraph.merge d (skolemize d e) n
       | { binder = Exists; _ }, false | { binder = Forall; _ }, true ->
           let triggers = Trigger.compute_top_triggers th in
diff --git a/src_colibri2/theories/quantifier/trigger.ml b/src_colibri2/theories/quantifier/trigger.ml
index 4232d43c936ae90e49541f2ac7e3f3a17de3d72b..d8f35bb6fe4f6765172eafe3c049228186a8b3a7 100644
--- a/src_colibri2/theories/quantifier/trigger.ml
+++ b/src_colibri2/theories/quantifier/trigger.ml
@@ -7,7 +7,7 @@ module T = struct
   type t = {
     pat : Pattern.t;
     checks : Pattern.t list;
-    (* list *) form : Ground.ThClosedQuantifier.t;
+    form : Ground.ThClosedQuantifier.t;
     eager : bool;
   }
   [@@deriving eq, ord, hash]
@@ -120,7 +120,7 @@ let compute_all_triggers (cq : Ground.ThClosedQuantifier.t) =
   let pats =
     List.filter_map
       (fun (c, (sty, st)) ->
-        if Expr.Ty.Var.S.equal sty tyvs && Expr.Term.Var.S.equal st tvs then
+        if Expr.Ty.Var.S.subset tyvs sty && Expr.Term.Var.S.subset tvs st then
           Some
             {
               pat = Pattern.of_term ~subst:cq'.subst c;
@@ -148,16 +148,7 @@ let add_trigger d t =
 let instantiate_aux d tri subst =
   let form = Ground.ThClosedQuantifier.sem tri.form in
   Debug.incr nb_instantiation;
-  let subst =
-    {
-      Ground.Subst.ty =
-        Expr.Ty.Var.M.union (fun _ _ -> assert false) subst.ty form.subst.ty;
-      Ground.Subst.term =
-        Expr.Term.Var.M.union
-          (fun _ _ -> assert false)
-          subst.Ground.Subst.term form.subst.term;
-    }
-  in
+  let subst = Ground.Subst.distinct_union subst form.subst in
   let n = Ground.convert ~subst form.body in
   if Debug.test_flag Debug.stats && not (Egraph.is_registered d n) then
     Debug.incr nb_new_instantiation;