Generic reflection and reification (by Amine Chaieb).
authorwenzelm
Thu, 03 Aug 2006 15:03:05 +0200
changeset 20319 a8761e8568de
parent 20318 0e0ea63fe768
child 20320 a5368278a72c
Generic reflection and reification (by Amine Chaieb).
src/HOL/ex/Reflection.thy
src/HOL/ex/ReflectionEx.thy
src/HOL/ex/reflection.ML
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HOL/ex/Reflection.thy	Thu Aug 03 15:03:05 2006 +0200
@@ -0,0 +1,27 @@
+(*
+    ID:         $Id$
+    Author:     Amine Chaieb, TU Muenchen
+*)
+
+header {* Generic reflection and reification *}
+
+theory Reflection
+imports Main
+uses "reflection.ML"
+begin
+
+method_setup reify = {*
+  fn src =>
+    Method.syntax (Attrib.thms --
+      Scan.option (Scan.lift (Args.$$$ "(") |-- Args.term --| Scan.lift (Args.$$$ ")"))) src #>
+  (fn (ctxt, (eqs,to)) => Method.SIMPLE_METHOD' HEADGOAL (Reflection.genreify_tac ctxt eqs to))
+*} "partial automatic reification"
+
+method_setup reflection = {*
+  fn src =>
+    Method.syntax (Attrib.thms --
+      Scan.option (Scan.lift (Args.$$$ "(") |-- Args.term --| Scan.lift (Args.$$$ ")"))) src #>
+  (fn (ctxt, (ths,to)) => Method.SIMPLE_METHOD' HEADGOAL (Reflection.reflection_tac ctxt ths to))
+*} "reflection method"
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HOL/ex/ReflectionEx.thy	Thu Aug 03 15:03:05 2006 +0200
@@ -0,0 +1,252 @@
+(*
+    ID:         $Id$
+    Author:     Amine Chaieb, TU Muenchen
+*)
+
+header {* Examples for generic reflection and reification *}
+
+theory ReflectionEx
+imports Reflection
+begin
+
+
+  (* The type fm represents simple propositional formulae *)
+datatype fm = And fm fm | Or fm fm | Imp fm fm | Iff fm fm | NOT fm | At nat
+
+consts Ifm :: "bool list \<Rightarrow> fm \<Rightarrow> bool"
+primrec
+  "Ifm vs (At n) = vs!n"
+  "Ifm bs (And p q) = (Ifm bs p \<and> Ifm bs q)"
+  "Ifm vs (Or p q) = (Ifm vs p \<or> Ifm vs q)"
+  "Ifm vs (Imp p q) = (Ifm vs p \<longrightarrow> Ifm vs q)"
+  "Ifm vs (Iff p q) = (Ifm vs p = Ifm vs q)"
+  "Ifm vs (NOT p) = (\<not> (Ifm vs p))"
+
+consts fmsize :: "fm \<Rightarrow> nat"
+primrec
+  "fmsize (At n) = 1"
+  "fmsize (NOT p) = 1 + fmsize p"
+  "fmsize (And p q) = 1 + fmsize p + fmsize q"
+  "fmsize (Or p q) = 1 + fmsize p + fmsize q"
+  "fmsize (Imp p q) = 2 + fmsize p + fmsize q"
+  "fmsize (Iff p q) = 2 + 2* fmsize p + 2* fmsize q"
+
+
+
+  (* reify maps a bool to an fm, for this it needs the 
+  semantics of fm (Ifm.simps)*)
+lemma "Q \<longrightarrow> (D & F & ((~ D) & (~ F)))"
+apply (reify Ifm.simps)
+oops
+
+  (* You can also just pick up a subterm to reify \<dots> *)
+lemma "Q \<longrightarrow> (D & F & ((~ D) & (~ F)))"
+apply (reify Ifm.simps ("((~ D) & (~ F))"))
+oops
+
+  (* Let's perform NNF, A version that tends to disjunctions *)
+consts nnf :: "fm \<Rightarrow> fm"
+recdef nnf "measure fmsize"
+  "nnf (At n) = At n"
+  "nnf (And p q) = And (nnf p) (nnf q)"
+  "nnf (Or p q) = Or (nnf p) (nnf q)"
+  "nnf (Imp p q) = Or (nnf (NOT p)) (nnf q)"
+  "nnf (Iff p q) = Or (And (nnf p) (nnf q)) (And (nnf (NOT p)) (nnf (NOT q)))"
+  "nnf (NOT (And p q)) = Or (nnf (NOT p)) (nnf (NOT q))"
+  "nnf (NOT (Or p q)) = And (nnf (NOT p)) (nnf (NOT q))"
+  "nnf (NOT (Imp p q)) = And (nnf p) (nnf (NOT q))"
+  "nnf (NOT (Iff p q)) = Or (And (nnf p) (nnf (NOT q))) (And (nnf (NOT p)) (nnf q))"
+  "nnf (NOT (NOT p)) = nnf p"
+  "nnf (NOT p) = NOT p"
+
+  (* nnf preserves the semantics of fm *)
+lemma nnf: "Ifm vs (nnf p) = Ifm vs p"
+  by (induct p rule: nnf.induct) auto
+
+  (* Now let's perform NNF using our function defined above. 
+  reflection takes the correctness theorem (nnf) the semantics of fm
+  and applies the function to the corresponding of the formula *)
+lemma "(\<not> (A = B)) \<and> (B \<longrightarrow> (A \<noteq> (B | C \<and> (B \<longrightarrow> A | D)))) \<longrightarrow> A \<or> B \<and> D"
+apply (reflection nnf Ifm.simps)
+oops
+
+  (* Here also you can just pick up a subterm \<dots> *)
+lemma "(\<not> (A = B)) \<and> (B \<longrightarrow> (A \<noteq> (B | C \<and> (B \<longrightarrow> A | D)))) \<longrightarrow> A \<or> B \<and> D"
+apply (reflection nnf Ifm.simps ("(B | C \<and> (B \<longrightarrow> A | D))"))
+oops
+
+
+  (* Example 2 : simple arithmetics formulae *)
+
+  (* num reflects linear expressions over natural number  *)
+datatype num = C nat | Add num num | Mul nat num | Var nat | CN nat nat num
+
+consts num_size :: "num \<Rightarrow> nat" 
+primrec 
+  "num_size (C c) = 1"
+  "num_size (Var n) = 1"
+  "num_size (Add a b) = 1 + num_size a + num_size b"
+  "num_size (Mul c a) = 1 + num_size a"
+  "num_size (CN n c a) = 4 + num_size a "
+
+  (* The semantics of num *)
+consts Inum:: "nat list \<Rightarrow> num \<Rightarrow> nat"
+primrec 
+  Inum_C  : "Inum vs (C i) = i"
+  Inum_Var: "Inum vs (Var n) = vs!n"
+  Inum_Add: "Inum vs (Add s t) = Inum vs s + Inum vs t"
+  Inum_Mul: "Inum vs (Mul c t) = c * Inum vs t"
+  Inum_CN : "Inum vs (CN n c t) = c*(vs!n) + Inum vs t"
+
+  (* Let's reify some nat expressions \<dots> *)
+lemma "4 * (2*x + (y::nat)) \<noteq> 0"
+  apply (reify Inum.simps ("4 * (2*x + (y::nat))"))
+    (* We're in a bad situation!!*)
+oops
+  (* So let's leave the Inum_C equation at the end and see what happens \<dots>*)
+lemma "4 * (2*x + (y::nat)) \<noteq> 0"
+  apply (reify Inum_Var Inum_Add Inum_Mul Inum_CN Inum_C ("4 * (2*x + (y::nat))"))
+    (* We're still in a bad situation!!*)
+    (* BUT!! It's better\<dots> Note that the reification depends on the order of the equations\<dots> *)
+    (* The problem is that Inum_C has the form : Inum vs (C i) = i *)
+    (* So the right handside matches any term of type nat, which makes things bad. *)
+    (* We want only numerals \<dots> So let's specialize Inum_C with numerals.*)
+oops
+
+lemma Inum_number: "Inum vs (C (number_of t)) = number_of t" by simp
+lemmas Inum_eqs = Inum_Var Inum_Add Inum_Mul Inum_CN Inum_number
+
+  (* Second trial *)
+lemma "1 * (2*x + (y::nat)) \<noteq> 0"
+  apply (reify Inum_eqs ("1 * (2*x + (y::nat))"))
+oops
+  (* That was fine, so let's try an other one\<dots> *)
+
+lemma "1 * (2*x + (y::nat) + 0 + 1) \<noteq> 0"
+  apply (reify Inum_eqs ("1 * (2*x + (y::nat) + 0 + 1)"))
+    (* Oh!! 0 is not a variable \<dots> Oh! 0 is not a number_of .. thing *)
+    (* Tha same for 1, so let's add those equations too *)
+oops
+
+lemma Inum_01: "Inum vs (C 0) = 0" "Inum vs (C 1) = 1" "Inum vs (C(Suc n)) = Suc n"
+  by simp+
+lemmas Inum_eqs'= Inum_eqs Inum_01
+  (* Third Trial *)
+
+lemma "1 * (2*x + (y::nat) + 0 + 1) \<noteq> 0"
+  apply (reify Inum_eqs' ("1 * (2*x + (y::nat) + 0 + 1)"))
+    (* Okay *)
+oops
+
+  (* Some simplifications for num terms, you can skimm untill the main theorem linum *)
+consts lin_add :: "num \<times> num \<Rightarrow> num"
+recdef lin_add "measure (\<lambda>(x,y). ((size x) + (size y)))"
+  "lin_add (CN n1 c1 r1,CN n2 c2 r2) =
+  (if n1=n2 then 
+  (let c = c1 + c2
+  in (if c=0 then lin_add(r1,r2) else CN n1 c (lin_add (r1,r2))))
+  else if n1 \<le> n2 then (CN n1 c1 (lin_add (r1,CN n2 c2 r2))) 
+  else (CN n2 c2 (lin_add (CN n1 c1 r1,r2))))"
+  "lin_add (CN n1 c1 r1,t) = CN n1 c1 (lin_add (r1, t))"  
+  "lin_add (t,CN n2 c2 r2) = CN n2 c2 (lin_add (t,r2))" 
+  "lin_add (C b1, C b2) = C (b1+b2)"
+  "lin_add (a,b) = Add a b"
+lemma lin_add: "Inum bs (lin_add (t,s)) = Inum bs (Add t s)"
+apply (induct t s rule: lin_add.induct, simp_all add: Let_def)
+apply (case_tac "c1+c2 = 0",case_tac "n1 \<le> n2", simp_all)
+by (case_tac "n1 = n2", simp_all add: ring_eq_simps)
+
+consts lin_mul :: "num \<Rightarrow> nat \<Rightarrow> num"
+recdef lin_mul "measure size "
+  "lin_mul (C j) = (\<lambda> i. C (i*j))"
+  "lin_mul (CN n c a) = (\<lambda> i. if i=0 then (C 0) else CN n (i*c) (lin_mul a i))"
+  "lin_mul t = (\<lambda> i. Mul i t)"
+
+lemma lin_mul: "\<And> i. Inum bs (lin_mul t i) = Inum bs (Mul i t)"
+by (induct t rule: lin_mul.induct, auto simp add: ring_eq_simps)
+
+consts linum:: "num \<Rightarrow> num"
+recdef linum "measure num_size"
+  "linum (C b) = C b"
+  "linum (Var n) = CN n 1 (C 0)"
+  "linum (Add t s) = lin_add (linum t, linum s)"
+  "linum (Mul c t) = lin_mul (linum t) c"
+  "linum (CN n c t) = lin_add (linum (Mul c (Var n)),linum t)"
+
+lemma linum : "Inum vs (linum t) = Inum vs t"
+by (induct t rule: linum.induct, simp_all add: lin_mul lin_add)
+
+  (* Now we can use linum to simplify nat terms using reflection *)
+lemma "(Suc (Suc 1)) * ((x::nat) + (Suc 1)*y) = 3*x + 6*y"
+apply (reflection linum Inum_eqs' ("(Suc (Suc 1)) * ((x::nat) + (Suc 1)*y)"))
+oops
+
+  (* Let's list this to formulae \<dots> *)
+
+datatype aform = Lt num num  | Eq num num | Ge num num | NEq num num | 
+  Conj aform aform | Disj aform aform | NEG aform | T | F
+consts linaformsize:: "aform \<Rightarrow> nat"
+recdef linaformsize "measure size"
+  "linaformsize T = 1"
+  "linaformsize F = 1"
+  "linaformsize (Lt a b) = 1"
+  "linaformsize (Ge a b) = 1"
+  "linaformsize (Eq a b) = 1"
+  "linaformsize (NEq a b) = 1"
+  "linaformsize (NEG p) = 2 + linaformsize p"
+  "linaformsize (Conj p q) = 1 + linaformsize p + linaformsize q"
+  "linaformsize (Disj p q) = 1 + linaformsize p + linaformsize q"
+
+
+consts aform :: "nat list => aform => bool"
+primrec
+  "aform vs T = True"
+  "aform vs F = False"
+  "aform vs (Lt a b) = (Inum vs a < Inum vs b)"
+  "aform vs (Eq a b) = (Inum vs a = Inum vs b)"
+  "aform vs (Ge a b) = (Inum vs a \<ge> Inum vs b)"
+  "aform vs (NEq a b) = (Inum vs a \<noteq> Inum vs b)"
+  "aform vs (NEG p) = (\<not> (aform vs p))"
+  "aform vs (Conj p q) = (aform vs p \<and> aform vs q)"
+  "aform vs (Disj p q) = (aform vs p \<or> aform vs q)"
+
+  (* Let's reify and do reflection .. *)
+lemma "(3::nat)*x + t < 0 \<and> (2 * x + y \<noteq> 17)"
+apply (reify Inum_eqs' aform.simps)
+oops
+
+lemma "(3::nat)*x + t < 0 & x*x + t*x + 3 + 1 = z*t*4*z | x + x + 1 < 0"
+apply (reflection linum Inum_eqs' aform.simps ("x + x + 1"))
+oops
+
+  (* We now define a simple transformation on aform: NNF + linum on atoms *)
+consts linaform:: "aform \<Rightarrow> aform"
+recdef linaform "measure linaformsize"
+  "linaform (Lt s t) = Lt (linum s) (linum t)"
+  "linaform (Eq s t) = Eq (linum s) (linum t)"
+  "linaform (Ge s t) = Ge (linum s) (linum t)"
+  "linaform (NEq s t) = NEq (linum s) (linum t)"
+  "linaform (Conj p q) = Conj (linaform p) (linaform q)"
+  "linaform (Disj p q) = Disj (linaform p) (linaform q)"
+  "linaform (NEG T) = F"
+  "linaform (NEG F) = T"
+  "linaform (NEG (Lt a b)) = Ge a b"
+  "linaform (NEG (Ge a b)) = Lt a b"
+  "linaform (NEG (Eq a b)) = NEq a b"
+  "linaform (NEG (NEq a b)) = Eq a b"
+  "linaform (NEG (NEG p)) = linaform p"
+  "linaform (NEG (Conj p q)) = Disj (linaform (NEG p)) (linaform (NEG q))"
+  "linaform (NEG (Disj p q)) = Conj (linaform (NEG p)) (linaform (NEG q))"
+  "linaform p = p"
+
+lemma linaform: "aform vs (linaform p) = aform vs p"
+  by (induct p rule: linaform.induct, auto simp add: linum)
+
+lemma "(((Suc(Suc (Suc 0)))*((x::nat) + (Suc (Suc 0)))) + (Suc (Suc (Suc 0))) * ((Suc(Suc (Suc 0)))*((x::nat) + (Suc (Suc 0))))< 0) \<and> (Suc 0  + Suc 0< 0)"
+  apply (reflection linaform Inum_eqs' aform.simps) (*  ("Suc 0 + Suc 0< 0")) *)
+oops
+
+    (* etc etc \<dots>*)
+
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HOL/ex/reflection.ML	Thu Aug 03 15:03:05 2006 +0200
@@ -0,0 +1,172 @@
+(*
+    ID:         $Id$
+    Author:     Amine Chaieb, TU Muenchen
+
+A trial for automatical reification.
+*)
+
+signature REFLECTION = sig
+  val genreify_tac: Proof.context -> thm list -> term option -> int -> tactic
+  val reflection_tac: Proof.context -> thm list -> term option -> int -> tactic
+end;
+
+structure Reflection : REFLECTION
+= struct
+
+    (* Make a congruence rule out of a defining equation for the interpretation *)
+        (* th is one defining equation of f, i.e.
+           th is "f (Cp ?t1 ... ?tn) = P(f ?t1, .., f ?tn)" *)
+        (* Cp is a constructor pattern and P is a pattern *)
+
+        (* The result is:
+         [|?A1 = f ?t1 ; .. ; ?An= f ?tn |] ==> P (?A1, .., ?An) = f (Cp ?t1 .. ?tn) *)
+        (*  + the a list of names of the A1 .. An, Those are fresh in the ctxt*)
+
+fun mk_congeq ctxt fs th =
+  let
+    val Const(fname,fT)$(Free(_,_))$_ =
+        (fst o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) th
+    val thy = ProofContext.theory_of ctxt;
+    val cert = Thm.cterm_of thy;
+    fun dest_listT (Type ("List.list",[vT])) = vT;
+    val vT = dest_listT (Term.domain_type fT);
+    val (((_,_),[th']), ctxt') = Variable.import true [th] ctxt;
+    val (lhs, rhs) = HOLogic.dest_eq (HOLogic.dest_Trueprop (Thm.prop_of th'));
+
+    fun add_fterms (t as t1 $ t2 $ t3) =
+          if exists (fn f => t1 aconv f) fs then insert (op aconv) t
+          else add_fterms (t1 $ t2) #> add_fterms t3
+      | add_fterms (t1 $ t2) = add_fterms t1 #> add_fterms t2
+      | add_fterms (Abs _) = sys_error "FIXME"
+      | add_fterms _ = I;
+    val fterms = add_fterms rhs [];
+
+    val (xs, ctxt'') = Variable.invent_fixes (replicate (length fterms) "x") ctxt';
+    val tys = map fastype_of fterms
+    val vs = map Free (xs ~~ tys);
+    val env = fterms ~~ vs;
+
+    fun replace_fterms (t as t1 $ t2 $ t3) =
+          (case AList.lookup (op aconv) env t of
+            SOME v => v
+          | NONE => replace_fterms (t1 $ t2) $ replace_fterms t3)
+      | replace_fterms (t1 $ t2) = replace_fterms t1 $ replace_fterms t2
+      | replace_fterms t = t;
+
+    fun mk_def (t, v) = HOLogic.mk_Trueprop (HOLogic.mk_eq (v, t));
+    val cong = (Goal.prove ctxt'' [] (map mk_def env)
+      (HOLogic.mk_Trueprop (HOLogic.mk_eq (lhs, replace_fterms rhs)))
+      (fn x => LocalDefs.unfold_tac (#context x) (#prems x) THEN rtac th' 1)) RS sym;
+
+    val (cong' :: vars') = Variable.export ctxt'' ctxt
+      (cong :: map (Drule.mk_term o cert) vs);
+    val vs' = map (fst o fst o Term.dest_Var o Thm.term_of o Drule.dest_term) vars';
+  in (vs', cong') end;
+
+ (* congs is a list of pairs (P,th) where th is a theorem for *)
+        (* [| f p1 = A1; ...; f pn = An|] ==> f (C p1 .. pn) = P *)
+val FWD = curry (op OF);
+
+ (* da is the decomposition for atoms, ie. it returns ([],g) where g
+ returns the right instance f (AtC n) = t , where AtC is the Atoms
+ constructor and n is the number of the atom corresponding to t *)
+
+(* Generic decomp for reification : matches the actual term with the
+rhs of one cong rule. The result of the matching guides the
+proof synthesis: The matches of the introduced Variables A1 .. An are
+processed recursively
+ The rest is instantiated in the cong rule,i.e. no reification is needed *)
+
+ fun decomp_genreif thy da ((vns,cong)::congs) t =
+    ((let
+        val cert = cterm_of thy
+        val (_, tmenv) =
+        Pattern.match thy
+        ((fst o HOLogic.dest_eq o HOLogic.dest_Trueprop) (concl_of cong), t)
+        (Envir.type_env (Envir.empty 0),Term.Vartab.empty)
+        val (fnvs,invs) = List.partition (fn ((vn,_),_) => vn mem vns) (Vartab.dest tmenv)
+        val (fts,its) = (map (snd o snd) fnvs,
+                         map (fn ((vn,vi),(tT,t)) => (cert(Var ((vn,vi),tT)), cert t)) invs)
+    in (fts, FWD (instantiate ([], its) cong))
+    end)
+      handle MATCH => decomp_genreif thy da congs t)
+   | decomp_genreif thy da [] t = da t;
+
+     (* We add the atoms here during reification *)
+val env = ref ([]: (term list));
+
+fun env_index t =
+    let val i = find_index_eq t (!env)
+    in if i = ~1 then (env:= (!env)@[t] ; (length (!env)) - 1) else i  end;
+
+exception REIF of string;
+
+          (* looks for the atoms equation and instantiates it with the right number *)
+fun mk_decompatom thy eqs =
+    let fun isateq (_$_$(Const("List.nth",_)$_$_)) = true
+          | isateq _ = false
+    in case List.find (isateq o HOLogic.dest_Trueprop o prop_of) eqs of
+           NONE => raise REIF "Can not find the atoms equation"
+         | SOME th =>
+           fn t => ([],
+                    fn ths =>
+                       instantiate' [] [SOME(cterm_of thy (HOLogic.mk_nat(env_index t)))]
+                                    (th RS sym))
+    end;
+
+  (* Generic reification procedure: *)
+  (* creates all needed cong rules and then just uses the theorem synthesis *)
+fun genreif ctxt raw_eqs t =
+    let val ([x], ctxt') = Variable.invent_fixes ["vs"] ctxt
+        val thy = ProofContext.theory_of ctxt'
+        val cert = cterm_of thy
+        val Const(fname,fT)$(Var(_,vT))$_ =
+            (fst o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) (hd raw_eqs)
+        val cv = cert (Free(x, vT))
+        val eqs = map (instantiate' [] [SOME cv]) raw_eqs
+        val fs =
+            foldr (fn (eq,fns) =>
+                      let val f$_$_ =  (fst o HOLogic.dest_eq o
+                                        HOLogic.dest_Trueprop o prop_of) eq
+                      in f ins fns end) [] eqs
+        val congs = map (mk_congeq ctxt' fs) eqs
+        val _ = (env := [])
+        val da = mk_decompatom thy eqs
+        val [th] = Variable.export ctxt' ctxt
+                 [divide_and_conquer (decomp_genreif thy da congs) t]
+        val cv' = cterm_of (ProofContext.theory_of ctxt)
+                           (HOLogic.mk_list I (body_type fT) (!env))
+        val _ = (env := [])
+        val th' = instantiate' [] [SOME cv'] th
+        val t' = (fst o HOLogic.dest_eq o HOLogic.dest_Trueprop o prop_of) th'
+        val th'' = Goal.prove ctxt [] [] (HOLogic.mk_Trueprop (HOLogic.mk_eq (t, t')))
+                   (fn _ => Simp_tac 1)
+    in FWD trans [th'',th']
+    end;
+
+fun genreflect ctxt corr_thm raw_eqs t =
+    let val th = FWD trans [genreif ctxt raw_eqs t, corr_thm RS sym]
+        val ft = (snd o Thm.dest_comb o snd o Thm.dest_comb o snd o Thm.dest_comb o cprop_of) th
+        val rth = normalization_conv ft
+    in simplify (HOL_basic_ss addsimps raw_eqs addsimps [nth_Cons_0, nth_Cons_Suc])
+                (simplify (HOL_basic_ss addsimps [rth]) th)
+    end
+
+fun genreify_tac ctxt eqs to i = (fn st =>
+  let
+    val P = HOLogic.dest_Trueprop (List.nth (prems_of st, i - 1))
+    val t = (case to of NONE => P | SOME x => x)
+    val th = (genreif ctxt eqs t) RS ssubst
+  in rtac th i st
+  end);
+
+    (* Reflection calls reification and uses the correctness *)
+        (* theorem assumed to be the dead of the list *)
+ fun reflection_tac ctxt (corr_thm::raw_eqs) to i =
+    (fn st =>
+        let val P = (HOLogic.dest_Trueprop (List.nth (prems_of st, i - 1)))
+            val t = (case to of NONE => P | SOME x => x)
+            val th = (genreflect ctxt corr_thm raw_eqs t) RS ssubst
+        in rtac th i st end);
+
+end