(*<*)
theory Typing
  imports  RCLogic WellformedL
begin
  (*>*)

chapter \<open>Type System\<close>

text \<open>The MiniSail type system. We define subtyping judgement first and then typing judgement
for the term forms\<close>

section \<open>Subtyping\<close>

text \<open> Subtyping is defined on top of refinement constraint logic (RCL).
A subtyping check is converted into an RCL validity check. \<close>

inductive subtype :: "\<Theta> \<Rightarrow> \<B> \<Rightarrow> \<Gamma> \<Rightarrow> \<tau> \<Rightarrow> \<tau> \<Rightarrow> bool"  ("_ ; _ ; _  \<turnstile> _ \<lesssim> _" [50, 50, 50] 50) where
  subtype_baseI: "\<lbrakk>
   atom x \<sharp> (\<Theta>, \<B>, \<Gamma>, z,c,z',c') ;
   \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f  \<lbrace> z : b | c \<rbrace>;
   \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f  \<lbrace> z' : b | c' \<rbrace>;
   \<Theta>; \<B> ; (x,b, c[z::=[x]\<^sup>v]\<^sub>v) #\<^sub>\<Gamma> \<Gamma> \<Turnstile> c'[z'::=[x]\<^sup>v]\<^sub>v
\<rbrakk> \<Longrightarrow>
   \<Theta>; \<B>; \<Gamma> \<turnstile>  \<lbrace> z : b | c \<rbrace> \<lesssim>  \<lbrace> z' : b | c' \<rbrace>"

equivariance subtype
nominal_inductive subtype
  avoids subtype_baseI: x
proof(goal_cases)
  case (1 \<Theta> \<B> \<Gamma> z b c z' c' x)
  then show ?case using  fresh_star_def 1 by force
next
  case (2 \<Theta> \<B> \<Gamma> z b c z' c' x)
  then show ?case by auto
qed

inductive_cases subtype_elims:
  "\<Theta>; \<B>; \<Gamma> \<turnstile> \<lbrace> z : b | c \<rbrace> \<lesssim>  \<lbrace> z' : b | c' \<rbrace>"
  "\<Theta>; \<B>; \<Gamma> \<turnstile> \<tau>\<^sub>1 \<lesssim>  \<tau>\<^sub>2"

section \<open>Literals\<close>

text \<open>The type synthesised has the constraint that z equates to the literal\<close>

inductive infer_l  :: "l \<Rightarrow> \<tau> \<Rightarrow> bool" (" \<turnstile> _ \<Rightarrow> _" [50, 50] 50) where
  infer_trueI:   " \<turnstile> L_true  \<Rightarrow> \<lbrace> z : B_bool | [[z]\<^sup>v]\<^sup>c\<^sup>e ==  [[L_true]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>"
| infer_falseI: " \<turnstile> L_false \<Rightarrow> \<lbrace> z : B_bool | [[z]\<^sup>v]\<^sup>c\<^sup>e == [[L_false]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>"
| infer_natI:   " \<turnstile> L_num n \<Rightarrow> \<lbrace> z : B_int  | [[z]\<^sup>v]\<^sup>c\<^sup>e == [[L_num n]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>"
| infer_unitI:  " \<turnstile> L_unit  \<Rightarrow> \<lbrace> z : B_unit | [[z]\<^sup>v]\<^sup>c\<^sup>e == [[L_unit]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>"
| infer_bitvecI:  " \<turnstile> L_bitvec bv \<Rightarrow> \<lbrace> z : B_bitvec | [[z]\<^sup>v]\<^sup>c\<^sup>e ==  [[L_bitvec bv]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>"

nominal_inductive infer_l  .
equivariance infer_l

inductive_cases infer_l_elims[elim!]:
  "\<turnstile> L_true \<Rightarrow> \<tau>"
  "\<turnstile> L_false \<Rightarrow> \<tau>"
  "\<turnstile> L_num n \<Rightarrow> \<tau>"
  "\<turnstile> L_unit \<Rightarrow> \<tau>"
  "\<turnstile> L_bitvec x \<Rightarrow> \<tau>"
  "\<turnstile> l \<Rightarrow> \<tau>"

lemma infer_l_form2[simp]:
  shows "\<exists>z. \<turnstile> l \<Rightarrow> (\<lbrace> z : base_for_lit l | [[z]\<^sup>v]\<^sup>c\<^sup>e == [[l]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>)"
proof (nominal_induct l rule: l.strong_induct)
  case (L_num x)
  then show ?case using infer_l.intros base_for_lit.simps has_fresh_z by metis
next
  case L_true
  then show ?case using infer_l.intros base_for_lit.simps has_fresh_z by metis
next
  case L_false
  then show ?case using infer_l.intros base_for_lit.simps has_fresh_z by metis
next
  case L_unit
  then show ?case using infer_l.intros base_for_lit.simps has_fresh_z by metis
next
  case (L_bitvec x)
  then show ?case using infer_l.intros base_for_lit.simps has_fresh_z by metis
qed

section \<open>Values\<close>

inductive infer_v :: "\<Theta> \<Rightarrow> \<B> \<Rightarrow> \<Gamma> \<Rightarrow> v \<Rightarrow> \<tau> \<Rightarrow> bool" (" _ ; _ ; _ \<turnstile> _ \<Rightarrow> _" [50, 50, 50] 50) where

infer_v_varI: "\<lbrakk>
      \<Theta>; \<B> \<turnstile>\<^sub>w\<^sub>f \<Gamma> ;
      Some (b,c) = lookup \<Gamma> x;
      atom z \<sharp> x ; atom z \<sharp>  (\<Theta>, \<B>, \<Gamma>)
\<rbrakk> \<Longrightarrow>
      \<Theta>; \<B>; \<Gamma> \<turnstile> [x]\<^sup>v \<Rightarrow> \<lbrace> z : b | [[z]\<^sup>v]\<^sup>c\<^sup>e == [[x]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>"

| infer_v_litI: "\<lbrakk>
      \<Theta>; \<B> \<turnstile>\<^sub>w\<^sub>f \<Gamma> ;
      \<turnstile> l \<Rightarrow> \<tau>
\<rbrakk> \<Longrightarrow>
      \<Theta>; \<B>; \<Gamma> \<turnstile> [l]\<^sup>v \<Rightarrow> \<tau>"

| infer_v_pairI: "\<lbrakk>
      atom z \<sharp> (v1, v2); atom z \<sharp>  (\<Theta>, \<B>, \<Gamma>) ;
      \<Theta>; \<B>; \<Gamma> \<turnstile> (v1::v) \<Rightarrow> t1 ;
      \<Theta>; \<B> ;  \<Gamma> \<turnstile> (v2::v) \<Rightarrow> t2
\<rbrakk> \<Longrightarrow>
      \<Theta>; \<B>; \<Gamma> \<turnstile> V_pair v1 v2 \<Rightarrow> (\<lbrace> z : B_pair (b_of t1) (b_of t2)  | [[z]\<^sup>v]\<^sup>c\<^sup>e == [[v1,v2]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>) "

| infer_v_consI: "\<lbrakk>
      AF_typedef s dclist \<in> set \<Theta>;
      (dc, tc) \<in> set dclist ;
      \<Theta>;  \<B>; \<Gamma> \<turnstile> v \<Rightarrow> tv ;
      \<Theta>; \<B>; \<Gamma> \<turnstile> tv \<lesssim> tc ;
      atom z \<sharp> v ;  atom z \<sharp> (\<Theta>, \<B>, \<Gamma>)
\<rbrakk> \<Longrightarrow>
      \<Theta>;  \<B>; \<Gamma>  \<turnstile> V_cons s dc v \<Rightarrow> (\<lbrace> z : B_id s |  [[z]\<^sup>v]\<^sup>c\<^sup>e == [ V_cons s dc v ]\<^sup>c\<^sup>e \<rbrace>)"

| infer_v_conspI: "\<lbrakk>
      AF_typedef_poly s bv dclist \<in> set \<Theta>;
      (dc, tc) \<in> set dclist ;
      \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Rightarrow> tv;
      \<Theta>; \<B>; \<Gamma> \<turnstile> tv \<lesssim> tc[bv::=b]\<^sub>\<tau>\<^sub>b ;
      atom z \<sharp> (\<Theta>, \<B>, \<Gamma>, v, b);
      atom bv \<sharp> (\<Theta>, \<B>, \<Gamma>, v, b);
      \<Theta>; \<B> \<turnstile>\<^sub>w\<^sub>f b
\<rbrakk> \<Longrightarrow>
      \<Theta>; \<B>; \<Gamma> \<turnstile> V_consp s dc b v \<Rightarrow> (\<lbrace> z : B_app s b |  [[z]\<^sup>v]\<^sup>c\<^sup>e == (CE_val (V_consp s dc b v)) \<rbrace>)"

equivariance infer_v
nominal_inductive infer_v
  avoids infer_v_conspI: bv and z | infer_v_varI: z | infer_v_pairI: z | infer_v_consI:  z
proof(goal_cases)
  case (1 \<Theta> \<B> \<Gamma> b c x z)
  hence "atom z \<sharp> \<lbrace> z : b  | [ [ z ]\<^sup>v ]\<^sup>c\<^sup>e  ==  [ [ x ]\<^sup>v ]\<^sup>c\<^sup>e  \<rbrace>" using \<tau>.fresh by simp
  then show ?case unfolding fresh_star_def using 1 by simp
next
  case (2 \<Theta> \<B> \<Gamma> b c x z)
  then show ?case by auto
next
  case (3 z v1 v2 \<Theta> \<B> \<Gamma> t1 t2)
  hence "atom z \<sharp> \<lbrace> z : [ b_of t1 , b_of t2 ]\<^sup>b  | [ [ z ]\<^sup>v ]\<^sup>c\<^sup>e  ==  [ [ v1 , v2 ]\<^sup>v ]\<^sup>c\<^sup>e  \<rbrace>" using \<tau>.fresh by simp
  then show ?case unfolding fresh_star_def using 3 by simp
next
  case (4 z v1 v2 \<Theta> \<B> \<Gamma> t1 t2)
  then show ?case by auto
next
  case (5 s dclist \<Theta> dc tc \<B> \<Gamma> v tv z)
  hence "atom z \<sharp> \<lbrace> z : B_id s  | [ [ z ]\<^sup>v ]\<^sup>c\<^sup>e  ==  [ V_cons s dc v ]\<^sup>c\<^sup>e  \<rbrace>" using \<tau>.fresh b.fresh pure_fresh by auto
  moreover have "atom z \<sharp> V_cons s dc v" using v.fresh 5 using v.fresh fresh_prodN pure_fresh  by metis
  then show ?case unfolding fresh_star_def using 5 by simp
next
  case (6 s dclist \<Theta> dc tc \<B> \<Gamma> v tv z)
  then show ?case by auto
next
  case (7 s bv dclist \<Theta> dc tc \<B> \<Gamma> v tv b z)
  hence "atom bv \<sharp> V_consp s dc b v" using v.fresh fresh_prodN pure_fresh by metis
  moreover then have "atom bv \<sharp> \<lbrace> z : B_id s  | [ [ z ]\<^sup>v ]\<^sup>c\<^sup>e  ==  [ V_consp s dc b v ]\<^sup>c\<^sup>e  \<rbrace>"
    using \<tau>.fresh ce.fresh v.fresh by auto
  moreover have "atom z \<sharp> V_consp s dc b v" using v.fresh fresh_prodN pure_fresh 7 by metis
  moreover then have "atom z \<sharp> \<lbrace> z : B_id s  | [ [ z ]\<^sup>v ]\<^sup>c\<^sup>e  ==  [ V_consp s dc b v ]\<^sup>c\<^sup>e  \<rbrace>"
    using \<tau>.fresh ce.fresh v.fresh by auto
  ultimately show ?case using fresh_star_def 7 by force
next
  case (8 s bv dclist \<Theta> dc tc \<B> \<Gamma> v tv b z)
  then show ?case by auto
qed

inductive_cases infer_v_elims[elim!]:
  "\<Theta>; \<B>; \<Gamma> \<turnstile> V_var x \<Rightarrow> \<tau>"
  "\<Theta>; \<B>; \<Gamma> \<turnstile> V_lit l \<Rightarrow> \<tau>"
  "\<Theta>; \<B>; \<Gamma> \<turnstile> V_pair v1 v2 \<Rightarrow> \<tau>"
  "\<Theta>; \<B>; \<Gamma> \<turnstile> V_cons s dc v \<Rightarrow> \<tau>"
  "\<Theta>; \<B>; \<Gamma> \<turnstile> V_pair v1 v2 \<Rightarrow> (\<lbrace> z : b |  c  \<rbrace>) "
  "\<Theta>; \<B>; \<Gamma> \<turnstile> V_pair v1 v2 \<Rightarrow> (\<lbrace> z : [ b1 , b2 ]\<^sup>b |  [[z]\<^sup>v]\<^sup>c\<^sup>e == [[v1,v2]\<^sup>v]\<^sup>c\<^sup>e \<rbrace>) "
  "\<Theta>; \<B>; \<Gamma> \<turnstile> V_consp s dc b v  \<Rightarrow> \<tau> "

inductive check_v :: "\<Theta> \<Rightarrow> \<B> \<Rightarrow> \<Gamma> \<Rightarrow> v \<Rightarrow> \<tau> \<Rightarrow> bool"  ("_ ; _ ; _  \<turnstile> _ \<Leftarrow> _" [50, 50, 50] 50) where
  check_v_subtypeI:  "\<lbrakk>  \<Theta>; \<B>; \<Gamma> \<turnstile> \<tau>1 \<lesssim> \<tau>2; \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Rightarrow> \<tau>1 \<rbrakk> \<Longrightarrow> \<Theta>; \<B> ;  \<Gamma> \<turnstile>  v \<Leftarrow> \<tau>2"
equivariance check_v
nominal_inductive check_v  .

inductive_cases check_v_elims[elim!]:
  "\<Theta>; \<B> ; \<Gamma> \<turnstile> v \<Leftarrow> \<tau>"

section \<open>Expressions\<close>

text \<open> Type synthesis for expressions \<close>

inductive infer_e :: "\<Theta> \<Rightarrow> \<Phi> \<Rightarrow> \<B> \<Rightarrow> \<Gamma> \<Rightarrow> \<Delta> \<Rightarrow> e \<Rightarrow> \<tau> \<Rightarrow> bool"  ("_ ; _ ; _ ; _ ; _  \<turnstile> _ \<Rightarrow> _" [50, 50, 50,50] 50) where

infer_e_valI:  "\<lbrakk>
         (\<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta>) ;
         (\<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>)) ;
         (\<Theta>; \<B>; \<Gamma> \<turnstile> v \<Rightarrow> \<tau>) \<rbrakk> \<Longrightarrow>
         \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_val v) \<Rightarrow> \<tau>"

