diff --git a/src_colibri2/theories/quantifier/F.ml b/src_colibri2/theories/quantifier/F.ml
new file mode 100644
index 0000000000000000000000000000000000000000..5808c457a971f835439327de691ea08d85f10171
--- /dev/null
+++ b/src_colibri2/theories/quantifier/F.ml
@@ -0,0 +1,2 @@
+include Expr.Term.Const
+module EnvApps = Datastructure.Memo (Expr.Term.Const)
diff --git a/src_colibri2/theories/quantifier/F_Pos.ml b/src_colibri2/theories/quantifier/F_Pos.ml
new file mode 100644
index 0000000000000000000000000000000000000000..129677c36ef3b8d9b3d1814504ae94f3a6be1722
--- /dev/null
+++ b/src_colibri2/theories/quantifier/F_Pos.ml
@@ -0,0 +1,13 @@
+open Colibri2_popop_lib
+(** A position inside a function symbol *)
+
+module T = struct
+  let hash_fold_int = Base.hash_fold_int
+
+  type t = { f : F.t; pos : int } [@@deriving eq, ord, hash]
+
+  let pp fmt { f; pos } = Format.fprintf fmt "%a.%i" F.pp f pos
+end
+
+include T
+include Popop_stdlib.MkDatatype (T)
diff --git a/src_colibri2/theories/quantifier/F_Pos.mli b/src_colibri2/theories/quantifier/F_Pos.mli
new file mode 100644
index 0000000000000000000000000000000000000000..147e5028a5b0f1c5dfa9e808e40a55323364fa3d
--- /dev/null
+++ b/src_colibri2/theories/quantifier/F_Pos.mli
@@ -0,0 +1,5 @@
+(** A position inside a function symbol *)
+
+type t = { f : F.t; pos : int }
+
+include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
diff --git a/src_colibri2/theories/quantifier/InvertedPath.ml b/src_colibri2/theories/quantifier/InvertedPath.ml
new file mode 100644
index 0000000000000000000000000000000000000000..b4c195a3f6b0aac87e4490652cc87a8b7310c626
--- /dev/null
+++ b/src_colibri2/theories/quantifier/InvertedPath.ml
@@ -0,0 +1,161 @@
+open Colibri2_popop_lib
+open Common
+
+(**
+     1) match the subterm
+     2) possible patterns found
+     3) GoUp and match other arguments
+     4) goto 2)
+  *)
+
+type t' = {
+  matches : t Pattern.M.t;
+  triggers : Trigger.t list;
+  ups :
+    (Expr.Ty.t list * Pattern.t list * int (* from which argument we come *) * t)
+    list
+    F_Pos.M.t;
+}
+
+and t = t' Context.Ref.t [@@deriving show]
+
+let create creator =
+  Context.Ref.create creator
+    { matches = Pattern.M.empty; triggers = []; ups = F_Pos.M.empty }
+
+let rec exec d acc substs n ip =
+  Debug.dprintf5 debug_full "[Quant] Exec: %a, %a[%i]" Node.pp n
+    Ground.Subst.S.pp substs
+    (Ground.Subst.S.cardinal substs);
+  (* pp ip; *)
+  if Ground.Subst.S.is_empty substs then acc
+  else
+    let ip = Context.Ref.get ip in
+    let acc =
+      List.fold_left
+        (fun acc tr ->
+          Trigger.M.add_change (fun s -> s) Ground.Subst.S.union tr substs acc)
+        acc ip.triggers
+    in
+    let acc =
+      Pattern.M.fold_left
+        (fun acc p ip ->
+          Debug.dprintf2 debug_full "[Quant] Exec match %a" Pattern.pp p;
+          let substs = Pattern.match_term d substs n p in
+          exec d acc substs n ip)
+        acc ip.matches
+    in
+    let acc =
+      let match_one_app (ptyl, pargs, pos, t) acc g =
+        let { Ground.Term.tyargs = tyl; args; _ } = Ground.sem g in
+        let substs = List.fold_left2 Pattern.match_ty substs tyl ptyl in
+        let substs =
+          if Ground.Subst.S.is_empty substs then substs
+          else
+            let i = ref (-1) in
+            Base.List.fold2_exn args pargs ~init:substs
+              ~f:(fun substs args parg ->
+                incr i;
+                if Int.equal !i pos then substs
+                else Pattern.match_term d substs args parg)
+        in
+        exec d acc substs (Ground.node g) t
+      in
+      let info =
+        Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
+      in
+      match info with
+      | None -> acc (* a class without term associated, can't be matched *)
+      | Some info ->
+          let forall_triplets parents acc pt =
+            Ground.S.fold_left (match_one_app pt) acc parents
+          in
+          let forall_fpos acc p ptl =
+            Debug.dprintf2 debug_full "[Quant] Exec ups %a@." F_Pos.pp p;
+            let parents = F_Pos.M.find_def Ground.S.empty p info.parents in
+            List.fold_left (forall_triplets parents) acc ptl
+          in
+          F_Pos.M.fold_left forall_fpos acc ip.ups
+    in
+    acc
+
+module HPC = Datastructure.Memo (PC)
+module HPP = Datastructure.Memo (PP)
+module HPT = Datastructure.Memo (F_Pos)
+
+(** parent-child, wait for a subterm with the right application *)
+let pc_ips = HPC.create Fmt.nop "Quantifier.pc" create
+
+(** parent-parent, a variable appears two times *)
+let pp_ips = HPP.create Fmt.nop "Quantifier.pp" create
+
+(** parent-type, wait for a type to match a variable *)
+let pt_ips = HPT.create Fmt.nop "Quantifier.pt" Context.Queue.create
+
+let add_pattern pat ip =
+  Context.Ref.set ip
+  @@
+  let ip = Context.Ref.get ip in
+  { ip with triggers = pat :: ip.triggers }
+
+let add_match pat ipr =
+  let ip = Context.Ref.get ipr in
+  match Pattern.M.find_opt pat ip.matches with
+  | Some ip' -> ip'
+  | None ->
+      let ip' = create (Context.Ref.creator ipr) in
+      Context.Ref.set ipr
+      @@ { ip with matches = Pattern.M.add pat ip' ip.matches };
+      ip'
+
+let add_up f_pos tytl tl ipr =
+  let ip = Context.Ref.get ipr in
+  let ip' = create (Context.Ref.creator ipr) in
+  Context.Ref.set ipr
+  @@ {
+       ip with
+       ups =
+         F_Pos.M.add_change Lists.singleton Lists.add f_pos
+           (tytl, tl, f_pos.pos, ip') ip.ups;
+     };
+  ip'
+
+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 =
+          List.filter
+            (fun pp ->
+              (* possible just parent1 only one path is needed by pairs *)
+              F_Pos.equal fp pp.PP.parent1 || F_Pos.equal fp pp.PP.parent2)
+            pairs
+        in
+        let ips = List.map (fun pp -> HPP.find pp_ips d pp) pairs in
+        let ip = create (Egraph.context d) in
+        Context.Queue.push (HPT.find pt_ips d fp) (v.ty, ip);
+        let ips = ip :: ips in
+        let ips = List.map (add_match p) ips in
+        ips
+    | App (f, tytl, tl) ->
+        let ips = insert_children pp_vars f tytl tl in
+        let ip = HPC.find pc_ips d PC.{ parent = fp; child = f } in
+        let ip = add_match p ip in
+        let ips = ip :: ips in
+        ips
+  and insert_children pp_vars f tytl tl =
+    List.foldi
+      (fun acc pos p ->
+        let f_pos = F_Pos.{ f; pos } in
+        let ips = aux f_pos pp_vars p in
+        let ips = List.map (add_up f_pos tytl tl) ips in
+        ips @ acc)
+      [] tl
+  in
+  let pp_vars = Pattern.get_pps trigger.pat in
+  match trigger.pat with
+  | Var _ -> ()
+  | App (f, tytl, tl) ->
+      let ips = insert_children pp_vars f tytl tl in
+      List.iter (add_pattern trigger) ips
diff --git a/src_colibri2/theories/quantifier/InvertedPath.mli b/src_colibri2/theories/quantifier/InvertedPath.mli
new file mode 100644
index 0000000000000000000000000000000000000000..d53db87e94bd983e32e9d9fcb702b0f9e000639f
--- /dev/null
+++ b/src_colibri2/theories/quantifier/InvertedPath.mli
@@ -0,0 +1,36 @@
+type t
+(** An InvertedPath is a way to go from one subterm of patterns to substitution
+   of triggers. It allows to know which nodes merge can create new substitutions
+   for a pattern and how to find them. Cf Efficient E-matching + modifications *)
+
+val pp : t Fmt.t
+
+val exec :
+  Egraph.t ->
+  Ground.Subst.S.t Trigger.M.t ->
+  Ground.Subst.S.t ->
+  Node.t ->
+  t ->
+  Ground.Subst.S.t Trigger.M.t
+(** [exec d acc substs n ip] adds to [acc] new substitutions to the triggers
+   that are obtained by the execution of the invertedpath *)
+
+val insert_pattern : Egraph.t -> Trigger.t -> unit
+(** [insert_pattern d tri] insert the pattern in the database of inverted path
+   for all its subterms *)
+
+module HPP : Datastructure.Sig3 with type key := PP.t
+
+val pp_ips : t HPP.t
+(** The database of inverted path for each parent-parent pairs *)
+
+module HPC : Datastructure.Sig3 with type key := PC.t
+
+val pc_ips : t HPC.t
+(** The database of inverted path for each parent-child pairs *)
+
+module HPT : Datastructure.Sig3 with type key := F_Pos.t
+
+val pt_ips : (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/PC.ml b/src_colibri2/theories/quantifier/PC.ml
new file mode 100644
index 0000000000000000000000000000000000000000..485fa19fa739a844853c088ef1f88068c238b7ae
--- /dev/null
+++ b/src_colibri2/theories/quantifier/PC.ml
@@ -0,0 +1,11 @@
+open Colibri2_popop_lib
+
+module T = struct
+  open! Base
+
+  type t = { parent : F_Pos.t; child : F.t }
+  [@@deriving show, ord, hash, show, eq]
+end
+
+include T
+include Popop_stdlib.MkDatatype (T)
diff --git a/src_colibri2/theories/quantifier/PC.mli b/src_colibri2/theories/quantifier/PC.mli
new file mode 100644
index 0000000000000000000000000000000000000000..f6c39f6f0cd9609dfb88e94812f6be59255c4803
--- /dev/null
+++ b/src_colibri2/theories/quantifier/PC.mli
@@ -0,0 +1,3 @@
+type t = { parent : F_Pos.t; child : F.t }
+
+include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
diff --git a/src_colibri2/theories/quantifier/PP.ml b/src_colibri2/theories/quantifier/PP.ml
new file mode 100644
index 0000000000000000000000000000000000000000..1cebebf12219c955ffd0c8db6ce61a86358d9a2e
--- /dev/null
+++ b/src_colibri2/theories/quantifier/PP.ml
@@ -0,0 +1,15 @@
+open Colibri2_popop_lib
+
+module T = struct
+  open! Base
+
+  type t = { parent1 : F_Pos.t; parent2 : F_Pos.t }
+  [@@deriving show, ord, hash, show, eq]
+end
+
+include T
+include Popop_stdlib.MkDatatype (T)
+
+let create p1 p2 =
+  if F_Pos.compare p1 p2 <= 0 then { parent1 = p1; parent2 = p2 }
+  else { parent1 = p1; parent2 = p2 }
diff --git a/src_colibri2/theories/quantifier/PP.mli b/src_colibri2/theories/quantifier/PP.mli
new file mode 100644
index 0000000000000000000000000000000000000000..3302a3c13bfa8ae939ea5f0d7672e103defdf366
--- /dev/null
+++ b/src_colibri2/theories/quantifier/PP.mli
@@ -0,0 +1,5 @@
+type t = private { parent1 : F_Pos.t; parent2 : F_Pos.t }
+
+include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
+
+val create : F_Pos.t -> F_Pos.t -> t
diff --git a/src_colibri2/theories/quantifier/common.ml b/src_colibri2/theories/quantifier/common.ml
new file mode 100644
index 0000000000000000000000000000000000000000..694237dad67d2ad624c69dbdda3297947169d63b
--- /dev/null
+++ b/src_colibri2/theories/quantifier/common.ml
@@ -0,0 +1,12 @@
+open Colibri2_popop_lib
+
+let debug =
+  Debug.register_info_flag ~desc:"Handling of quantifiers" "quantifiers"
+
+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_new_instantiation =
+  Debug.register_stats_int ~name:"new_instantiation" ~init:0
diff --git a/src_colibri2/theories/quantifier/info.ml b/src_colibri2/theories/quantifier/info.ml
new file mode 100644
index 0000000000000000000000000000000000000000..60b1d78ec9f4bb26a41bc80f351b02b9ef06024e
--- /dev/null
+++ b/src_colibri2/theories/quantifier/info.ml
@@ -0,0 +1,27 @@
+(** information added on nodes *)
+
+type t = {
+  parents : Ground.S.t F_Pos.M.t;  (** parents *)
+  apps : Ground.S.t F.M.t;  (** parent parent *)
+  tys : Ground.Ty.S.t;
+}
+[@@deriving show]
+
+let _ = pp
+
+let merge info info' =
+  {
+    parents =
+      F_Pos.M.union
+        (fun _ a b -> Some (Ground.S.union a b))
+        info.parents info'.parents;
+    apps =
+      F.M.union (fun _ a b -> Some (Ground.S.union a b)) info.apps info'.apps;
+    tys = Ground.Ty.S.union info.tys info'.tys;
+  }
+
+let empty =
+  { parents = F_Pos.M.empty; apps = F.M.empty; tys = Ground.Ty.S.empty }
+
+let env =
+  Datastructure.HNode.create (Fmt.nop : t Format.printer) "Quantifier.info"
diff --git a/src_colibri2/theories/quantifier/pattern.ml b/src_colibri2/theories/quantifier/pattern.ml
new file mode 100644
index 0000000000000000000000000000000000000000..00bca883795271a5798f5b4c90783a2159c099a1
--- /dev/null
+++ b/src_colibri2/theories/quantifier/pattern.ml
@@ -0,0 +1,268 @@
+open Colibri2_popop_lib
+open Common
+
+(** Patterns *)
+
+module T = struct
+  open Base
+
+  type t = Var of Expr.Term.Var.t | App of F.t * Expr.Ty.t list * t list
+  [@@deriving eq, ord, hash]
+
+  let rec pp fmt = function
+    | Var v -> Expr.Term.Var.pp fmt v
+    | App (f, tyl, tl) ->
+        Fmt.pf fmt "%a[%a](%a)" F.pp f
+          Fmt.(list ~sep:comma Expr.Ty.pp)
+          tyl
+          Fmt.(list ~sep:comma pp)
+          tl
+end
+
+include T
+include Popop_stdlib.MkDatatype (T)
+
+let rec of_term (t : Expr.Term.t) =
+  match t.descr with
+  | Var v -> Var v
+  | App ({ builtin = Expr.Coercion; _ }, _, [ a ]) -> of_term a
+  | App (f, tys, tl) -> App (f, tys, List.map of_term tl)
+  | _ -> (* absurd *) assert false
+
+let init = Ground.Subst.S.singleton Ground.Subst.empty
+
+let rec match_ty substs (ty : Ground.Ty.t) (p : Expr.Ty.t) : Ground.Subst.S.t =
+  match p.descr with
+  | Var v ->
+      Ground.Subst.S.fold_left
+        (fun acc subst ->
+          match Expr.Ty.Var.M.find v subst.ty with
+          | exception Not_found ->
+              Ground.Subst.S.add
+                { subst with ty = Expr.Ty.Var.M.add v ty subst.ty }
+                acc
+          | ty' ->
+              if Ground.Ty.equal ty ty' then Ground.Subst.S.add subst acc
+              else acc)
+        Ground.Subst.S.empty substs
+  | App (cst', tyl') ->
+      if Expr.Ty.Const.equal ty.app cst' then
+        List.fold_left2 match_ty substs ty.args tyl'
+      else Ground.Subst.S.empty
+
+let rec match_term d (substs : Ground.Subst.S.t) (n : Node.t) (p : t) :
+    Ground.Subst.S.t =
+  Debug.dprintf4 debug "[Quant] matching %a %a" pp p Node.pp n;
+  if Ground.Subst.S.is_empty substs then substs
+  else
+    match p with
+    | Var v ->
+        let match_ty (acc : Ground.Subst.S.t) ty : Ground.Subst.S.t =
+          Debug.dprintf4 debug "[Quant] match_ty %a %a" Expr.Ty.pp v.ty
+            Ground.Ty.pp ty;
+          Ground.Subst.S.union (match_ty substs ty v.ty) acc
+        in
+        let substs =
+          match Datastructure.HNode.find_opt Info.env d n with
+          | None -> Ground.Subst.S.empty
+          | Some info ->
+              Ground.Ty.S.fold_left match_ty Ground.Subst.S.empty info.tys
+        in
+        Ground.Subst.S.fold_left
+          (fun acc subst ->
+            match Expr.Term.Var.M.find v subst.term with
+            | exception Not_found ->
+                Ground.Subst.S.add
+                  { subst with term = Expr.Term.Var.M.add v n subst.term }
+                  acc
+            | n' ->
+                if Egraph.is_equal d n n' then Ground.Subst.S.add subst acc
+                else acc)
+          Ground.Subst.S.empty substs
+    | App (pf, ptyl, pargs) ->
+        let s =
+          Opt.get_def Ground.S.empty
+          @@
+          let open Option in
+          let* info =
+            Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
+          in
+          F.M.find_opt pf info.apps
+        in
+        Ground.S.fold_left
+          (fun acc n ->
+            let { Ground.Term.app = f; tyargs = tyl; args; _ } = Ground.sem n in
+            assert (Term.Const.equal f pf);
+            let substs = List.fold_left2 match_ty substs tyl ptyl in
+            if Ground.Subst.S.is_empty substs then acc
+            else
+              let substs =
+                Base.List.fold2_exn args pargs ~init:substs ~f:(match_term d)
+              in
+              Ground.Subst.S.union substs acc)
+          Ground.Subst.S.empty s
+
+let rec check_ty subst (ty : Ground.Ty.t) (p : Expr.Ty.t) : bool =
+  match p.descr with
+  | Var v -> (
+      match Expr.Ty.Var.M.find v subst.Ground.Subst.ty with
+      | exception Not_found -> false
+      | ty' -> Ground.Ty.equal ty ty' )
+  | App (cst', tyl') ->
+      Expr.Ty.Const.equal ty.app cst'
+      && List.for_all2 (check_ty subst) ty.args tyl'
+
+let rec check_term d (subst : Ground.Subst.t) (n : Node.t) (p : t) : bool =
+  Debug.dprintf4 debug "[Quant] check %a %a" pp p Node.pp n;
+  match p with
+  | Var v -> (
+      match Datastructure.HNode.find_opt Info.env d n with
+      | None -> false
+      | Some info -> (
+          Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty) info.tys
+          &&
+          match Expr.Term.Var.M.find v subst.term with
+          | exception Not_found -> false
+          | n' -> Egraph.is_equal d n n' ) )
+  | App (pf, ptyl, pargs) ->
+      let s =
+        Opt.get_def Ground.S.empty
+        @@
+        let open Option in
+        let* info =
+          Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
+        in
+        F.M.find_opt pf info.apps
+      in
+      Ground.S.exists
+        (fun n ->
+          let { Ground.Term.app = f; tyargs = tyl; args; _ } = Ground.sem n in
+          assert (Term.Const.equal f pf);
+          List.for_all2 (check_ty subst) tyl ptyl
+          && List.for_all2 (check_term d subst) args pargs)
+        s
+
+let _ = check_term
+
+exception IncompleteSubstitution
+
+let rec check_term_exists 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
+      | exception Not_found -> raise IncompleteSubstitution
+      | n -> (
+          match Datastructure.HNode.find_opt Info.env d n with
+          | None -> raise Not_found
+          | Some info ->
+              if
+                (Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty)) info.tys
+              then Node.S.singleton (Egraph.find_def d n)
+              else raise Not_found ) )
+  | App (pf, ptyl, []) ->
+      let g =
+        Ground.apply pf (List.map (Ground.Ty.convert subst.ty) ptyl) []
+        |> Ground.index |> Ground.node
+      in
+      if Egraph.is_registered d g then Node.S.singleton (Egraph.find d g)
+      else raise Not_found
+  | App (pf, ptyl, parg :: pargs) ->
+      let find_app pos n =
+        Opt.get_def Ground.S.empty
+        @@
+        let open Option in
+        let* info =
+          Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
+        in
+        F_Pos.M.find_opt { f = pf; pos } info.parents
+      in
+      let get_parents pos parg =
+        let nodes = check_term_exists d subst parg in
+        let nodes =
+          Node.S.fold_left
+            (fun acc n ->
+              Ground.S.fold_left
+                (fun acc g ->
+                  if List.for_all2 (check_ty subst) (Ground.sem g).tyargs ptyl
+                  then Node.S.add (Ground.node g) acc
+                  else acc)
+                acc (find_app pos n))
+            Node.S.empty nodes
+        in
+        nodes
+      in
+      let nodes =
+        Lists.fold_lefti
+          (fun acc i parg -> Node.S.inter acc (get_parents (i + 1) parg))
+          (get_parents 0 parg) pargs
+      in
+      if Node.S.is_empty nodes then raise Not_found else nodes
+
+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
+    | App (f, _, tl) ->
+        List.foldi (fun acc pos p -> aux acc F_Pos.{ f; pos } p) acc tl
+  in
+  let m =
+    match pattern with
+    | Var _ -> Expr.Term.Var.M.empty
+    | App (f, _, tl) ->
+        List.foldi
+          (fun acc pos p -> aux acc F_Pos.{ f; pos } p)
+          Expr.Term.Var.M.empty tl
+  in
+  Expr.Term.Var.M.map
+    (fun s ->
+      let l = F_Pos.S.elements s in
+      Lists.fold_product
+        (fun acc parents1 parents2 ->
+          if F_Pos.equal parents1 parents2 then acc
+          else PP.create parents1 parents2 :: acc)
+        [] l l)
+    m
+
+let env_ground_by_apps =
+  F.EnvApps.create
+    (Context.Queue.pp ~sep:Fmt.semi Ground.pp)
+    "Quantifier.Trigger.ground_by_apps" Context.Queue.create
+
+module EnvTy = Datastructure.Memo (Ground.Ty)
+
+let env_node_by_ty =
+  EnvTy.create
+    (Context.Queue.pp ~sep:Fmt.semi Node.pp)
+    "Quantifier.Trigger.node_by_ty" Context.Queue.create
+
+let match_any_term d (p : t) : Ground.Subst.S.t =
+  Debug.dprintf2 debug "[Quant] matching any for %a" pp p;
+  match p with
+  | Var v ->
+      EnvTy.fold
+        (fun ty q acc ->
+          let substs = match_ty init ty v.ty in
+          if Ground.Subst.S.is_empty substs then acc
+          else
+            Ground.Subst.S.fold_left
+              (fun acc subst ->
+                Context.Queue.fold
+                  (fun acc n ->
+                    Ground.Subst.S.add
+                      { subst with term = Expr.Term.Var.M.add v n subst.term }
+                      acc)
+                  acc q)
+              acc substs)
+        env_node_by_ty d Ground.Subst.S.empty
+  | App (pf, ptyl, pargs) ->
+      Context.Queue.fold
+        (fun acc n ->
+          let { Ground.Term.app = f; tyargs = tyl; args; _ } = Ground.sem n in
+          assert (Term.Const.equal f pf);
+          let substs = List.fold_left2 match_ty init tyl ptyl in
+          let substs =
+            Base.List.fold2_exn args pargs ~init:substs ~f:(match_term d)
+          in
+          Ground.Subst.S.union substs acc)
+        Ground.Subst.S.empty
+        (F.EnvApps.find env_ground_by_apps d pf)
diff --git a/src_colibri2/theories/quantifier/pattern.mli b/src_colibri2/theories/quantifier/pattern.mli
new file mode 100644
index 0000000000000000000000000000000000000000..bf9565f7df0f2ee5b8a6f2d449fad08836a65bf2
--- /dev/null
+++ b/src_colibri2/theories/quantifier/pattern.mli
@@ -0,0 +1,46 @@
+(** Pattern *)
+
+type t = Var of Expr.Term.Var.t | App of F.t * Expr.Ty.t list * t list
+
+include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
+
+val of_term : Expr.Term.t -> t
+
+val init : Ground.Subst.S.t
+(** Singleton set with the empty substitution, can be used as initial value for
+   the following function. [Ground.Subst.empty] should not be used since the
+   functions will do nothing *)
+
+val match_ty : Ground.Subst.S.t -> Ground.Ty.t -> Expr.Ty.t -> Ground.Subst.S.t
+(** [match_ty substs ty pat] match the [ty] on [pat] for each substitution of
+   [substs] *)
+
+val match_term : Egraph.t -> Ground.Subst.S.t -> Node.t -> t -> Ground.Subst.S.t
+(** [match_term d substs n pat] match the node [n] on the pattern [pat] for each
+   substitution. Each return substitution corresponds to one given as input. *)
+
+val check_ty : Ground.Subst.t -> Ground.Ty.t -> Expr.Ty.t -> bool
+(** [check_ty subst ty pat] checks that the pattern [pat] substituted by [subst]
+    is equal to ty *)
+
+val check_term : Egraph.t -> Ground.Subst.t -> Node.t -> t -> bool
+(** [check_term d subst n pat] checks the pattern [pat] subsituted by [subst] is
+   equal by congruence of [d] to [n] *)
+
+val check_term_exists : Egraph.t -> Ground.Subst.t -> t -> Node.S.t
+(** [check_term_exists d subst pat] return nodes that are equal to the pattern
+   [pat] substituted by the substitution [subst] *)
+
+val match_any_term : Egraph.t -> t -> Ground.Subst.S.t
+(** [match_any_term d pat] match the pattern [pat] to all the nodes of [d] *)
+
+val get_pps : t -> PP.t list Term.Var.M.t
+(** [get_pps pat] return the parent-parent pairs in [pat] for each variables *)
+
+val env_ground_by_apps : Ground.t Context.Queue.t F.EnvApps.t
+(** The set of ground terms sorted by their top function *)
+
+module EnvTy : Datastructure.Sig3 with type key := Ground.Ty.t
+
+val env_node_by_ty : Node.t Context.Queue.t EnvTy.t
+(** The set of nodes sorted by their type. A node can have multiple types *)
diff --git a/src_colibri2/theories/quantifier/quantifier.ml b/src_colibri2/theories/quantifier/quantifier.ml
index fcc771de8d79908cfe7017de6bcff5bc0d03231c..79e651f97b8bd96b6f44bb6b359c468d3fcb03cc 100644
--- a/src_colibri2/theories/quantifier/quantifier.ml
+++ b/src_colibri2/theories/quantifier/quantifier.ml
@@ -2,778 +2,7 @@
 
 open Colibri2_popop_lib
 open Colibri2_core
-
-module F = struct
-  include Expr.Term.Const
-  module EnvApps = Datastructure.Memo (Expr.Term.Const)
-end
-
-let debug =
-  Debug.register_info_flag ~desc:"Handling of quantifiers" "quantifiers"
-
-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_new_instantiation =
-  Debug.register_stats_int ~name:"new_instantiation" ~init:0
-
-(** instantiated function symbol *)
-module FA = struct
-  module T = struct
-    let hash_fold_list = Base.hash_fold_list
-
-    type t = { tc : F.t; ta : Expr.Ty.t list } [@@deriving eq, ord, hash]
-
-    let pp fmt { tc; ta } =
-      Format.fprintf fmt "%a[%a]" F.pp tc (Pp.list Pp.colon Expr.Ty.pp) ta
-  end
-
-  include T
-  include Popop_stdlib.MkDatatype (T)
-end
-
-(** A position inside a function symbol *)
-module FA_Pos = struct
-  module T = struct
-    let hash_fold_int = Base.hash_fold_int
-
-    type t = { fa : FA.t; pos : int } [@@deriving eq, ord, hash]
-
-    let pp fmt { fa; pos } = Format.fprintf fmt "%a.%i" FA.pp fa pos
-  end
-
-  include T
-  include Popop_stdlib.MkDatatype (T)
-end
-
-(** A position inside a function symbol *)
-module F_Pos = struct
-  module T = struct
-    let hash_fold_int = Base.hash_fold_int
-
-    type t = { f : F.t; pos : int } [@@deriving eq, ord, hash]
-
-    let pp fmt { f; pos } = Format.fprintf fmt "%a.%i" F.pp f pos
-  end
-
-  include T
-  include Popop_stdlib.MkDatatype (T)
-end
-
-(** information added on nodes *)
-module Info = struct
-  type t = {
-    parents : Ground.S.t F_Pos.M.t;  (** parents *)
-    apps : Ground.S.t F.M.t;  (** parent parent *)
-    tys : Ground.Ty.S.t;
-  }
-  [@@deriving show]
-
-  let _ = pp
-
-  let merge info info' =
-    {
-      parents =
-        F_Pos.M.union
-          (fun _ a b -> Some (Ground.S.union a b))
-          info.parents info'.parents;
-      apps =
-        F.M.union (fun _ a b -> Some (Ground.S.union a b)) info.apps info'.apps;
-      tys = Ground.Ty.S.union info.tys info'.tys;
-    }
-
-  let empty =
-    { parents = F_Pos.M.empty; apps = F.M.empty; tys = Ground.Ty.S.empty }
-
-  let env =
-    Datastructure.HNode.create (Fmt.nop : t Format.printer) "Quantifier.info"
-end
-
-module PC = struct
-  module T = struct
-    open! Base
-
-    type t = { parent : F_Pos.t; child : F.t }
-    [@@deriving show, ord, hash, show, eq]
-  end
-
-  include T
-  include Popop_stdlib.MkDatatype (T)
-end
-
-module PP = struct
-  module T = struct
-    open! Base
-
-    type t = { parent1 : F_Pos.t; parent2 : F_Pos.t }
-    [@@deriving show, ord, hash, show, eq]
-  end
-
-  include T
-  include Popop_stdlib.MkDatatype (T)
-
-  let create p1 p2 =
-    if F_Pos.compare p1 p2 <= 0 then { parent1 = p1; parent2 = p2 }
-    else { parent1 = p1; parent2 = p2 }
-end
-
-module Pattern = struct
-  module T = struct
-    open Base
-
-    type t = Var of Expr.Term.Var.t | App of F.t * Expr.Ty.t list * t list
-    [@@deriving eq, ord, hash]
-
-    let rec pp fmt = function
-      | Var v -> Expr.Term.Var.pp fmt v
-      | App (f, tyl, tl) ->
-          Fmt.pf fmt "%a[%a](%a)" F.pp f
-            Fmt.(list ~sep:comma Expr.Ty.pp)
-            tyl
-            Fmt.(list ~sep:comma pp)
-            tl
-  end
-
-  include T
-  include Popop_stdlib.MkDatatype (T)
-
-  let rec of_term (t : Expr.Term.t) =
-    match t.descr with
-    | Var v -> Var v
-    | App ({ builtin = Expr.Coercion; _ }, _, [ a ]) -> of_term a
-    | App (f, tys, tl) -> App (f, tys, List.map of_term tl)
-    | _ -> (* absurd *) assert false
-
-  let init = Ground.Subst.S.singleton Ground.Subst.empty
-
-  let rec match_ty substs (ty : Ground.Ty.t) (p : Expr.Ty.t) : Ground.Subst.S.t
-      =
-    match p.descr with
-    | Var v ->
-        Ground.Subst.S.fold_left
-          (fun acc subst ->
-            match Expr.Ty.Var.M.find v subst.ty with
-            | exception Not_found ->
-                Ground.Subst.S.add
-                  { subst with ty = Expr.Ty.Var.M.add v ty subst.ty }
-                  acc
-            | ty' ->
-                if Ground.Ty.equal ty ty' then Ground.Subst.S.add subst acc
-                else acc)
-          Ground.Subst.S.empty substs
-    | App (cst', tyl') ->
-        if Expr.Ty.Const.equal ty.app cst' then
-          List.fold_left2 match_ty substs ty.args tyl'
-        else Ground.Subst.S.empty
-
-  let rec match_term d (substs : Ground.Subst.S.t) (n : Node.t) (p : t) :
-      Ground.Subst.S.t =
-    Debug.dprintf4 debug "[Quant] matching %a %a" pp p Node.pp n;
-    if Ground.Subst.S.is_empty substs then substs
-    else
-      match p with
-      | Var v ->
-          let match_ty (acc : Ground.Subst.S.t) ty : Ground.Subst.S.t =
-            Debug.dprintf4 debug "[Quant] match_ty %a %a" Expr.Ty.pp v.ty
-              Ground.Ty.pp ty;
-            Ground.Subst.S.union (match_ty substs ty v.ty) acc
-          in
-          let substs =
-            match Datastructure.HNode.find_opt Info.env d n with
-            | None -> Ground.Subst.S.empty
-            | Some info ->
-                Ground.Ty.S.fold_left match_ty Ground.Subst.S.empty info.tys
-          in
-          Ground.Subst.S.fold_left
-            (fun acc subst ->
-              match Expr.Term.Var.M.find v subst.term with
-              | exception Not_found ->
-                  Ground.Subst.S.add
-                    { subst with term = Expr.Term.Var.M.add v n subst.term }
-                    acc
-              | n' ->
-                  if Egraph.is_equal d n n' then Ground.Subst.S.add subst acc
-                  else acc)
-            Ground.Subst.S.empty substs
-      | App (pf, ptyl, pargs) ->
-          let s =
-            Opt.get_def Ground.S.empty
-            @@
-            let open Option in
-            let* info =
-              Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
-            in
-            F.M.find_opt pf info.apps
-          in
-          Ground.S.fold_left
-            (fun acc n ->
-              let { Ground.Term.app = f; tyargs = tyl; args; _ } =
-                Ground.sem n
-              in
-              assert (Term.Const.equal f pf);
-              let substs = List.fold_left2 match_ty substs tyl ptyl in
-              if Ground.Subst.S.is_empty substs then acc
-              else
-                let substs =
-                  Base.List.fold2_exn args pargs ~init:substs ~f:(match_term d)
-                in
-                Ground.Subst.S.union substs acc)
-            Ground.Subst.S.empty s
-
-  let rec check_ty subst (ty : Ground.Ty.t) (p : Expr.Ty.t) : bool =
-    match p.descr with
-    | Var v -> (
-        match Expr.Ty.Var.M.find v subst.Ground.Subst.ty with
-        | exception Not_found -> false
-        | ty' -> Ground.Ty.equal ty ty' )
-    | App (cst', tyl') ->
-        Expr.Ty.Const.equal ty.app cst'
-        && List.for_all2 (check_ty subst) ty.args tyl'
-
-  let rec check_term d (subst : Ground.Subst.t) (n : Node.t) (p : t) : bool =
-    Debug.dprintf4 debug "[Quant] check %a %a" pp p Node.pp n;
-    match p with
-    | Var v -> (
-        match Datastructure.HNode.find_opt Info.env d n with
-        | None -> false
-        | Some info -> (
-            Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty) info.tys
-            &&
-            match Expr.Term.Var.M.find v subst.term with
-            | exception Not_found -> false
-            | n' -> Egraph.is_equal d n n' ) )
-    | App (pf, ptyl, pargs) ->
-        let s =
-          Opt.get_def Ground.S.empty
-          @@
-          let open Option in
-          let* info =
-            Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
-          in
-          F.M.find_opt pf info.apps
-        in
-        Ground.S.exists
-          (fun n ->
-            let { Ground.Term.app = f; tyargs = tyl; args; _ } = Ground.sem n in
-            assert (Term.Const.equal f pf);
-            List.for_all2 (check_ty subst) tyl ptyl
-            && List.for_all2 (check_term d subst) args pargs)
-          s
-
-  let _ = check_term
-
-  exception IncompleteSubstitution
-
-  let rec check_term_exists 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
-        | exception Not_found -> raise IncompleteSubstitution
-        | n -> (
-            match Datastructure.HNode.find_opt Info.env d n with
-            | None -> raise Not_found
-            | Some info ->
-                if
-                  (Ground.Ty.S.exists (fun ty -> check_ty subst ty v.ty))
-                    info.tys
-                then Node.S.singleton (Egraph.find_def d n)
-                else raise Not_found ) )
-    | App (pf, ptyl, []) ->
-        let g =
-          Ground.apply pf (List.map (Ground.Ty.convert subst.ty) ptyl) []
-          |> Ground.index |> Ground.node
-        in
-        if Egraph.is_registered d g then Node.S.singleton (Egraph.find d g)
-        else raise Not_found
-    | App (pf, ptyl, parg :: pargs) ->
-        let find_app pos n =
-          Opt.get_def Ground.S.empty
-          @@
-          let open Option in
-          let* info =
-            Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
-          in
-          F_Pos.M.find_opt { f = pf; pos } info.parents
-        in
-        let get_parents pos parg =
-          let nodes = check_term_exists d subst parg in
-          let nodes =
-            Node.S.fold_left
-              (fun acc n ->
-                Ground.S.fold_left
-                  (fun acc g ->
-                    if List.for_all2 (check_ty subst) (Ground.sem g).tyargs ptyl
-                    then Node.S.add (Ground.node g) acc
-                    else acc)
-                  acc (find_app pos n))
-              Node.S.empty nodes
-          in
-          nodes
-        in
-        let nodes =
-          Lists.fold_lefti
-            (fun acc i parg -> Node.S.inter acc (get_parents (i + 1) parg))
-            (get_parents 0 parg) pargs
-        in
-        if Node.S.is_empty nodes then raise Not_found else nodes
-
-  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
-      | App (f, _, tl) ->
-          List.foldi (fun acc pos p -> aux acc F_Pos.{ f; pos } p) acc tl
-    in
-    let m =
-      match pattern with
-      | Var _ -> Expr.Term.Var.M.empty
-      | App (f, _, tl) ->
-          List.foldi
-            (fun acc pos p -> aux acc F_Pos.{ f; pos } p)
-            Expr.Term.Var.M.empty tl
-    in
-    Expr.Term.Var.M.map
-      (fun s ->
-        let l = F_Pos.S.elements s in
-        Lists.fold_product
-          (fun acc parents1 parents2 ->
-            if F_Pos.equal parents1 parents2 then acc
-            else PP.create parents1 parents2 :: acc)
-          [] l l)
-      m
-
-  let env_ground_by_apps =
-    F.EnvApps.create
-      (Context.Queue.pp ~sep:Fmt.semi Ground.pp)
-      "Quantifier.Trigger.ground_by_apps" Context.Queue.create
-
-  module EnvTy = Datastructure.Memo (Ground.Ty)
-
-  let env_node_by_ty =
-    EnvTy.create
-      (Context.Queue.pp ~sep:Fmt.semi Node.pp)
-      "Quantifier.Trigger.node_by_ty" Context.Queue.create
-
-  let match_any_term d (p : t) : Ground.Subst.S.t =
-    Debug.dprintf2 debug "[Quant] matching any for %a" pp p;
-    match p with
-    | Var v ->
-        EnvTy.fold
-          (fun ty q acc ->
-            let substs = match_ty init ty v.ty in
-            if Ground.Subst.S.is_empty substs then acc
-            else
-              Ground.Subst.S.fold_left
-                (fun acc subst ->
-                  Context.Queue.fold
-                    (fun acc n ->
-                      Ground.Subst.S.add
-                        { subst with term = Expr.Term.Var.M.add v n subst.term }
-                        acc)
-                    acc q)
-                acc substs)
-          env_node_by_ty d Ground.Subst.S.empty
-    | App (pf, ptyl, pargs) ->
-        Context.Queue.fold
-          (fun acc n ->
-            let { Ground.Term.app = f; tyargs = tyl; args; _ } = Ground.sem n in
-            assert (Term.Const.equal f pf);
-            let substs = List.fold_left2 match_ty init tyl ptyl in
-            let substs =
-              Base.List.fold2_exn args pargs ~init:substs ~f:(match_term d)
-            in
-            Ground.Subst.S.union substs acc)
-          Ground.Subst.S.empty
-          (F.EnvApps.find env_ground_by_apps d pf)
-end
-
-module Trigger = struct
-  module T = struct
-    open! Base
-
-    type t = {
-      pat : Pattern.t;
-      checks : Pattern.t list;
-      (* list *) form : Ground.ThClosedQuantifier.t;
-      eager : bool;
-    }
-    [@@deriving eq, ord, hash]
-
-    let pp fmt t =
-      Fmt.pf fmt "[%a, %a -> %a]" Pattern.pp t.pat
-        Fmt.(list ~sep:comma Pattern.pp)
-        t.checks Ground.ThClosedQuantifier.pp t.form
-  end
-
-  include T
-  include Popop_stdlib.MkDatatype (T)
-
-  let compute_top_triggers (cq : Ground.ThClosedQuantifier.t) =
-    let cq' = Ground.ThClosedQuantifier.sem cq in
-    let tyvs = cq'.ty_vars in
-    let tvs = cq'.term_vars in
-    let t = cq'.body in
-    let rec aux pats (t : Expr.Term.t) =
-      match t.descr with
-      | Expr.Binder (_, _) -> pats (* todo *)
-      | Expr.Match (_, _) -> pats (* todo *)
-      | Expr.App
-          ( {
-              builtin =
-                ( Expr.And | Expr.Equal | Expr.Equiv | Expr.Or | Expr.Xor
-                | Expr.Imply );
-              _;
-            },
-            _,
-            tl ) ->
-          List.fold_left aux pats tl
-      | _ -> t :: pats
-    in
-    let pats = List.sort_uniq ~cmp:Expr.Term.compare (aux [] t) in
-    let tyvs = Expr.Ty.Var.S.of_list tyvs in
-    let tvs = Expr.Term.Var.S.of_list tvs in
-    let pats_full, pats_partial =
-      Base.List.partition_tf
-        ~f:(fun pat ->
-          let sty, st =
-            Expr.Term.free_vars (Expr.Ty.Var.S.empty, Expr.Term.Var.S.empty) pat
-          in
-          Expr.Ty.Var.S.equal sty tyvs && Expr.Term.Var.S.equal st tvs)
-        pats
-    in
-    let pats_full = List.map Pattern.of_term pats_full in
-    let pats_partial = List.map Pattern.of_term pats_partial in
-    let multi_pats =
-      let rec aux acc other = function
-        | [] -> acc
-        | a :: l ->
-            aux
-              ( {
-                  pat = a;
-                  checks = other @ l @ pats_partial;
-                  form = cq;
-                  eager = true;
-                }
-              :: acc )
-              (a :: other) l
-      in
-      aux [] [] pats_full
-    in
-    Debug.dprintf6 debug "@[pats_full:%a,@ pats_partial:%a,@ tri:%a@]@."
-      Fmt.(list ~sep:comma Pattern.pp)
-      pats_full
-      Fmt.(list ~sep:comma Pattern.pp)
-      pats_partial
-      Fmt.(list ~sep:comma pp)
-      multi_pats;
-    multi_pats
-
-  let compute_all_triggers (cq : Ground.ThClosedQuantifier.t) =
-    let cq' = Ground.ThClosedQuantifier.sem cq in
-    let tyvs = cq'.ty_vars in
-    let tvs = cq'.term_vars in
-    let t = cq'.body in
-    let union (sty, st) = function
-      | None -> None
-      | Some (sty', st') ->
-          Some (Expr.Ty.Var.S.union sty sty', Expr.Term.Var.S.union st st')
-    in
-    let add t fv pats = (t, fv) :: pats in
-    let rec aux (pats, acc_fv) (t : Expr.Term.t) =
-      match t.descr with
-      | Var _ ->
-          let fv =
-            Expr.Term.free_vars (Expr.Ty.Var.S.empty, Expr.Term.Var.S.empty) t
-          in
-          (add t fv pats, union fv acc_fv)
-      | Expr.App (_, tyl, tl) -> (
-          let pats, fv =
-            List.fold_left aux
-              (pats, Some (Expr.Ty.Var.S.empty, Expr.Term.Var.S.empty))
-              tl
-          in
-          match fv with
-          | None -> (pats, None)
-          | Some (sty, st) ->
-              let fv = (List.fold_left Expr.Ty.free_vars sty tyl, st) in
-              (add t fv pats, union fv acc_fv) )
-      | Expr.Binder (_, _) -> (pats, None) (* todo *)
-      | Expr.Match (_, _) -> (pats, None)
-      (* todo *)
-    in
-    let pats, _ = aux ([], None) t in
-    let tyvs = Expr.Ty.Var.S.of_list tyvs in
-    let tvs = Expr.Term.Var.S.of_list tvs in
-    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
-            Some
-              { pat = Pattern.of_term c; form = cq; eager = true; checks = [] }
-          else None)
-        pats
-    in
-    pats
-
-  let _ = compute_all_triggers
-
-  let _ = compute_top_triggers
-
-  let env_vars = Datastructure.Queue.create pp "Quantifier.Trigger.vars"
-
-  let env_tri_by_apps =
-    F.EnvApps.create
-      (Context.Queue.pp ~sep:Fmt.semi pp)
-      "Quantifier.Trigger.tri_by_apps" Context.Queue.create
-
-  let add_trigger d t =
-    match t.pat with
-    | Var _ -> Datastructure.Queue.push env_vars d t
-    | App (f, _, _) -> Context.Queue.push (F.EnvApps.find env_tri_by_apps d f) t
-
-  let instantiate_aux d tri subst =
-    let form = Ground.ThClosedQuantifier.sem tri.form in
-    Debug.incr nb_instantiation;
-    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;
-    Egraph.register d n;
-    Egraph.merge d n (Ground.ThClosedQuantifier.node tri.form)
-
-  module Delayed_instantiation = struct
-    type event = unit
-
-    let print_event = Unit.pp
-
-    let enqueue _ _ = assert false
-
-    let key =
-      Events.Dem.create_key
-        ( module struct
-          type d = t * Ground.Subst.t
-
-          type t = unit
-
-          let name = "delayed_instantiation"
-        end )
-
-    let delay = Events.LastEffort 1
-
-    type runable = t * Ground.Subst.t [@@deriving show]
-
-    let print_runable = pp_runable
-
-    let run d (tri, subst) =
-      Debug.dprintf8 debug
-        "[Quant] %a delayed instantiation %a, pat %a, checks:%a" Ground.Subst.pp
-        subst Ground.ThClosedQuantifier.pp tri.form Pattern.pp tri.pat
-        Fmt.(list ~sep:comma Pattern.pp)
-        tri.checks;
-      instantiate_aux d tri subst;
-      None
-  end
-
-  let () = Egraph.Wait.register_dem (module Delayed_instantiation)
-
-  let instantiate d tri subst =
-    let subst =
-      {
-        subst with
-        Ground.Subst.term =
-          Expr.Term.Var.M.map (Egraph.find_def d) subst.Ground.Subst.term;
-      }
-    in
-    Debug.dprintf8 debug "[Quant] %a instantiation found %a, pat %a, checks:%a"
-      Ground.Subst.pp subst Ground.ThClosedQuantifier.pp tri.form Pattern.pp
-      tri.pat
-      Fmt.(list ~sep:comma Pattern.pp)
-      tri.checks;
-    if
-      tri.eager
-      && List.for_all
-           (fun pat ->
-             try not (Node.S.is_empty (Pattern.check_term_exists d subst pat))
-             with Not_found -> false)
-           tri.checks
-    then instantiate_aux d tri subst
-    else (
-      Debug.dprintf0 debug "[Quant] Delayed";
-      Egraph.register_delayed_event d Delayed_instantiation.key (tri, subst) )
-
-  let match_ d tri n =
-    Debug.dprintf4 debug "[Quant] match %a %a" pp tri Node.pp n;
-    let mvar = Pattern.match_term d Ground.Subst.S.empty n tri.pat in
-    Ground.Subst.S.iter (fun subst -> instantiate d tri subst) mvar
-end
-
-module InvertedPath = struct
-  (**
-     1) match the subterm
-     2) possible patterns found
-     3) GoUp and match other arguments
-     4) goto 2)
-  *)
-
-  type t' = {
-    matches : t Pattern.M.t;
-    triggers : Trigger.t list;
-    ups :
-      ( Expr.Ty.t list
-      * Pattern.t list
-      * int (* from which argument we come *)
-      * t )
-      list
-      F_Pos.M.t;
-  }
-
-  and t = t' Context.Ref.t [@@deriving show]
-
-  let _ = pp
-
-  let create creator =
-    Context.Ref.create creator
-      { matches = Pattern.M.empty; triggers = []; ups = F_Pos.M.empty }
-
-  let rec exec d acc substs n ip =
-    Debug.dprintf5 debug_full "[Quant] Exec: %a, %a[%i]" Node.pp n
-      Ground.Subst.S.pp substs
-      (Ground.Subst.S.cardinal substs);
-    (* pp ip; *)
-    if Ground.Subst.S.is_empty substs then acc
-    else
-      let ip = Context.Ref.get ip in
-      let acc =
-        List.fold_left
-          (fun acc tr ->
-            Trigger.M.add_change (fun s -> s) Ground.Subst.S.union tr substs acc)
-          acc ip.triggers
-      in
-      let acc =
-        Pattern.M.fold_left
-          (fun acc p ip ->
-            Debug.dprintf2 debug_full "[Quant] Exec match %a" Pattern.pp p;
-            let substs = Pattern.match_term d substs n p in
-            exec d acc substs n ip)
-          acc ip.matches
-      in
-      let acc =
-        let match_one_app (ptyl, pargs, pos, t) acc g =
-          let { Ground.Term.tyargs = tyl; args; _ } = Ground.sem g in
-          let substs = List.fold_left2 Pattern.match_ty substs tyl ptyl in
-          let substs =
-            if Ground.Subst.S.is_empty substs then substs
-            else
-              let i = ref (-1) in
-              Base.List.fold2_exn args pargs ~init:substs
-                ~f:(fun substs args parg ->
-                  incr i;
-                  if Int.equal !i pos then substs
-                  else Pattern.match_term d substs args parg)
-          in
-          exec d acc substs (Ground.node g) t
-        in
-        let info =
-          Datastructure.HNode.find_opt Info.env d (Egraph.find_def d n)
-        in
-        match info with
-        | None -> acc (* a class without term associated, can't be matched *)
-        | Some info ->
-            let forall_triplets parents acc pt =
-              Ground.S.fold_left (match_one_app pt) acc parents
-            in
-            let forall_fpos acc p ptl =
-              Debug.dprintf2 debug_full "[Quant] Exec ups %a@." F_Pos.pp p;
-              let parents = F_Pos.M.find_def Ground.S.empty p info.parents in
-              List.fold_left (forall_triplets parents) acc ptl
-            in
-            F_Pos.M.fold_left forall_fpos acc ip.ups
-      in
-      acc
-
-  module HPC = Datastructure.Memo (PC)
-  module HPP = Datastructure.Memo (PP)
-  module HPT = Datastructure.Memo (F_Pos)
-
-  (** parent-child, wait for a subterm with the right application *)
-  let pc_ips = HPC.create Fmt.nop "Quantifier.pc" create
-
-  (** parent-parent, a variable appears two times *)
-  let pp_ips = HPP.create Fmt.nop "Quantifier.pp" create
-
-  (** parent-type, wait for a type to match a variable *)
-  let pt_ips = HPT.create Fmt.nop "Quantifier.pt" Context.Queue.create
-
-  let add_pattern pat ip =
-    Context.Ref.set ip
-    @@
-    let ip = Context.Ref.get ip in
-    { ip with triggers = pat :: ip.triggers }
-
-  let add_match pat ipr =
-    let ip = Context.Ref.get ipr in
-    match Pattern.M.find_opt pat ip.matches with
-    | Some ip' -> ip'
-    | None ->
-        let ip' = create (Context.Ref.creator ipr) in
-        Context.Ref.set ipr
-        @@ { ip with matches = Pattern.M.add pat ip' ip.matches };
-        ip'
-
-  let add_up f_pos tytl tl ipr =
-    let ip = Context.Ref.get ipr in
-    let ip' = create (Context.Ref.creator ipr) in
-    Context.Ref.set ipr
-    @@ {
-         ip with
-         ups =
-           F_Pos.M.add_change Lists.singleton Lists.add f_pos
-             (tytl, tl, f_pos.pos, ip') ip.ups;
-       };
-    ip'
-
-  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 =
-            List.filter
-              (fun pp ->
-                (* possible just parent1 only one path is needed by pairs *)
-                F_Pos.equal fp pp.PP.parent1 || F_Pos.equal fp pp.PP.parent2)
-              pairs
-          in
-          let ips = List.map (fun pp -> HPP.find pp_ips d pp) pairs in
-          let ip = create (Egraph.context d) in
-          Context.Queue.push (HPT.find pt_ips d fp) (v.ty, ip);
-          let ips = ip :: ips in
-          let ips = List.map (add_match p) ips in
-          ips
-      | App (f, tytl, tl) ->
-          let ips = insert_children pp_vars f tytl tl in
-          let ip = HPC.find pc_ips d PC.{ parent = fp; child = f } in
-          let ip = add_match p ip in
-          let ips = ip :: ips in
-          ips
-    and insert_children pp_vars f tytl tl =
-      List.foldi
-        (fun acc pos p ->
-          let f_pos = F_Pos.{ f; pos } in
-          let ips = aux f_pos pp_vars p in
-          let ips = List.map (add_up f_pos tytl tl) ips in
-          ips @ acc)
-        [] tl
-    in
-    let pp_vars = Pattern.get_pps trigger.pat in
-    match trigger.pat with
-    | Var _ -> ()
-    | App (f, tytl, tl) ->
-        let ips = insert_children pp_vars f tytl tl in
-        List.iter (add_pattern trigger) ips
-end
+open Common
 
 let add_trigger d t =
   Trigger.add_trigger d t;
diff --git a/src_colibri2/theories/quantifier/trigger.ml b/src_colibri2/theories/quantifier/trigger.ml
new file mode 100644
index 0000000000000000000000000000000000000000..b83779e08e946935f2b7276524f1b29d6d5be736
--- /dev/null
+++ b/src_colibri2/theories/quantifier/trigger.ml
@@ -0,0 +1,214 @@
+open Colibri2_popop_lib
+open Common
+
+module T = struct
+  open! Base
+
+  type t = {
+    pat : Pattern.t;
+    checks : Pattern.t list;
+    (* list *) form : Ground.ThClosedQuantifier.t;
+    eager : bool;
+  }
+  [@@deriving eq, ord, hash]
+
+  let pp fmt t =
+    Fmt.pf fmt "[%a, %a -> %a]" Pattern.pp t.pat
+      Fmt.(list ~sep:comma Pattern.pp)
+      t.checks Ground.ThClosedQuantifier.pp t.form
+end
+
+include T
+include Popop_stdlib.MkDatatype (T)
+
+let compute_top_triggers (cq : Ground.ThClosedQuantifier.t) =
+  let cq' = Ground.ThClosedQuantifier.sem cq in
+  let tyvs = cq'.ty_vars in
+  let tvs = cq'.term_vars in
+  let t = cq'.body in
+  let rec aux pats (t : Expr.Term.t) =
+    match t.descr with
+    | Expr.Binder (_, _) -> pats (* todo *)
+    | Expr.Match (_, _) -> pats (* todo *)
+    | Expr.App
+        ( {
+            builtin =
+              ( Expr.And | Expr.Equal | Expr.Equiv | Expr.Or | Expr.Xor
+              | Expr.Imply );
+            _;
+          },
+          _,
+          tl ) ->
+        List.fold_left aux pats tl
+    | _ -> t :: pats
+  in
+  let pats = List.sort_uniq ~cmp:Expr.Term.compare (aux [] t) in
+  let tyvs = Expr.Ty.Var.S.of_list tyvs in
+  let tvs = Expr.Term.Var.S.of_list tvs in
+  let pats_full, pats_partial =
+    Base.List.partition_tf
+      ~f:(fun pat ->
+        let sty, st =
+          Expr.Term.free_vars (Expr.Ty.Var.S.empty, Expr.Term.Var.S.empty) pat
+        in
+        Expr.Ty.Var.S.equal sty tyvs && Expr.Term.Var.S.equal st tvs)
+      pats
+  in
+  let pats_full = List.map Pattern.of_term pats_full in
+  let pats_partial = List.map Pattern.of_term pats_partial in
+  let multi_pats =
+    let rec aux acc other = function
+      | [] -> acc
+      | a :: l ->
+          aux
+            ( {
+                pat = a;
+                checks = other @ l @ pats_partial;
+                form = cq;
+                eager = true;
+              }
+            :: acc )
+            (a :: other) l
+    in
+    aux [] [] pats_full
+  in
+  Debug.dprintf6 debug "@[pats_full:%a,@ pats_partial:%a,@ tri:%a@]@."
+    Fmt.(list ~sep:comma Pattern.pp)
+    pats_full
+    Fmt.(list ~sep:comma Pattern.pp)
+    pats_partial
+    Fmt.(list ~sep:comma pp)
+    multi_pats;
+  multi_pats
+
+let compute_all_triggers (cq : Ground.ThClosedQuantifier.t) =
+  let cq' = Ground.ThClosedQuantifier.sem cq in
+  let tyvs = cq'.ty_vars in
+  let tvs = cq'.term_vars in
+  let t = cq'.body in
+  let union (sty, st) = function
+    | None -> None
+    | Some (sty', st') ->
+        Some (Expr.Ty.Var.S.union sty sty', Expr.Term.Var.S.union st st')
+  in
+  let add t fv pats = (t, fv) :: pats in
+  let rec aux (pats, acc_fv) (t : Expr.Term.t) =
+    match t.descr with
+    | Var _ ->
+        let fv =
+          Expr.Term.free_vars (Expr.Ty.Var.S.empty, Expr.Term.Var.S.empty) t
+        in
+        (add t fv pats, union fv acc_fv)
+    | Expr.App (_, tyl, tl) -> (
+        let pats, fv =
+          List.fold_left aux
+            (pats, Some (Expr.Ty.Var.S.empty, Expr.Term.Var.S.empty))
+            tl
+        in
+        match fv with
+        | None -> (pats, None)
+        | Some (sty, st) ->
+            let fv = (List.fold_left Expr.Ty.free_vars sty tyl, st) in
+            (add t fv pats, union fv acc_fv) )
+    | Expr.Binder (_, _) -> (pats, None) (* todo *)
+    | Expr.Match (_, _) -> (pats, None)
+    (* todo *)
+  in
+  let pats, _ = aux ([], None) t in
+  let tyvs = Expr.Ty.Var.S.of_list tyvs in
+  let tvs = Expr.Term.Var.S.of_list tvs in
+  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
+          Some { pat = Pattern.of_term c; form = cq; eager = true; checks = [] }
+        else None)
+      pats
+  in
+  pats
+
+let env_vars = Datastructure.Queue.create pp "Quantifier.Trigger.vars"
+
+let env_tri_by_apps =
+  F.EnvApps.create
+    (Context.Queue.pp ~sep:Fmt.semi pp)
+    "Quantifier.Trigger.tri_by_apps" Context.Queue.create
+
+let add_trigger d t =
+  match t.pat with
+  | Var _ -> Datastructure.Queue.push env_vars d t
+  | App (f, _, _) -> Context.Queue.push (F.EnvApps.find env_tri_by_apps d f) t
+
+let instantiate_aux d tri subst =
+  let form = Ground.ThClosedQuantifier.sem tri.form in
+  Debug.incr nb_instantiation;
+  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;
+  Egraph.register d n;
+  Egraph.merge d n (Ground.ThClosedQuantifier.node tri.form)
+
+module Delayed_instantiation = struct
+  type event = unit
+
+  let print_event = Unit.pp
+
+  let enqueue _ _ = assert false
+
+  let key =
+    Events.Dem.create_key
+      ( module struct
+        type d = t * Ground.Subst.t
+
+        type t = unit
+
+        let name = "delayed_instantiation"
+      end )
+
+  let delay = Events.LastEffort 1
+
+  type runable = t * Ground.Subst.t [@@deriving show]
+
+  let print_runable = pp_runable
+
+  let run d (tri, subst) =
+    Debug.dprintf8 debug
+      "[Quant] %a delayed instantiation %a, pat %a, checks:%a" Ground.Subst.pp
+      subst Ground.ThClosedQuantifier.pp tri.form Pattern.pp tri.pat
+      Fmt.(list ~sep:comma Pattern.pp)
+      tri.checks;
+    instantiate_aux d tri subst;
+    None
+end
+
+let () = Egraph.Wait.register_dem (module Delayed_instantiation)
+
+let instantiate d tri subst =
+  let subst =
+    {
+      subst with
+      Ground.Subst.term =
+        Expr.Term.Var.M.map (Egraph.find_def d) subst.Ground.Subst.term;
+    }
+  in
+  Debug.dprintf8 debug "[Quant] %a instantiation found %a, pat %a, checks:%a"
+    Ground.Subst.pp subst Ground.ThClosedQuantifier.pp tri.form Pattern.pp
+    tri.pat
+    Fmt.(list ~sep:comma Pattern.pp)
+    tri.checks;
+  if
+    tri.eager
+    && List.for_all
+         (fun pat ->
+           try not (Node.S.is_empty (Pattern.check_term_exists d subst pat))
+           with Not_found -> false)
+         tri.checks
+  then instantiate_aux d tri subst
+  else (
+    Debug.dprintf0 debug "[Quant] Delayed";
+    Egraph.register_delayed_event d Delayed_instantiation.key (tri, subst) )
+
+let match_ d tri n =
+  Debug.dprintf4 debug "[Quant] match %a %a" pp tri Node.pp n;
+  let mvar = Pattern.match_term d Ground.Subst.S.empty n tri.pat in
+  Ground.Subst.S.iter (fun subst -> instantiate d tri subst) mvar
diff --git a/src_colibri2/theories/quantifier/trigger.mli b/src_colibri2/theories/quantifier/trigger.mli
new file mode 100644
index 0000000000000000000000000000000000000000..f1cffad60119edd3722a7759d38c383a7174dde6
--- /dev/null
+++ b/src_colibri2/theories/quantifier/trigger.mli
@@ -0,0 +1,43 @@
+(** Trigger *)
+
+type t = {
+  pat : Pattern.t;
+      (** The main pattern, the one matched to obtain a complete
+                     substitution *)
+  checks : Pattern.t list;  (** Guards for the existence of terms *)
+  form : Ground.ThClosedQuantifier.t;  (** the body of the formula *)
+  eager : bool;
+      (** If it should be eagerly applied, otherwise wait for LastEffort *)
+}
+
+include Colibri2_popop_lib.Popop_stdlib.Datatype with type t := t
+
+val compute_top_triggers : Ground.ThClosedQuantifier.t -> t list
+(** Compute triggers, that should only add logical connective or equalities are
+   new terms *)
+
+val compute_all_triggers : Ground.ThClosedQuantifier.t -> t list
+(** Compute all the triggers whose patterns contain all the variables of the formula *)
+
+val env_vars : t Datastructure.Queue.t
+(** Triggers that are only variables *)
+
+val env_tri_by_apps : t Context.Queue.t F.EnvApps.t
+(** Triggers sorted by their top symbol *)
+
+val add_trigger : Egraph.t -> t -> unit
+(** Register a new trigger *)
+
+val instantiate : Egraph.t -> t -> Ground.Subst.t -> unit
+(** [instantiate d tri subst] instantiates the trigger [tri] with the
+   substitution [subst]:
+
+   * now if the trigger is eager and the checks of the trigger exists when
+   substituted by [subst]
+
+   * at last effort otherwise
+*)
+
+val match_ : Egraph.t -> t -> Node.t -> unit
+(** [match_ d tri n] match the pattern of [tri] with [n] and instantiate [tri]
+    with the resulting substitutions *)