| infer_e_plusI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v1 \<Rightarrow> \<lbrace> z1 : B_int | c1 \<rbrace> ;
        \<Theta>; \<B>; \<Gamma> \<turnstile>  v2 \<Rightarrow> \<lbrace> z2 : B_int | c2 \<rbrace>;
        atom z3 \<sharp> (AE_op Plus v1 v2); atom z3 \<sharp> \<Gamma> \<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_op Plus v1 v2 \<Rightarrow> \<lbrace> z3 : B_int | [[z3]\<^sup>v]\<^sup>c\<^sup>e == (CE_op Plus [v1]\<^sup>c\<^sup>e [v2]\<^sup>c\<^sup>e) \<rbrace>"

| infer_e_leqI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta>;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v1 \<Rightarrow> \<lbrace> z1 : B_int | c1 \<rbrace> ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v2 \<Rightarrow> \<lbrace> z2 : B_int | c2 \<rbrace>;
        atom z3 \<sharp> (AE_op LEq v1 v2); atom z3 \<sharp> \<Gamma>
\<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_op LEq v1 v2 \<Rightarrow>  \<lbrace> z3 : B_bool | [[z3]\<^sup>v]\<^sup>c\<^sup>e == (CE_op LEq [v1]\<^sup>c\<^sup>e [v2]\<^sup>c\<^sup>e) \<rbrace>"

| infer_e_eqI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta>;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v1 \<Rightarrow> \<lbrace> z1 : b | c1 \<rbrace> ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v2 \<Rightarrow> \<lbrace> z2 : b | c2 \<rbrace>;
        atom z3 \<sharp> (AE_op Eq v1 v2); atom z3 \<sharp> \<Gamma>  ;
        b \<in> { B_bool, B_int, B_unit }
\<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_op Eq v1 v2 \<Rightarrow>  \<lbrace> z3 : B_bool | [[z3]\<^sup>v]\<^sup>c\<^sup>e == (CE_op Eq [v1]\<^sup>c\<^sup>e [v2]\<^sup>c\<^sup>e) \<rbrace>"

| infer_e_appI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        Some (AF_fundef f (AF_fun_typ_none (AF_fun_typ x b c \<tau>' s'))) = lookup_fun \<Phi> f;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Leftarrow> \<lbrace>  x : b | c \<rbrace>;
        atom x \<sharp> (\<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>,v , \<tau>);
        \<tau>'[x::=v]\<^sub>v = \<tau>
\<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_app f v \<Rightarrow> \<tau>"

| infer_e_appPI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B> \<turnstile>\<^sub>w\<^sub>f b' ;
        Some (AF_fundef f (AF_fun_typ_some bv (AF_fun_typ x b c \<tau>' s'))) = lookup_fun \<Phi> f;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Leftarrow> \<lbrace>  x : b[bv::=b']\<^sub>b | c[bv::=b']\<^sub>b \<rbrace>; atom x \<sharp> \<Gamma>;
        (\<tau>'[bv::=b']\<^sub>b[x::=v]\<^sub>v) = \<tau> ;
        atom bv \<sharp> (\<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>, b', v, \<tau>)
\<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_appP f b' v \<Rightarrow> \<tau>"

| infer_e_fstI:  "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Rightarrow> \<lbrace> z' : [b1,b2]\<^sup>b | c \<rbrace>;
        atom z \<sharp> AE_fst v ; atom z \<sharp> \<Gamma> \<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_fst v \<Rightarrow> \<lbrace> z : b1 | [[z]\<^sup>v]\<^sup>c\<^sup>e == ((CE_fst [v]\<^sup>c\<^sup>e)) \<rbrace>"

| infer_e_sndI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Rightarrow> \<lbrace> z' : [ b1, b2]\<^sup>b | c \<rbrace>;
        atom z \<sharp> AE_snd v ; atom z \<sharp> \<Gamma> \<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_snd v \<Rightarrow> \<lbrace> z : b2 | [[z]\<^sup>v]\<^sup>c\<^sup>e == ((CE_snd [v]\<^sup>c\<^sup>e))  \<rbrace>"

| infer_e_lenI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Rightarrow> \<lbrace> z' : B_bitvec | c \<rbrace>;
        atom z \<sharp> AE_len v ; atom z \<sharp> \<Gamma>\<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_len v \<Rightarrow> \<lbrace> z : B_int | [[z]\<^sup>v]\<^sup>c\<^sup>e == ((CE_len [v]\<^sup>c\<^sup>e))  \<rbrace>"

| infer_e_mvarI: "\<lbrakk>
        \<Theta>; \<B> \<turnstile>\<^sub>w\<^sub>f \<Gamma> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta>;
        (u,\<tau>) \<in> setD \<Delta> \<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile>  AE_mvar u \<Rightarrow> \<tau>"

| infer_e_concatI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>) ;
        \<Theta>; \<B>; \<Gamma> \<turnstile> v1 \<Rightarrow> \<lbrace> z1 : B_bitvec | c1 \<rbrace> ;
        \<Theta>; \<B>; \<Gamma> \<turnstile>  v2 \<Rightarrow> \<lbrace> z2 : B_bitvec | c2 \<rbrace>;
        atom z3 \<sharp> (AE_concat v1 v2); atom z3 \<sharp> \<Gamma> \<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_concat  v1 v2 \<Rightarrow> \<lbrace> z3 : B_bitvec | [[z3]\<^sup>v]\<^sup>c\<^sup>e == (CE_concat [v1]\<^sup>c\<^sup>e [v2]\<^sup>c\<^sup>e) \<rbrace>"

| infer_e_splitI: "\<lbrakk>
        \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<Theta> \<turnstile>\<^sub>w\<^sub>f (\<Phi>::\<Phi>);
        \<Theta> ; \<B> ; \<Gamma> \<turnstile> v1 \<Rightarrow> \<lbrace> z1 : B_bitvec | c1 \<rbrace> ;
        \<Theta> ; \<B> ; \<Gamma> \<turnstile> v2 \<Leftarrow> \<lbrace> z2 : B_int | (CE_op LEq (CE_val (V_lit (L_num 0))) (CE_val (V_var z2))) == (CE_val (V_lit L_true)) AND
                                         (CE_op LEq (CE_val (V_var z2)) (CE_len (CE_val (v1)))) == (CE_val (V_lit L_true)) \<rbrace>;
        atom z1 \<sharp> (AE_split v1 v2); atom z1 \<sharp> \<Gamma>;
        atom z2 \<sharp> (AE_split v1 v2); atom z2 \<sharp> \<Gamma>;
        atom z3 \<sharp> (AE_split v1 v2); atom z3 \<sharp> \<Gamma>
\<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_split  v1 v2) \<Rightarrow> \<lbrace> z3 : B_pair B_bitvec B_bitvec  |
                      ((CE_val v1) == (CE_concat (CE_fst (CE_val (V_var z3))) (CE_snd (CE_val (V_var z3)))))
                  AND (((CE_len (CE_fst (CE_val (V_var z3))))) == (CE_val ( v2))) \<rbrace>"

equivariance infer_e
nominal_inductive infer_e
  avoids  infer_e_appI: x |infer_e_appPI: bv |  infer_e_splitI: z3 and z1 and z2
proof(goal_cases)
  case (1 \<Theta> \<B> \<Gamma> \<Delta> \<Phi> f x b c \<tau>' s' v \<tau>)
  moreover hence "atom x \<sharp> [ f  v  ]\<^sup>e" using fresh_prodN pure_fresh e.fresh by force
  ultimately show ?case unfolding fresh_star_def using fresh_prodN e.fresh pure_fresh by simp
next
  case (2 \<Theta> \<B> \<Gamma> \<Delta> \<Phi> f x b c \<tau>' s' v \<tau>)
  then show ?case by auto
next
  case (3 \<Theta> \<B> \<Gamma> \<Delta> \<Phi> b' f bv x b c \<tau>' s' v \<tau>)
  moreover hence "atom bv \<sharp> AE_appP f b' v"  using fresh_prodN pure_fresh e.fresh by force
  ultimately show ?case unfolding fresh_star_def using fresh_prodN  e.fresh pure_fresh fresh_Pair by auto
next
  case (4 \<Theta> \<B> \<Gamma> \<Delta> \<Phi> b' f bv x b c \<tau>' s' v \<tau>)
  then show ?case by auto
next
  case (5 \<Theta> \<B> \<Gamma> \<Delta> \<Phi> v1 z1 c1 v2 z2 z3)
  have "atom z3 \<sharp> \<lbrace> z3 : [ B_bitvec , B_bitvec ]\<^sup>b  | [ v1 ]\<^sup>c\<^sup>e  ==  [ [#1[ [ z3 ]\<^sup>v ]\<^sup>c\<^sup>e]\<^sup>c\<^sup>e @@ [#2[ [ z3 ]\<^sup>v ]\<^sup>c\<^sup>e]\<^sup>c\<^sup>e ]\<^sup>c\<^sup>e   AND  [| [#1[ [ z3 ]\<^sup>v ]\<^sup>c\<^sup>e]\<^sup>c\<^sup>e |]\<^sup>c\<^sup>e  ==  [ v2 ]\<^sup>c\<^sup>e   \<rbrace>"
    using \<tau>.fresh by simp
  then show ?case unfolding fresh_star_def fresh_prod7 using wfG_fresh_x2 5 by auto
next
  case (6 \<Theta> \<B> \<Gamma> \<Delta> \<Phi> v1 z1 c1 v2 z2 z3)
  then show ?case by auto
qed

inductive_cases infer_e_elims[elim!]:
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op Plus v1 v2) \<Rightarrow> \<lbrace> z3 : B_int | [[z3]\<^sup>v]\<^sup>c\<^sup>e == (CE_op Plus [v1]\<^sup>c\<^sup>e [v2]\<^sup>c\<^sup>e) \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op LEq v1 v2) \<Rightarrow> \<lbrace> z3 : B_bool | [[z3]\<^sup>v]\<^sup>c\<^sup>e == (CE_op LEq [v1]\<^sup>c\<^sup>e [v2]\<^sup>c\<^sup>e) \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op Plus v1 v2) \<Rightarrow> \<lbrace> z3 : B_int | c \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op Plus v1 v2) \<Rightarrow> \<lbrace> z3 : b | c \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op LEq v1 v2) \<Rightarrow> \<lbrace> z3 : b | c \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_app f v ) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_val v) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_fst v) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_snd v) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_mvar u) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op Plus v1 v2) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op LEq v1 v2) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op LEq v1 v2) \<Rightarrow> \<lbrace> z3 : B_bool | c \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_app f v )  \<Rightarrow> \<tau>[x::=v]\<^sub>\<tau>\<^sub>v"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op opp v1 v2) \<Rightarrow>  \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_len v) \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_len v) \<Rightarrow> \<lbrace> z : B_int | [[z]\<^sup>v]\<^sup>c\<^sup>e == ((CE_len [v]\<^sup>c\<^sup>e))\<rbrace> "
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_concat v1 v2 \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_concat v1 v2 \<Rightarrow> (\<lbrace> z : b |  c  \<rbrace>) "
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_concat v1 v2 \<Rightarrow> (\<lbrace> z : B_bitvec |  [[z]\<^sup>v]\<^sup>c\<^sup>e == (CE_concat [v1]\<^sup>c\<^sup>e [v1]\<^sup>c\<^sup>e) \<rbrace>) "
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_appP f b v )  \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AE_split v1 v2 \<Rightarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op Eq v1 v2) \<Rightarrow> \<lbrace> z3 : b | c \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op Eq v1 v2) \<Rightarrow> \<lbrace> z3 : B_bool | c \<rbrace>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AE_op Eq v1 v2) \<Rightarrow> \<tau>"
nominal_termination (eqvt)  by lexicographic_order

section \<open>Statements\<close>

inductive check_s ::  "\<Theta> \<Rightarrow> \<Phi> \<Rightarrow> \<B> \<Rightarrow> \<Gamma> \<Rightarrow> \<Delta> \<Rightarrow> s \<Rightarrow> \<tau> \<Rightarrow> bool" (" _ ; _ ; _ ; _ ; _  \<turnstile> _ \<Leftarrow> _" [50, 50, 50,50,50] 50) and
  check_branch_s ::  "\<Theta> \<Rightarrow> \<Phi> \<Rightarrow> \<B> \<Rightarrow> \<Gamma> \<Rightarrow> \<Delta>  \<Rightarrow> tyid \<Rightarrow> string \<Rightarrow> \<tau> \<Rightarrow> v \<Rightarrow> branch_s \<Rightarrow> \<tau> \<Rightarrow> bool" (" _ ;  _ ; _ ; _ ; _ ; _ ; _ ; _ ; _ \<turnstile> _ \<Leftarrow> _" [50, 50, 50,50,50] 50) and
  check_branch_list ::  "\<Theta> \<Rightarrow> \<Phi> \<Rightarrow> \<B> \<Rightarrow> \<Gamma> \<Rightarrow> \<Delta>  \<Rightarrow> tyid \<Rightarrow> (string * \<tau>) list \<Rightarrow> v \<Rightarrow> branch_list \<Rightarrow> \<tau> \<Rightarrow> bool" (" _ ;  _ ; _ ; _ ; _ ; _ ; _ ; _ \<turnstile> _ \<Leftarrow> _" [50, 50, 50,50,50] 50) where
  check_valI:  "\<lbrakk>
       \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
       \<Theta> \<turnstile>\<^sub>w\<^sub>f \<Phi> ;
       \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Rightarrow> \<tau>';
       \<Theta>; \<B>; \<Gamma> \<turnstile> \<tau>' \<lesssim> \<tau> \<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AS_val v) \<Leftarrow> \<tau>"

| check_letI: "\<lbrakk>
       atom x \<sharp> (\<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>, e, \<tau>);
       atom z \<sharp> (x, \<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>, e, \<tau>, s);
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> e \<Rightarrow> \<lbrace> z : b | c \<rbrace> ;
       \<Theta>; \<Phi> ; \<B> ; ((x,b,c[z::=V_var x]\<^sub>v)#\<^sub>\<Gamma>\<Gamma>) ; \<Delta> \<turnstile> s \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AS_let x e s) \<Leftarrow> \<tau>"

| check_assertI: "\<lbrakk>
       atom x \<sharp> (\<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>, c, \<tau>, s);
       \<Theta>; \<Phi> ; \<B> ; ((x,B_bool,c)#\<^sub>\<Gamma>\<Gamma>) ; \<Delta> \<turnstile> s \<Leftarrow> \<tau> ;
       \<Theta>; \<B>; \<Gamma>  \<Turnstile> c;
       \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (AS_assert c s) \<Leftarrow> \<tau>"

|check_branch_s_branchI: "\<lbrakk>
       \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
        \<turnstile>\<^sub>w\<^sub>f \<Theta> ;
       \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<tau> ;
       \<Theta> ; {||} ; GNil \<turnstile>\<^sub>w\<^sub>f const;
       atom x \<sharp> (\<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>, tid, cons , const, v, \<tau>);
       \<Theta>; \<Phi>; \<B>; ((x,b_of const,  ([v]\<^sup>c\<^sup>e == [ V_cons tid cons [x]\<^sup>v]\<^sup>c\<^sup>e ) AND (c_of const x))#\<^sub>\<Gamma>\<Gamma>) ; \<Delta> \<turnstile> s \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> ; tid ; cons ; const ; v \<turnstile> (AS_branch cons x s) \<Leftarrow> \<tau>"

|check_branch_list_consI: "\<lbrakk>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta>; tid; cons; const; v \<turnstile> cs \<Leftarrow> \<tau> ;
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta>; tid; dclist; v \<turnstile> css \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> ; tid ; (cons,const)#dclist ; v \<turnstile> AS_cons cs css \<Leftarrow> \<tau>"

|check_branch_list_finalI: "\<lbrakk>
       \<Theta>; \<Phi>;\<B>; \<Gamma>; \<Delta>; tid ; cons ; const ; v \<turnstile> cs \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> ; tid ; [(cons,const)] ; v \<turnstile> AS_final cs  \<Leftarrow> \<tau>"

| check_ifI: "\<lbrakk>
       atom z \<sharp> (\<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>, v , s1 , s2 , \<tau> );
       (\<Theta>; \<B>; \<Gamma> \<turnstile> v \<Leftarrow> (\<lbrace> z : B_bool | TRUE \<rbrace>)) ;
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> s1 \<Leftarrow> (\<lbrace> z : b_of \<tau> | ([v]\<^sup>c\<^sup>e == [[L_true]\<^sup>v]\<^sup>c\<^sup>e) IMP (c_of \<tau> z) \<rbrace>) ;
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> s2 \<Leftarrow> (\<lbrace> z : b_of \<tau> | ([v]\<^sup>c\<^sup>e == [[L_false]\<^sup>v]\<^sup>c\<^sup>e) IMP (c_of \<tau> z) \<rbrace>)
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> IF v THEN s1 ELSE s2 \<Leftarrow> \<tau>"

| check_let2I: "\<lbrakk>
       atom x \<sharp> (\<Theta>, \<Phi>, \<B>, G, \<Delta>, t, s1, \<tau>) ;
       \<Theta>; \<Phi> ; \<B> ; G; \<Delta> \<turnstile> s1 \<Leftarrow> t;
       \<Theta>; \<Phi> ; \<B> ; ((x,b_of t,c_of t x)#\<^sub>\<Gamma>G) ; \<Delta> \<turnstile> s2 \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi> ; \<B> ; G ; \<Delta> \<turnstile> (LET x : t  = s1 IN s2) \<Leftarrow> \<tau>"

| check_varI: "\<lbrakk>
       atom u \<sharp> (\<Theta>, \<Phi>, \<B>, \<Gamma>, \<Delta>, \<tau>', v, \<tau>) ;
       \<Theta>; \<B>; \<Gamma> \<turnstile>  v \<Leftarrow> \<tau>';
       \<Theta>; \<Phi>; \<B>; \<Gamma> ; ((u,\<tau>') #\<^sub>\<Delta> \<Delta>)  \<turnstile> s \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> (VAR u : \<tau>' = v  IN s) \<Leftarrow> \<tau>"

| check_assignI: "\<lbrakk>
       \<Theta> \<turnstile>\<^sub>w\<^sub>f \<Phi> ;
       \<Theta>; \<B>; \<Gamma> \<turnstile>\<^sub>w\<^sub>f \<Delta> ;
       (u,\<tau>) \<in> setD \<Delta> ;
       \<Theta>; \<B>; \<Gamma> \<turnstile>  v \<Leftarrow> \<tau>;
       \<Theta>; \<B>; \<Gamma> \<turnstile> (\<lbrace> z : B_unit | TRUE \<rbrace>) \<lesssim> \<tau>'
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile>  (u ::= v) \<Leftarrow> \<tau>'"

| check_whileI: "\<lbrakk>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> s1 \<Leftarrow> \<lbrace> z : B_bool | TRUE \<rbrace>;
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> s2 \<Leftarrow> \<lbrace> z : B_unit | TRUE \<rbrace>;
        \<Theta>; \<B>; \<Gamma> \<turnstile> (\<lbrace> z : B_unit | TRUE \<rbrace>) \<lesssim> \<tau>'
\<rbrakk> \<Longrightarrow>
        \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> WHILE s1 DO { s2 } \<Leftarrow> \<tau>'"

| check_seqI: "\<lbrakk>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> s1 \<Leftarrow> \<lbrace> z : B_unit | TRUE \<rbrace>;
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> s2 \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>
       \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> s1 ;; s2 \<Leftarrow> \<tau>"

| check_caseI: "\<lbrakk>
      \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta>; tid ; dclist ; v \<turnstile>  cs \<Leftarrow> \<tau> ;
       (AF_typedef tid dclist ) \<in> set \<Theta> ;
       \<Theta>; \<B>; \<Gamma> \<turnstile> v \<Leftarrow> \<lbrace> z : B_id tid | TRUE \<rbrace>;
       \<turnstile>\<^sub>w\<^sub>f \<Theta>
\<rbrakk> \<Longrightarrow>
      \<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_match v cs \<Leftarrow> \<tau>"

equivariance check_s

text \<open>We only need avoidance for cases where a variable is added to a context\<close>
nominal_inductive check_s
  avoids check_letI: x and z  |  check_branch_s_branchI: x | check_let2I: x  | check_varI: u | check_ifI: z | check_assertI: x
proof(goal_cases)
  case (1 x \<Theta> \<Phi> \<B> \<Gamma> \<Delta> e \<tau> z s b c)
  hence "atom x \<sharp> AS_let x e s" using s_branch_s_branch_list.fresh(2) by auto
  moreover have "atom z \<sharp> AS_let x e s" using s_branch_s_branch_list.fresh(2) 1 fresh_prod8 by auto
  then show ?case using fresh_star_def 1 by force
next
  case (3 x \<Theta> \<Phi> \<B> \<Gamma> \<Delta> c \<tau> s)
  hence "atom x \<sharp> AS_assert c s" using fresh_prodN s_branch_s_branch_list.fresh pure_fresh  by auto
  then show ?case using fresh_star_def 3 by force
next
  case (5 \<Theta> \<B> \<Gamma> \<Delta> \<tau> const x \<Phi> tid cons v s)
  hence "atom x \<sharp> AS_branch cons x s" using fresh_prodN s_branch_s_branch_list.fresh pure_fresh  by auto
  then show ?case using fresh_star_def 5 by force
next
  case (7 z \<Theta> \<Phi> \<B> \<Gamma> \<Delta> v s1 s2 \<tau>)
  hence "atom z \<sharp> AS_if v s1 s2 " using s_branch_s_branch_list.fresh by auto
  then show ?case using 7 fresh_prodN fresh_star_def by fastforce
next
  case (9 x \<Theta> \<Phi> \<B> G \<Delta> t s1 \<tau> s2)
  hence "atom x \<sharp> AS_let2 x t s1 s2" using s_branch_s_branch_list.fresh by auto
  thus ?case  using fresh_star_def 9 by force
next
  case (11 u \<Theta> \<Phi> \<B> \<Gamma> \<Delta> \<tau>' v \<tau> s)
  hence "atom u \<sharp> AS_var u \<tau>' v s" using s_branch_s_branch_list.fresh by auto
  then show ?case  using fresh_star_def 11 by force

qed(auto+)

inductive_cases check_s_elims[elim!]:
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_val v \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_let x e s \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_if v s1 s2 \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_let2 x t s1 s2 \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_while s1 s2 \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_var u t v s \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_seq s1 s2 \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_assign u v \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_match v cs \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta> \<turnstile> AS_assert c s \<Leftarrow> \<tau>"

inductive_cases check_branch_s_elims[elim!]:
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta>; tid ; dclist ; v \<turnstile> (AS_final cs) \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta>; tid ; dclist ; v \<turnstile> (AS_cons cs css) \<Leftarrow> \<tau>"
  "\<Theta>; \<Phi>; \<B>; \<Gamma>; \<Delta>; tid ; cons ; const ; v \<turnstile> (AS_branch dc x s ) \<Leftarrow> \<tau>"

section \<open>Programs\<close>

text \<open>Type check function bodies\<close>

inductive check_funtyp :: "\<Theta> \<Rightarrow> \<Phi> \<Rightarrow>  \<B> \<Rightarrow> fun_typ \<Rightarrow> bool" ( " _ ; _ ; _ \<turnstile> _ " ) where
  check_funtypI: "\<lbrakk>
  atom x \<sharp> (\<Theta>, \<Phi>, B , b );
  \<Theta>; \<Phi> ;  B ; ((x,b,c) #\<^sub>\<Gamma> GNil) ; []\<^sub>\<Delta> \<turnstile> s \<Leftarrow> \<tau>
\<rbrakk>  \<Longrightarrow>
  \<Theta>; \<Phi> ; B \<turnstile> (AF_fun_typ x b c \<tau> s)"

equivariance check_funtyp
nominal_inductive check_funtyp
  avoids check_funtypI: x
proof(goal_cases)
  case (1 x \<Theta> \<Phi> B b c s \<tau>  )
  hence "atom x \<sharp> (AF_fun_typ x b c \<tau> s)"  using fun_def.fresh fun_typ_q.fresh fun_typ.fresh by simp
  then show ?case using fresh_star_def 1 fresh_prodN by fastforce
next
  case (2 \<Theta> \<Phi> x b c s \<tau> f)
  then show ?case by auto
qed

inductive check_funtypq :: "\<Theta> \<Rightarrow> \<Phi> \<Rightarrow> fun_typ_q \<Rightarrow> bool"  ( " _ ; _ \<turnstile> _ " ) where
  check_fundefq_simpleI: "\<lbrakk>
  \<Theta>; \<Phi> ; {||} \<turnstile> (AF_fun_typ x b c t s)
\<rbrakk>  \<Longrightarrow>
  \<Theta>; \<Phi> \<turnstile> ((AF_fun_typ_none (AF_fun_typ x b c t s)))"

|check_funtypq_polyI: "\<lbrakk>
  atom bv \<sharp> (\<Theta>, \<Phi>, (AF_fun_typ x b c t s));
  \<Theta>; \<Phi>; {|bv|} \<turnstile> (AF_fun_typ x b c t s)
\<rbrakk>  \<Longrightarrow>
  \<Theta>; \<Phi> \<turnstile> (AF_fun_typ_some bv (AF_fun_typ x b c t s))"

equivariance check_funtypq
nominal_inductive check_funtypq
  avoids check_funtypq_polyI: bv
proof(goal_cases)
  case (1 bv \<Theta> \<Phi> x b c t s )
  hence "atom bv \<sharp> (AF_fun_typ_some bv (AF_fun_typ x b c t s))"  using fun_def.fresh fun_typ_q.fresh fun_typ.fresh by simp
  thus ?case using fresh_star_def 1 fresh_prodN by fastforce
next
  case (2 bv \<Theta> \<Phi> ft )
  then show ?case by auto
qed

inductive check_fundef :: "\<Theta> \<Rightarrow> \<Phi> \<Rightarrow> fun_def \<Rightarrow> bool" ( " _ ; _ \<turnstile> _ " ) where
  check_fundefI: "\<lbrakk>
  \<Theta>; \<Phi> \<turnstile> ft
\<rbrakk>  \<Longrightarrow>
  \<Theta>; \<Phi> \<turnstile> (AF_fundef f ft)"

equivariance check_fundef
nominal_inductive check_fundef .

text \<open>Temporarily remove this simproc as it produces untidy eliminations\<close>
declare[[ simproc del: alpha_lst]]

inductive_cases check_funtyp_elims[elim!]:
  "check_funtyp \<Theta> \<Phi> B ft"

inductive_cases check_funtypq_elims[elim!]:
  "check_funtypq \<Theta> \<Phi> (AF_fun_typ_none (AF_fun_typ x b c \<tau> s))"
  "check_funtypq \<Theta> \<Phi> (AF_fun_typ_some bv (AF_fun_typ x b c \<tau> s))"

inductive_cases check_fundef_elims[elim!]:
  "check_fundef \<Theta> \<Phi> (AF_fundef f ftq)"

declare[[ simproc add: alpha_lst]]

nominal_function \<Delta>_of :: "var_def list \<Rightarrow> \<Delta>" where
  "\<Delta>_of [] = DNil"
| "\<Delta>_of ((AV_def u t v)#vs) = (u,t) #\<^sub>\<Delta>  (\<Delta>_of vs)"
  apply auto
  using  eqvt_def \<Delta>_of_graph_aux_def neq_Nil_conv old.prod.exhaust apply force
  using  eqvt_def \<Delta>_of_graph_aux_def neq_Nil_conv old.prod.exhaust
  by (metis var_def.strong_exhaust)
nominal_termination (eqvt) by lexicographic_order

inductive check_prog :: "p \<Rightarrow> \<tau> \<Rightarrow> bool" ( "\<turnstile> _ \<Leftarrow> _")  where
  "\<lbrakk>
   \<Theta>; \<Phi>; {||}; GNil ; \<Delta>_of \<G> \<turnstile> s \<Leftarrow> \<tau>
\<rbrakk> \<Longrightarrow>  \<turnstile> (AP_prog \<Theta> \<Phi> \<G> s) \<Leftarrow> \<tau>"

inductive_cases check_prog_elims[elim!]:
  "\<turnstile> (AP_prog \<Theta> \<Phi> \<G> s) \<Leftarrow> \<tau>"

end


theory Proofs_6
  imports Requirements ExtraInv VCTheoryLemmas
begin

definition inv6 where "inv6 s \<equiv> R6 s \<and> extraInv s"

theorem proof_6_1: "VC1 inv6 s0"
  apply(simp only: VC1_def inv6_def R6_def extraInv_def)
  by auto

theorem proof_6_4: "VC4 R6 env s0 user_value pressure_value"
  apply(simp only: VC4_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

theorem proof_6_5: "VC5 inv6 env s0 user_value pressure_value"
  apply(simp only: VC5_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

theorem proof_6_6: "VC6 R6 env s0 user_value pressure_value"
  apply(simp only: VC6_def inv6_def R6_def extraInv_def)
  by auto

theorem proof_6_7: "VC7 inv6 env s0 user_value pressure_value"
  apply(simp only: VC7_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

theorem proof_6_8: "VC8 R6 env s0 user_value pressure_value"
  apply(simp only: VC8_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

theorem proof_6_9: "VC9 R6 env s0 user_value pressure_value"
  apply(simp only: VC9_def inv6_def R6_def extraInv_def)
  by auto

theorem proof_6_10: "VC10 R6 env s0 user_value pressure_value"
  apply(simp only: VC10_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

theorem proof_6_11: "VC11 inv6 env s0 user_value pressure_value"
  apply(simp only: VC11_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

theorem proof_6_12: "VC12 R6 env s0 user_value pressure_value"
  apply(simp only: VC12_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

theorem proof_6_13: "VC13 R6 env s0 user_value pressure_value"
  apply(simp only: VC13_def inv6_def R6_def extraInv_def)
  by auto

theorem proof_6_14: "VC14 R6 env s0 user_value pressure_value"
  apply(simp only: VC14_def inv6_def R6_def extraInv_def)
  using substate_refl  by auto

end


           (*-------------------------------------------*
            |    Example 1 [Roscoe_Dathi_1987 P.10]     |
            |             WITH computation              |
            |  Self-timed version of a systolic array   |
            |                   June 2005               |
            |               December 2005  (modified)   |
            |                                           |
            |   on DFP on CSP-Prover ver.3.0            |
            |              September 2006  (modified)   |
            |                                           |
            |   on DFP on CSP-Prover ver.4.0            |
            |                  April 2007  (modified)   |
            |                   June 2008  (modified)   |
            |                                           |
            |   on DFP on CSP-Prover ver.5.0            |
            |                    May 2016  (modified)   |
            |                                           |
            |        Yoshinao Isobe (AIST JAPAN)        |
            *-------------------------------------------*)

theory SA_definition
imports DFP_Main Rings
begin

(*********************************************************
                         event
 *********************************************************)

datatype 'r Event = vert "nat * nat" 'r | hori "nat * nat" 'r

(*********************************************************
                  element process
 *********************************************************)

type_synonym index_type = "nat * nat"

datatype 'r ProcName = pe "index_type" 'r | pe' "index_type" 'r 'r 'r

fun
  SAfun  :: "('r::ring) ProcName => ('r ProcName, 'r Event) proc"

where
  "SAfun (pe (i,j) r) = (vert (i,j) ? x -> hori (i,j) ? y -> $(pe' (i,j) r x y)) [+]
                        (hori (i,j) ? y -> vert (i,j) ? x -> $(pe' (i,j) r x y))"

 | "SAfun (pe' (i,j) r x y) = ((vert (i+1,j) ! x -> SKIP) |||
                                 (hori (i,j+1) ! y -> SKIP)) ;;
                                 $(pe (i,j) (r + x * y))"
(*
defs (overloaded)
 set_SAfun_Pnfun_def [simp]: "PNfun == SAfun"
*)

overloading Set_SAfun ==
  "PNfun :: (('r::ring) ProcName, 'r Event) pnfun"
begin
  definition "PNfun :: (('r::ring) ProcName, 'r Event) pnfun == SAfun"
end

declare Set_SAfun_def [simp]

(*
defs
 set_FPmode_def [simp]: "FPmode == CPOmode"
*)

overloading FPmode ==
  "FPmode :: fpmode"
begin
  definition "FPmode == CPOmode"
end
declare FPmode_def [simp]

(*** fixed point ***)

lemma pe_FIX:
  "$(pe ij r) =F (FIX SAfun) (pe ij r)"
apply (rule cspF_rw_left)
apply (rule cspF_FIX)
apply (simp)+
done

(*** network ***)

fun
  Alpha_pe :: "index_type => ('r::ring) Event set"

where
  "Alpha_pe (i,j) = {a. EX x. a = vert (i  ,j) x | a = hori (i,j  ) x |
                              a = vert (i+1,j) x | a = hori (i,j+1) x }"

declare Alpha_pe.simps [simp del]

definition
  Array_Index :: "nat => index_type set"
  where
  Array_Index_def : "Array_Index N == {(i,j)|i j. i < N & j < N}"

definition
  Systolic_Array :: "nat => (index_type,('r::ring) ProcName,'r Event) Network"
  where
  Systolic_Array_def:
  "Systolic_Array N ==
   { ($(pe (i,j) 0), Alpha_pe (i,j)) | (i,j) : (Array_Index N)}net"

(*---------------------------------*
 | Failures sets of Systolic_Array |
 *---------------------------------*)

(*** out term ***)

definition
  Faiures_out_hori :: "('r::ring) => index_type
                         => 'r Event failure set => 'r Event failure set"
  where
  Faiures_out_hori_def :
  "Faiures_out_hori y ==
   (%(i,j). (%F.
      {(<>, {Ev a |a. EX z. (a = vert (i,j) z | a = hori (i,j) z | a = vert (i+1,j) z |
                              (a = hori (i,j+1) z & z ~= y))})} Un
      {(<Ev (hori (i,j+1) y)> ^^^ s, Y) |s Y. (s, Y) : F}))"

definition
  Faiures_out_vert :: "('r::ring) => index_type
                         => 'r Event failure set => 'r Event failure set"
  where
  Faiures_out_vert_def :
  "Faiures_out_vert x ==
   (%(i,j). (%F.
      {(<>, {Ev a |a. EX z. (a = hori (i,j) z | a = vert (i,j) z | a = hori (i,j+1) z |
                              (a = vert (i+1,j) z & z ~= x))})} Un
      {(<Ev (vert (i+1,j) x)> ^^^ s, Y) |s Y. (s, Y) : F}))"

definition
  Faiures_out:: "('r::ring) => 'r => index_type
                         => 'r Event failure set => 'r Event failure set"
  where
  Faiures_out_def :
  "Faiures_out x y ==
   (%(i,j). (%F.
      {(<>, {Ev a |a. EX z. (a = hori (i,j) z | a = vert (i,j) z |
                              (a = hori (i,j+1) z & z ~= y) |
                              (a = vert (i+1,j) z & z ~= x))})} Un
      {(<Ev (vert (i+1,j) x)> ^^^ s, Y) |s Y. (s, Y) : Faiures_out_hori y (i,j) F} Un
      {(<Ev (hori (i,j+1) y)> ^^^ s, Y) |s Y. (s, Y) : Faiures_out_vert x (i,j) F}))"

(*** in term ***)

definition
  Faiures_in_hori :: "('r::ring) => index_type
                       => 'r Event failure set => 'r Event failure set"
  where
  Faiures_in_hori_def :
  "Faiures_in_hori x ==
   (%(i,j) F.
      {(<>, {Ev a |a. EX x. (a = vert (i,j) x | a = hori (i,j+1) x |
                              a = vert (i+1,j) x)})} Un
      {(<Ev (hori (i,j) y)> ^^^ s, Y) |y s Y. (s, Y) : Faiures_out x y (i,j) F})"

definition
  Faiures_in_vert :: "('r::ring) => index_type
                       => 'r Event failure set => 'r Event failure set"
  where
  Faiures_in_vert_def :
  "Faiures_in_vert y ==
   (%(i,j) F.
      {(<>, {Ev a |a. EX x. (a = hori (i,j) x | a = vert (i+1,j) x |
                              a = hori (i,j+1) x)})} Un
      {(<Ev (vert (i,j) x)> ^^^ s, Y) |x s Y. (s, Y) : Faiures_out x y (i,j) F})"

definition
  Faiures_in      :: "index_type => ('r::ring) Event failure set => 'r Event failure set"
  where
  Faiures_in_def :
  "Faiures_in ==
   (%(i,j) F.
      {(<>, {Ev a |a. EX x. (a = vert (i+1,j) x | a = hori (i,j+1) x)})} Un
      {(<Ev (vert (i,j) x)> ^^^ s, Y) |x s Y. (s, Y) : Faiures_in_hori x (i,j) F} Un
      {(<Ev (hori (i,j) y)> ^^^ s, Y) |y s Y. (s, Y) : Faiures_in_vert y (i,j) F})"

(*** induction ***)

primrec
  peF_rec :: "nat => index_type => ('r::ring) Event failure set"
where
  "peF_rec 0       = (%ij. {})"
 |"peF_rec (Suc n) = (%ij. Faiures_in ij (peF_rec n ij))"

definition
  peF     :: "index_type => ('r::ring) Event failure set"
  where
   peF_def : "peF ij == Union {peF_rec n ij |n. True}"

(*** NetworkF ***)

definition
  Systolic_ArrayF :: "nat => (index_type, ('r::ring) Event) NetworkF"
  where
  Systolic_ArrayF_def:
  "Systolic_ArrayF ==
   (%N. { (peF ij, Alpha_pe ij) | ij : (Array_Index N)}Fnet)"

(******************************************************************
                        small lemmas
 ******************************************************************)

(* index *)

lemma Example1_index_mem:
  "ij : Array_Index N = (EX i j. ij = (i,j) & i < N & j < N)"
apply (simp add: Array_Index_def)
by (auto)

(****************** to add it again ******************)

end


(*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: BSD-2-Clause
 *)

theory Trace_Schematic_Insts_Test
imports
  Eisbach_Tools.Trace_Schematic_Insts
begin

text \<open>
  Trace the schematic variables and types that a method instantiates.
  This only works for the variables already in the proof goal; new
  variables introduced by the traced method are not tracked.
\<close>

experiment begin
section \<open>Examples\<close>

text \<open>Schematic variables\<close>
lemma "\<lbrakk> \<forall>x. P x \<rbrakk> \<Longrightarrow> P x"
  apply (drule spec) \<comment> \<open>introduces schematic var "?x"\<close>
  apply (trace_schematic_insts \<open>assumption\<close>)
  done

definition foo :: "'a \<Rightarrow> bool"
  where "foo x = True"

lemma fooI1:
  "foo 0 \<Longrightarrow> foo x"
  by (simp add: foo_def)

lemma fooI2:
  "foo x \<Longrightarrow> foo 0"
  by (simp add: foo_def)

lemma fooI2':
  "foo x \<Longrightarrow> foo (0 :: nat)"
  by (erule fooI2)

text \<open>Schematic type variables\<close>
lemma "foo x \<Longrightarrow> foo y"
  apply (rule fooI1) \<comment> \<open>introduces schematic type "0 :: ?'a"\<close>
  apply (trace_schematic_insts \<open>erule fooI2'\<close>)
  done

text \<open>When backtracking, every recursive invocation is traced\<close>
lemma "\<lbrakk> \<forall>x. Q x \<longrightarrow> R x; \<forall>x. P x \<longrightarrow> Q x; P x; P y \<longrightarrow> R x \<rbrakk> \<Longrightarrow> R x"
  apply (drule spec)
  apply (drule spec)
  text \<open>For more clarity, methods can be named\<close>
  apply (trace_schematic_insts impE1 \<open>erule impE\<close>,
         trace_schematic_insts impE2 \<open>erule impE\<close>,
         (trace_schematic_insts "try assumption" \<open>assumption\<close>)+; fail)
  done

text \<open>Interactive example\<close>
ML \<open>
  fun trace_resolve_tac ctxt =
      Trace_Schematic_Insts.trace_schematic_insts_tac ctxt
        (Trace_Schematic_Insts.default_rule_report ctxt "demo title")
        (fn t => resolve_tac ctxt [t])
\<close>
lemma
  assumes Pf: "\<And>f (x :: nat). Q f \<Longrightarrow> P x \<Longrightarrow> P (f x)"
  assumes Q: "Q ((*) 2)"
  assumes P: "P a"
  shows "\<exists>x. P (x + a + a)"
  apply (tactic \<open>trace_resolve_tac @{context} @{thm exI} 1\<close>)
  apply (trace_schematic_insts \<open>subst add_0\<close>)

  \<comment>\<open>
    This picks *some* instantiation of `f` in @{thm Pf}. The first one is
    @{term "\<lambda>a. a"}, which isn't what we want.
  \<close>
  apply (tactic \<open>trace_resolve_tac @{context} @{thm Pf} 1\<close>)
   \<comment>\<open>
     This picks the *next* instantiation of `f`, in this case @{term "\<lambda>a. a + a"}
     Notice that the reporting callback gets called with the new instantiations!
   \<close>
   back

   apply (tactic \<open>
     Trace_Schematic_Insts.trace_schematic_insts_tac
       @{context}
       (Trace_Schematic_Insts.default_rule_report @{context} "demo title")
       (fn t => EqSubst.eqsubst_tac @{context} [0] [t])
       @{thm mult_2[symmetric]} 1
   \<close>)
   apply (tactic \<open>trace_resolve_tac @{context} @{thm Q} 1\<close>)
  apply (tactic \<open>trace_resolve_tac @{context} @{thm P} 1\<close>)
  done

section \<open>Tests\<close>

ML \<open>
fun trace_schematic_assert ctxt test_name tac expected_vars expected_tvars =
  let
    fun skip_dummy_state tac = fn st =>
        case Thm.prop_of st of
            Const (@{const_name Pure.prop}, _) $
              (Const (@{const_name Pure.term}, _) $ Const (@{const_name Pure.dummy_pattern}, _)) =>
              Seq.succeed st
          | _ => tac st

    fun check insts =
      if expected_vars = #terms insts andalso expected_tvars = #typs insts then () else
        error ("Trace_Schematic_Insts failed test: " ^ test_name)

  in NO_CONTEXT_TACTIC ctxt
        (Trace_Schematic_Insts.trace_schematic_insts (SIMPLE_METHOD tac) check [])
     |> skip_dummy_state
  end
\<close>

text \<open>Schematic variables\<close>
lemma "\<lbrakk> \<forall>x. P x \<rbrakk> \<Longrightarrow> P x"
  apply (drule spec)
  apply (tactic \<open>let
      val alpha = TFree ("'a", @{sort type})
      val expected_vars = [(Var (("x", 0), alpha), Free ("x", alpha))]
      val expected_tvars = []
      in trace_schematic_assert @{context}
            "basic Var test" (assume_tac @{context} 1)
            expected_vars expected_tvars
      end\<close>)
  done

text \<open>Schematic type variables\<close>
lemma "foo x \<Longrightarrow> foo y"
  apply (rule fooI1)
  apply (tactic \<open>let
      val expected_vars = []
      val expected_tvars = [(TVar (("'a", 0), @{sort zero}), @{typ nat})]
      in trace_schematic_assert
            @{context}
            "basic TVar test"
            (eresolve_tac @{context} @{thms fooI2'} 1)
            expected_vars expected_tvars
      end\<close>)
  done

ML \<open>
fun trace_schematic_resolve_tac_assert ctxt test_name thm expected_rule_insts expected_proof_insts =
  let
    fun check rule_insts proof_insts =
        if expected_rule_insts = rule_insts andalso expected_proof_insts = proof_insts
        then ()
        else
          let
            val _ = tracing (@{make_string} (rule_insts, proof_insts))
          in error ("Trace_Schematic_Insts failed test: " ^ test_name) end
    fun tactic thm = resolve_tac ctxt [thm]
  in HEADGOAL (Trace_Schematic_Insts.trace_schematic_insts_tac ctxt check tactic thm)
  end
\<close>

text \<open>Simultaneous rule and goal instantiations\<close>
lemma "\<exists>a. foo (a :: nat)"
  apply (rule exI)
  apply (tactic \<open>
    let
      val a' = TVar (("'a", 0), @{sort type})
      val b' = TVar (("'b", 0), @{sort zero})
      val a'' = TVar (("'a", 2), @{sort type})
      val expected_rule_vars = [
        (Var (("x", 0), a'), Var(("x", 2), a''))
      ]
      val expected_rule_tvars = [
        (a', a''),
        (b', @{typ nat})
      ]
      val expected_goal_vars = [
        (Var (("a", 0), @{typ nat}), @{term "0 :: nat"})
      ]
    in
      trace_schematic_resolve_tac_assert
        @{context}
        "basic rule tracing"
        @{thm fooI2}
        {bounds = [], terms = expected_rule_vars, typs = expected_rule_tvars}
        {bounds = [], terms = expected_goal_vars, typs = []}
    end
  \<close>)
  by (simp add: foo_def)

text \<open>Rule instantiations with bound variables\<close>
lemma "\<And>X. X \<and> Y \<Longrightarrow> Y \<and> X"
  apply (tactic \<open>
    let
      val expected_rule_bounds = [("X", @{typ bool})]
      val expected_rule_vars = [
        (Var (("P", 0), @{typ bool}), @{term "\<lambda>X :: bool. Y :: bool"}),
        (Var (("Q", 0), @{typ bool}), @{term "\<lambda>X :: bool. X :: bool"})
      ]
    in
      trace_schematic_resolve_tac_assert
        @{context}
        "rule tracing with bound variables"
        @{thm conjI}
        {bounds = expected_rule_bounds, terms = expected_rule_vars, typs = []}
        {bounds = [], terms = [], typs = []}
    end
  \<close>)
  by simp+

text \<open>Rule instantiations with function terms\<close>
lemma "\<exists>f. \<forall>x. f x = x"
  apply (intro exI allI)
  apply (rule fun_cong)
  apply (tactic \<open>
    let
      val a' = TVar (("'a", 0), @{sort type})
      \<comment>\<open>
        New lambda abstraction gets an anonymous variable name. Usually rendered as
        @{term "\<lambda>x a. a"}.
      \<close>
      val lambda = Abs ("x", @{typ 'a}, Abs ("", @{typ 'a}, Bound 0))

      val expected_rule_bounds = [("x", @{typ 'a})]
      val expected_rule_vars = [
        (Var (("t", 0), a'), lambda)
      ]
      val expected_rule_typs = [
        (a', @{typ "'a \<Rightarrow> 'a"})
      ]
      val expected_goal_vars = [
        (Var (("f", 2), @{typ "'a \<Rightarrow> 'a \<Rightarrow> 'a"}), lambda)
      ]
    in
      trace_schematic_resolve_tac_assert
        @{context}
        "rule tracing with function term instantiations"
        @{thm refl}
        {bounds = expected_rule_bounds, terms = expected_rule_vars, typs = expected_rule_typs}
        {bounds = [], terms = expected_goal_vars, typs = []}
    end
  \<close>)
  done

end

end


section\<open>Auxiliary renamings for Separation\<close>
theory Separation_Rename
  imports Interface Renaming
begin

lemmas apply_fun = apply_iff[THEN iffD1]

lemma nth_concat : "[p,t] \<in> list(A) \<Longrightarrow> env\<in> list(A) \<Longrightarrow> nth(1 #+ length(env),[p]@ env @ [t]) = t"
  by(auto simp add:nth_append)

lemma nth_concat2 : "env\<in> list(A) \<Longrightarrow> nth(length(env),env @ [p,t]) = p"
  by(auto simp add:nth_append)

lemma nth_concat3 : "env\<in> list(A) \<Longrightarrow> u = nth(succ(length(env)), env @ [pi, u])"
  by(auto simp add:nth_append)

definition
  sep_var :: "i \<Rightarrow> i" where
  "sep_var(n) \<equiv> {\<langle>0,1\<rangle>,\<langle>1,3\<rangle>,\<langle>2,4\<rangle>,\<langle>3,5\<rangle>,\<langle>4,0\<rangle>,\<langle>5#+n,6\<rangle>,\<langle>6#+n,2\<rangle>}"

definition
  sep_env :: "i \<Rightarrow> i" where
  "sep_env(n) \<equiv> \<lambda> i \<in> (5#+n)-5 . i#+2"

definition weak :: "[i, i] \<Rightarrow> i" where
  "weak(n,m) \<equiv> {i#+m . i \<in> n}"

lemma weakD :
  assumes "n \<in> nat" "k\<in>nat" "x \<in> weak(n,k)"
  shows "\<exists> i \<in> n . x = i#+k"
  using assms unfolding weak_def by blast

lemma weak_equal :
  assumes "n\<in>nat" "m\<in>nat"
  shows "weak(n,m) = (m#+n) - m"
proof -
  have "weak(n,m)\<subseteq>(m#+n)-m"
  proof(intro subsetI)
    fix x
    assume "x\<in>weak(n,m)"
    with assms
    obtain i where
      "i\<in>n" "x=i#+m"
      using weakD by blast
    then
    have "m\<le>i#+m" "i<n"
      using add_le_self2[of m i] \<open>m\<in>nat\<close> \<open>n\<in>nat\<close> ltI[OF \<open>i\<in>n\<close>] by simp_all
    then
    have "\<not>i#+m<m"
      using not_lt_iff_le in_n_in_nat[OF \<open>n\<in>nat\<close> \<open>i\<in>n\<close>] \<open>m\<in>nat\<close> by simp
    with \<open>x=i#+m\<close>
    have "x\<notin>m"
      using ltI \<open>m\<in>nat\<close> by auto
    moreover
    from assms \<open>x=i#+m\<close> \<open>i<n\<close>
    have "x<m#+n"
      using add_lt_mono1[OF \<open>i<n\<close> \<open>n\<in>nat\<close>] by simp
    ultimately
    show "x\<in>(m#+n)-m"
      using ltD DiffI by simp
  qed
  moreover
  have "(m#+n)-m\<subseteq>weak(n,m)"
  proof (intro subsetI)
    fix x
    assume "x\<in>(m#+n)-m"
    then
    have "x\<in>m#+n" "x\<notin>m"
      using DiffD1[of x "n#+m" m] DiffD2[of x "n#+m" m] by simp_all
    then
    have "x<m#+n" "x\<in>nat"
      using ltI in_n_in_nat[OF add_type[of m n]] by simp_all
    then
    obtain i where
      "m#+n = succ(x#+i)"
      using less_iff_succ_add[OF \<open>x\<in>nat\<close>,of "m#+n"] add_type by auto
    then
    have "x#+i<m#+n" using succ_le_iff by simp
    with \<open>x\<notin>m\<close>
    have "\<not>x<m" using ltD by blast
    with \<open>m\<in>nat\<close> \<open>x\<in>nat\<close>
    have "m\<le>x" using not_lt_iff_le by simp
    with \<open>x<m#+n\<close>  \<open>n\<in>nat\<close>
    have "x#-m<m#+n#-m"
      using diff_mono[OF \<open>x\<in>nat\<close> _ \<open>m\<in>nat\<close>] by simp
    have "m#+n#-m =  n" using diff_cancel2 \<open>m\<in>nat\<close> \<open>n\<in>nat\<close> by simp
    with \<open>x#-m<m#+n#-m\<close> \<open>x\<in>nat\<close>
    have "x#-m \<in> n" "x=x#-m#+m"
      using ltD add_diff_inverse2[OF \<open>m\<le>x\<close>] by simp_all
    then
    show "x\<in>weak(n,m)"
      unfolding weak_def by auto
  qed
  ultimately
  show ?thesis by auto
qed

lemma weak_zero:
  shows "weak(0,n) = 0"
  unfolding weak_def by simp

lemma weakening_diff :
  assumes "n \<in> nat"
  shows "weak(n,7) - weak(n,5) \<subseteq> {5#+n, 6#+n}"
  unfolding weak_def using assms
proof(auto)
  {
    fix i
    assume "i\<in>n" "succ(succ(natify(i)))\<noteq>n" "\<forall>w\<in>n. succ(succ(natify(i))) \<noteq> natify(w)"
    then
    have "i<n"
      using ltI \<open>n\<in>nat\<close> by simp
    from \<open>n\<in>nat\<close> \<open>i\<in>n\<close> \<open>succ(succ(natify(i)))\<noteq>n\<close>
    have "i\<in>nat" "succ(succ(i))\<noteq>n" using in_n_in_nat by simp_all
    from \<open>i<n\<close>
    have "succ(i)\<le>n" using succ_leI by simp
    with \<open>n\<in>nat\<close>
    consider (a) "succ(i) = n" | (b) "succ(i) < n"
      using leD by auto
    then have "succ(i) = n"
    proof cases
      case a
      then show ?thesis .
    next
      case b
      then
      have "succ(succ(i))\<le>n" using succ_leI by simp
      with \<open>n\<in>nat\<close>
      consider (a) "succ(succ(i)) = n" | (b) "succ(succ(i)) < n"
        using leD by auto
      then have "succ(i) = n"
      proof cases
        case a
        with \<open>succ(succ(i))\<noteq>n\<close> show ?thesis by blast
      next
        case b
        then
        have "succ(succ(i))\<in>n" using ltD by simp
        with \<open>i\<in>nat\<close>
        have "succ(succ(natify(i))) \<noteq> natify(succ(succ(i)))"
          using  \<open>\<forall>w\<in>n. succ(succ(natify(i))) \<noteq> natify(w)\<close> by auto
        then
        have "False" using \<open>i\<in>nat\<close> by auto
        then show ?thesis by blast
      qed
      then show ?thesis .
    qed
    with \<open>i\<in>nat\<close> have "succ(natify(i)) = n" by simp
  }
  then
  show "n \<in> nat \<Longrightarrow>
    succ(succ(natify(y))) \<noteq> n \<Longrightarrow>
    \<forall>x\<in>n. succ(succ(natify(y))) \<noteq> natify(x) \<Longrightarrow>
    y \<in> n \<Longrightarrow> succ(natify(y)) = n" for y
    by blast
qed

lemma in_add_del :
  assumes "x\<in>j#+n" "n\<in>nat" "j\<in>nat"
  shows "x < j \<or> x \<in> weak(n,j)"
proof (cases "x<j")
  case True
  then show ?thesis ..
next
  case False
  have "x\<in>nat" "j#+n\<in>nat"
    using in_n_in_nat[OF _ \<open>x\<in>j#+n\<close>] assms by simp_all
  then
  have "j \<le> x" "x < j#+n"
    using not_lt_iff_le False \<open>j\<in>nat\<close> \<open>n\<in>nat\<close> ltI[OF \<open>x\<in>j#+n\<close>] by auto
  then
  have "x#-j < (j #+ n) #- j" "x = j #+ (x #-j)"
    using diff_mono \<open>x\<in>nat\<close> \<open>j#+n\<in>nat\<close> \<open>j\<in>nat\<close> \<open>n\<in>nat\<close>
      add_diff_inverse[OF \<open>j\<le>x\<close>] by simp_all
  then
  have "x#-j < n" "x = (x #-j ) #+ j"
    using diff_add_inverse \<open>n\<in>nat\<close> add_commute by simp_all
  then
  have "x#-j \<in>n" using ltD by simp
  then
  have "x \<in> weak(n,j)"
    unfolding weak_def
    using \<open>x= (x#-j) #+j\<close> RepFunI[OF \<open>x#-j\<in>n\<close>] add_commute by force
  then show ?thesis  ..
qed

lemma sep_env_action:
  assumes
    "[t,p,u,P,leq,o,pi] \<in> list(M)"
    "env \<in> list(M)"
  shows "\<forall> i . i \<in> weak(length(env),5) \<longrightarrow>
      nth(sep_env(length(env))`i,[t,p,u,P,leq,o,pi]@env) = nth(i,[p,P,leq,o,t] @ env @ [pi,u])"
proof -
  from assms
  have A: "5#+length(env)\<in>nat" "[p, P, leq, o, t] \<in>list(M)"
    by simp_all
  let ?f="sep_env(length(env))"
  have EQ: "weak(length(env),5) = 5#+length(env) - 5"
    using weak_equal length_type[OF \<open>env\<in>list(M)\<close>] by simp
  let ?tgt="[t,p,u,P,leq,o,pi]@env"
  let ?src="[p,P,leq,o,t] @ env @ [pi,u]"
  have "nth(?f`i,[t,p,u,P,leq,o,pi]@env) = nth(i,[p,P,leq,o,t] @ env @ [pi,u])"
    if "i \<in> (5#+length(env)-5)" for i
  proof -
    from that
    have 2: "i \<in> 5#+length(env)"  "i \<notin> 5" "i \<in> nat" "i#-5\<in>nat" "i#+2\<in>nat"
      using in_n_in_nat[OF \<open>5#+length(env)\<in>nat\<close>] by simp_all
    then
    have 3: "\<not> i < 5" using ltD by force
    then
    have "5 \<le> i" "2 \<le> 5"
      using  not_lt_iff_le \<open>i\<in>nat\<close> by simp_all
    then have "2 \<le> i" using le_trans[OF \<open>2\<le>5\<close>] by simp
    from A \<open>i \<in> 5#+length(env)\<close>
    have "i < 5#+length(env)" using ltI by simp
    with \<open>i\<in>nat\<close> \<open>2\<le>i\<close> A
    have C:"i#+2 < 7#+length(env)"  by simp
    with that
    have B: "?f`i = i#+2" unfolding sep_env_def by simp
    from 3 assms(1) \<open>i\<in>nat\<close>
    have "\<not> i#+2 < 7" using not_lt_iff_le add_le_mono by simp
    from \<open>i < 5#+length(env)\<close> 3 \<open>i\<in>nat\<close>
    have "i#-5 < 5#+length(env) #- 5"
      using diff_mono[of i "5#+length(env)" 5,OF _ _ _ \<open>i < 5#+length(env)\<close>]
        not_lt_iff_le[THEN iffD1] by force
    with assms(2)
    have "i#-5 < length(env)" using diff_add_inverse length_type by simp
    have "nth(i,?src) =nth(i#-5,env@[pi,u])"
      using nth_append[OF A(2) \<open>i\<in>nat\<close>] 3 by simp
    also
    have "... = nth(i#-5, env)"
      using nth_append[OF \<open>env \<in>list(M)\<close> \<open>i#-5\<in>nat\<close>] \<open>i#-5 < length(env)\<close> by simp
    also
    have "... = nth(i#+2, ?tgt)"
      using nth_append[OF assms(1) \<open>i#+2\<in>nat\<close>] \<open>\<not> i#+2 <7\<close> by simp
    ultimately
    have "nth(i,?src) = nth(?f`i,?tgt)"
      using B by simp
    then show ?thesis using that by simp
  qed
  then show ?thesis using EQ by force
qed

lemma sep_env_type :
  assumes "n \<in> nat"
  shows "sep_env(n) : (5#+n)-5 \<rightarrow> (7#+n)-7"
proof -
  let ?h="sep_env(n)"
  from \<open>n\<in>nat\<close>
  have "(5#+n)#+2 = 7#+n" "7#+n\<in>nat" "5#+n\<in>nat" by simp_all
  have
    D: "sep_env(n)`x \<in> (7#+n)-7" if "x \<in> (5#+n)-5" for x
  proof -
    from \<open>x\<in>5#+n-5\<close>
    have "?h`x = x#+2" "x<5#+n" "x\<in>nat"
      unfolding sep_env_def using ltI in_n_in_nat[OF \<open>5#+n\<in>nat\<close>] by simp_all
    then
    have "x#+2 < 7#+n" by simp
    then
    have "x#+2 \<in> 7#+n" using ltD by simp
    from \<open>x\<in>5#+n-5\<close>
    have "x\<notin>5" by simp
    then have "\<not>x<5" using ltD by blast
    then have "5\<le>x" using not_lt_iff_le \<open>x\<in>nat\<close> by simp
    then have "7\<le>x#+2" using add_le_mono \<open>x\<in>nat\<close> by simp
    then have "\<not>x#+2<7" using not_lt_iff_le \<open>x\<in>nat\<close> by simp
    then have "x#+2 \<notin> 7" using ltI \<open>x\<in>nat\<close> by force
    with \<open>x#+2 \<in> 7#+n\<close> show ?thesis using  \<open>?h`x = x#+2\<close> DiffI by simp
  qed
  then show ?thesis unfolding sep_env_def using lam_type by simp
qed

lemma sep_var_fin_type :
  assumes "n \<in> nat"
  shows "sep_var(n) : 7#+n  -||> 7#+n"
  unfolding sep_var_def
  using consI ltD emptyI by force

lemma sep_var_domain :
  assumes "n \<in> nat"
  shows "domain(sep_var(n)) =  7#+n - weak(n,5)"
proof -
  let ?A="weak(n,5)"
  have A:"domain(sep_var(n)) \<subseteq> (7#+n)"
    unfolding sep_var_def
    by(auto simp add: le_natE)
  have C: "x=5#+n \<or> x=6#+n \<or> x \<le> 4" if "x\<in>domain(sep_var(n))" for x
    using that unfolding sep_var_def by auto
  have D : "x<n#+7" if "x\<in>7#+n" for x
    using that \<open>n\<in>nat\<close> ltI by simp
  have "\<not> 5#+n < 5#+n" using \<open>n\<in>nat\<close>  lt_irrefl[of _ False] by force
  have "\<not> 6#+n < 5#+n" using \<open>n\<in>nat\<close> by force
  have R: "x < 5#+n" if "x\<in>?A" for x
  proof -
    from that
    obtain i where
      "i<n" "x=5#+i"
      unfolding weak_def
      using ltI \<open>n\<in>nat\<close> RepFun_iff by force
    with \<open>n\<in>nat\<close>
    have "5#+i < 5#+n" using add_lt_mono2 by simp
    with \<open>x=5#+i\<close>
    show "x < 5#+n" by simp
  qed
  then
  have 1:"x\<notin>?A" if "\<not>x <5#+n" for x using that by blast
  have "5#+n \<notin> ?A" "6#+n\<notin>?A"
  proof -
    show "5#+n \<notin> ?A" using 1 \<open>\<not>5#+n<5#+n\<close> by blast
    with 1 show "6#+n \<notin> ?A" using  \<open>\<not>6#+n<5#+n\<close> by blast
  qed
  then
  have E:"x\<notin>?A" if "x\<in>domain(sep_var(n))" for x
    unfolding weak_def
    using C that by force
  then
  have F: "domain(sep_var(n)) \<subseteq> 7#+n - ?A" using A by auto
  from assms
  have "x<7 \<or> x\<in>weak(n,7)" if "x\<in>7#+n" for x
    using in_add_del[OF \<open>x\<in>7#+n\<close>] by simp
  moreover
  {
    fix x
    assume asm:"x\<in>7#+n"  "x\<notin>?A"  "x\<in>weak(n,7)"
    then
    have "x\<in>domain(sep_var(n))"
    proof -
      from \<open>n\<in>nat\<close>
      have "weak(n,7)-weak(n,5)\<subseteq>{n#+5,n#+6}"
        using weakening_diff by simp
      with  \<open>x\<notin>?A\<close> asm
      have "x\<in>{n#+5,n#+6}" using  subsetD DiffI by blast
      then
      show ?thesis unfolding sep_var_def by simp
    qed
  }
  moreover
  {
    fix x
    assume asm:"x\<in>7#+n"  "x\<notin>?A" "x<7"
    then have "x\<in>domain(sep_var(n))"
    proof (cases "2 \<le> n")
      case True
      moreover
      have "0<n" using  leD[OF \<open>n\<in>nat\<close> \<open>2\<le>n\<close>] lt_imp_0_lt by auto
      ultimately
      have "x<5"
        using \<open>x<7\<close> \<open>x\<notin>?A\<close> \<open>n\<in>nat\<close> in_n_in_nat
        unfolding weak_def
        by (clarsimp simp add:not_lt_iff_le, auto simp add:lt_def)
      then
      show ?thesis unfolding sep_var_def
        by (clarsimp simp add:not_lt_iff_le, auto simp add:lt_def)
    next
      case False
      then
      show ?thesis
      proof (cases "n=0")
        case True
        then show ?thesis
          unfolding sep_var_def using ltD asm \<open>n\<in>nat\<close> by auto
      next
        case False
        then
        have "n < 2" using  \<open>n\<in>nat\<close> not_lt_iff_le \<open>\<not> 2 \<le> n\<close>  by force
        then
        have "\<not> n <1" using \<open>n\<noteq>0\<close> by simp
        then
        have "n=1" using not_lt_iff_le \<open>n<2\<close> le_iff by auto
        then show ?thesis
          using \<open>x\<notin>?A\<close>
          unfolding weak_def sep_var_def
          using ltD asm \<open>n\<in>nat\<close> by force
      qed
    qed
  }
  ultimately
  have "w\<in>domain(sep_var(n))" if "w\<in> 7#+n - ?A" for w
    using that by blast
  then
  have "7#+n - ?A \<subseteq> domain(sep_var(n))" by blast
  with F
  show ?thesis by auto
qed

lemma sep_var_type :
  assumes "n \<in> nat"
  shows "sep_var(n) : (7#+n)-weak(n,5) \<rightarrow> 7#+n"
  using FiniteFun_is_fun[OF sep_var_fin_type[OF \<open>n\<in>nat\<close>]]
    sep_var_domain[OF \<open>n\<in>nat\<close>] by simp

lemma sep_var_action :
  assumes
    "[t,p,u,P,leq,o,pi] \<in> list(M)"
    "env \<in> list(M)"
  shows "\<forall> i . i \<in> (7#+length(env)) - weak(length(env),5) \<longrightarrow>
    nth(sep_var(length(env))`i,[t,p,u,P,leq,o,pi]@env) = nth(i,[p,P,leq,o,t] @ env @ [pi,u])"
  using assms
proof (subst sep_var_domain[OF length_type[OF \<open>env\<in>list(M)\<close>],symmetric],auto)
  fix i y
  assume "\<langle>i, y\<rangle> \<in> sep_var(length(env))"
  with assms
  show "nth(sep_var(length(env)) ` i,
               Cons(t, Cons(p, Cons(u, Cons(P, Cons(leq, Cons(o, Cons(pi, env)))))))) =
           nth(i, Cons(p, Cons(P, Cons(leq, Cons(o, Cons(t, env @ [pi, u]))))))"
    using apply_fun[OF sep_var_type] assms
      unfolding sep_var_def
      using nth_concat2[OF \<open>env\<in>list(M)\<close>]  nth_concat3[OF \<open>env\<in>list(M)\<close>,symmetric]
      by force
  qed

definition
  rensep :: "i \<Rightarrow> i" where
  "rensep(n) \<equiv> union_fun(sep_var(n),sep_env(n),7#+n-weak(n,5),weak(n,5))"

lemma rensep_aux :
  assumes "n\<in>nat"
  shows "(7#+n-weak(n,5)) \<union> weak(n,5) = 7#+n" "7#+n \<union> ( 7 #+ n - 7) = 7#+n"
proof -
  from \<open>n\<in>nat\<close>
  have "weak(n,5) = n#+5-5"
    using weak_equal by simp
  with  \<open>n\<in>nat\<close>
  show "(7#+n-weak(n,5)) \<union> weak(n,5) = 7#+n" "7#+n \<union> ( 7 #+ n - 7) = 7#+n"
    using Diff_partition le_imp_subset by auto
qed

lemma rensep_type :
  assumes "n\<in>nat"
  shows "rensep(n) \<in> 7#+n \<rightarrow> 7#+n"
proof -
  from \<open>n\<in>nat\<close>
  have "rensep(n) \<in> (7#+n-weak(n,5)) \<union> weak(n,5) \<rightarrow> 7#+n \<union> (7#+n - 7)"
    unfolding rensep_def
    using union_fun_type  sep_var_type \<open>n\<in>nat\<close> sep_env_type weak_equal
    by force
  then
  show ?thesis using rensep_aux \<open>n\<in>nat\<close> by auto
qed

lemma rensep_action :
  assumes "[t,p,u,P,leq,o,pi] @ env \<in> list(M)"
  shows "\<forall> i . i < 7#+length(env) \<longrightarrow> nth(rensep(length(env))`i,[t,p,u,P,leq,o,pi]@env) = nth(i,[p,P,leq,o,t] @ env @ [pi,u])"
proof -
  let ?tgt="[t,p,u,P,leq,o,pi]@env"
  let ?src="[p,P,leq,o,t] @ env @ [pi,u]"
  let ?m="7 #+ length(env) - weak(length(env),5)"
  let ?p="weak(length(env),5)"
  let ?f="sep_var(length(env))"
  let ?g="sep_env(length(env))"
  let ?n="length(env)"
  from assms
  have 1 : "[t,p,u,P,leq,o,pi] \<in> list(M)" " env \<in> list(M)"
    "?src \<in> list(M)" "?tgt \<in> list(M)"
    "7#+?n = (7#+?n-weak(?n,5)) \<union> weak(?n,5)"
    " length(?src) = (7#+?n-weak(?n,5)) \<union> weak(?n,5)"
    using Diff_partition le_imp_subset rensep_aux by auto
  then
  have "nth(i, ?src) = nth(union_fun(?f, ?g, ?m, ?p) ` i, ?tgt)" if "i < 7#+length(env)" for i
  proof -
    from \<open>i<7#+?n\<close>
    have "i \<in> (7#+?n-weak(?n,5)) \<union> weak(?n,5)"
      using ltD by simp
    then show ?thesis
      unfolding rensep_def using
        union_fun_action[OF \<open>?src\<in>list(M)\<close> \<open>?tgt\<in>list(M)\<close> \<open>length(?src) = (7#+?n-weak(?n,5)) \<union> weak(?n,5)\<close>
          sep_var_action[OF \<open>[t,p,u,P,leq,o,pi] \<in> list(M)\<close> \<open>env\<in>list(M)\<close>]
          sep_env_action[OF \<open>[t,p,u,P,leq,o,pi] \<in> list(M)\<close> \<open>env\<in>list(M)\<close>]
          ] that
      by simp
  qed
  then show ?thesis unfolding rensep_def by simp
qed

definition sep_ren :: "[i,i] \<Rightarrow> i" where
  "sep_ren(n,\<phi>) \<equiv> ren(\<phi>)`(7#+n)`(7#+n)`rensep(n)"

lemma arity_rensep: assumes "\<phi>\<in>formula" "env \<in> list(M)"
  "arity(\<phi>) \<le> 7#+length(env)"
shows "arity(sep_ren(length(env),\<phi>)) \<le> 7#+length(env)"
  unfolding sep_ren_def
  using arity_ren rensep_type assms
  by simp

lemma type_rensep [TC]:
  assumes "\<phi>\<in>formula" "env\<in>list(M)"
  shows "sep_ren(length(env),\<phi>) \<in> formula"
  unfolding sep_ren_def
  using ren_tc rensep_type assms
  by simp

lemma sepren_action:
  assumes "arity(\<phi>) \<le> 7 #+ length(env)"
    "[t,p,u,P,leq,o,pi] \<in> list(M)"
    "env\<in>list(M)"
    "\<phi>\<in>formula"
  shows "sats(M, sep_ren(length(env),\<phi>),[t,p,u,P,leq,o,pi] @ env) \<longleftrightarrow> sats(M, \<phi>,[p,P,leq,o,t] @ env @ [pi,u])"
proof -
  from assms
  have 1: " [t, p, u, P, leq, o, pi] @ env \<in> list(M)"
    "[P,leq,o,p,t] \<in> list(M)"
    "[pi,u] \<in> list(M)"
    by simp_all
  then
  have 2: "[p,P,leq,o,t] @ env @ [pi,u] \<in> list(M)" using app_type by simp
  show ?thesis
    unfolding sep_ren_def
    using sats_iff_sats_ren[OF \<open>\<phi>\<in>formula\<close>
        add_type[of 7 "length(env)"]
        add_type[of 7 "length(env)"]
        2 1(1)
        rensep_type[OF length_type[OF \<open>env\<in>list(M)\<close>]]
        \<open>arity(\<phi>) \<le> 7 #+ length(env)\<close>]
      rensep_action[OF 1(1),rule_format,symmetric]
    by simp
qed

end