Theory MSOinHOL_deep_subst_lemma

theory MSOinHOL_deep_subst_lemma
  imports MSOinHOL_deep
begin

subsection ‹First-order machinery (Part A)›

text ‹Free and bound first-order variable occurrences.›

primrec is_free (infix "free'_in" 900)
  where
    "x free_in (rd(u,v)) = (x = u  x = v)"
  | "x free_in (Xd(z)) = (x = z)"
  | "x free_in (¬dφ) = (x free_in φ)"
  | "x free_in (φ d ψ) = (x free_in φ  x free_in ψ)"
  | "x free_in (dy. φ) = (x free_in φ  x  y)"
  | "x free_in (d2Y. φ) = (x free_in φ)"

abbreviation is_not_free (infix "not'_free'_in" 900)
  where "x not_free_in φ  ¬ (x free_in φ)"

fun is_bound (infix "bound'_in" 900)
  where
    "x bound_in (rd(u,v)) = False"
  | "x bound_in (Xd(z)) = False"
  | "x bound_in (¬dφ) = (x bound_in φ)"
  | "x bound_in (φ d ψ) = (x bound_in φ  x bound_in ψ)"
  | "x bound_in (dy. φ) = (x = y  x bound_in φ)"
  | "x bound_in (d2Y. φ) = (x bound_in φ)"

abbreviation is_not_bound (infix "not'_bound'_in" 900)
  where "x not_bound_in φ  ¬ (x bound_in φ)"

abbreviation occurs (infix "occurs'_in" 900)
  where "x occurs_in φ  x free_in φ  x bound_in φ"

abbreviation not_in (infix "not'_in" 900)
  where "x not_in φ  x not_free_in φ  x not_bound_in φ"

text ‹A fresh first-order variable: strictly larger than every first-order
  variable occurring in φ›.›

primrec fresh ("fresh'(_')")
  where
    "fresh (rd(u,v)) = max (u+1) (v+1)"
  | "fresh (Xd(z)) = z+1"
  | "fresh (¬dφ) = fresh φ"
  | "fresh (φ d ψ) = max (fresh φ) (fresh ψ)"
  | "fresh (dx. φ) = max (x+1) (fresh φ)"
  | "fresh (d2Y. φ) = fresh φ"

lemma L5: "x bound_in φ  x < (fresh φ)"
  by (induct φ) auto

lemma L6: "x free_in φ  x < (fresh φ)"
  by (induct φ) auto

lemma L7: "(fresh φ) not_in φ"
  using L5 L6 by blast

lemma L8: "max (fresh φ) (fresh ψ) not_free_in φ"
  by (metis L6 L7 max.absorb3 max_def)

lemma L9: "max (fresh φ) (fresh ψ) not_bound_in φ"
  by (metis L5 L7 max.absorb3 max_def)

lemma L10: "max (fresh φ) (fresh ψ) not_free_in ψ"
  by (metis L8 max.commute)

lemma L11: "max (fresh φ) (fresh ψ) not_bound_in ψ"
  by (metis L9 max.commute)

text ‹Irrelevance lemma: updating a non-free first-order variable does not
  affect truth.›

lemma L12:
  "y not_free_in φ  (I,D,E⟩,g[yd],G d φ) = (I,D,E⟩,g,G d φ)"
  by (induct φ arbitrary: g G; simp; metis L4 L2)

text ‹First-order variable-for-variable substitution.  The second-order
  binder descends transparently.›

primrec Subst ("[__]'(_')")
  where
    "[xz](rd(u,v)) = rd((if x = u then z else u), (if x = v then z else v))"
  | "[xz](Xd(w)) = Xd(if x = w then z else w)"
  | "[xz](¬dφ) = ¬d([xz](φ))"
  | "[xz](φ d ψ) = ([xz](φ) d [xz](ψ))"
  | "[xz](dy. φ) = (if x = y then (dy. φ) else (dy. [xz](φ)))"
  | "[xz](d2Y. φ) = (d2Y. [xz](φ))"

lemma L13 [simp]: "size [xz](φ) = size φ"
  by (induct φ; auto)

lemma L14 [simp]: "[xx](φ) = φ"
  by (induct φ; auto)

lemma L15:
  assumes "x  a"
  shows "[az]([ax](φ)) = [ax](φ)"
  using assms by (induct φ) auto

lemma L16 [simp]:
  assumes "a  x"
  shows "a not_free_in ([ax](φ))"
  using assms by (induct φ) auto

text ‹Size-based induction principle (size-based on both existential
  binders).›

lemma SInduct:
  assumes "r u v. P (rd(u,v))"
    and "X z. P (Xd(z))"
    and "φ. (ψ. size ψ  size φ  P ψ)  P (¬dφ)"
    and "φ ψ. (χ. size χ  size φ + size ψ  P χ)  P (φ d ψ)"
    and "y φ. (ψ. size ψ  size φ  P ψ)  P (dy. φ)"
    and "Y φ. (ψ. size ψ  size φ  P ψ)  P (d2Y. φ)"
  shows "P φ"
  using assms
proof (induct "size φ" arbitrary: φ rule: less_induct)
  case less thus ?case by (induct φ) auto
qed

text ‹Stronger induction: structural for the propositional cases,
  size-based for the two binders.›

lemma QInduct:
  assumes "r u v. P (rd(u,v))"
    and "X z. P (Xd(z))"
    and "φ. P φ  P (¬dφ)"
    and "φ ψ. P φ  P ψ  P (φ d ψ)"
    and "y φ. (ψ. size ψ  size φ  P ψ)  P (dy. φ)"
    and "Y φ. (ψ. size ψ  size φ  P ψ)  P (d2Y. φ)"
  shows "P φ"
  using assms by (induct φ rule: SInduct) auto

text ‹Substitutability predicate: z› may safely replace x› in φ›
  without capture.›

primrec SubstitutableForIn ("_ is'_subst'_for _ in _" [999,1,999] 999)
  where
    "z is_subst_for x in (rd(u,v)) = True"
  | "z is_subst_for x in (Xd(w)) = True"
  | "z is_subst_for x in (¬dφ) = (z is_subst_for x in φ)"
  | "z is_subst_for x in (φ d ψ) = (z is_subst_for x in φ  z is_subst_for x in ψ)"
  | "z is_subst_for x in (dy. φ) = (y = x  (x not_free_in φ  y  z)  z is_subst_for x in φ)"
  | "z is_subst_for x in (d2Y. φ) = (z is_subst_for x in φ)"

text ‹Substitution lemma: a syntactic z›-for-x› substitution
  corresponds to updating the assignment.›

lemma SubstitutionLemma [simp]:
  assumes "z is_subst_for x in φ"
  shows "(I,D,E⟩,g,G d ([xz](φ))) = (I,D,E⟩,g[x(g z)],G d φ)"
  using assms by (induction φ arbitrary: g G; auto simp: L12 L2)

text ‹Alphabetic renaming preparing capture-avoiding substitution.›

fun ren_for_subst
  where
    "ren_for_subst x z (rd(u,v)) = rd(u,v)"
  | "ren_for_subst x z (Xd(w)) = Xd(w)"
  | "ren_for_subst x z (¬dφ) = ¬d(ren_for_subst x z φ)"
  | "ren_for_subst x z (φ d ψ) = (ren_for_subst x z φ d ren_for_subst x z ψ)"
  | "ren_for_subst x z (dy. φ) =
       (if y = z  x free_in φ
        then let f = max (fresh φ) (z+1); φ' = [yf](φ)
             in (df. ren_for_subst x z φ')
        else dy. ren_for_subst x z φ)"
  | "ren_for_subst x z (d2Y. φ) = d2Y. ren_for_subst x z φ"

lemma L17 [simp]: "size (ren_for_subst x z φ) = size φ"
  by (induct φ arbitrary: z x rule: QInduct; simp add: Let_def)

lemma L18: "α not_in φ  α is_subst_for β in φ"
  by (induct φ) auto

lemma L19: "x free_in ψ  y  x  x free_in [yz](ψ)"
  by (induct ψ) auto

lemma L20 [simp]:
  "x free_in ren_for_subst x z φ = (x free_in φ)"
  by (induct φ rule: QInduct; simp add: Let_def)
     (metis L16 L6 L7 L19 max.absorb3 max_def_raw)

lemma L21:
  "(I,D,E⟩,g,G d φ) = (I,D,E⟩,g,G d (ren_for_subst x z φ))"
  by (induct φ arbitrary: z g G rule: QInduct;
      simp add: Let_def;
      smt (verit) L12 L18 L2 L3 L5 L6 SubstitutionLemma
        max.strict_order_iff max_def)

lemma L22: "z is_subst_for x in (ren_for_subst x z φ)"
  by (induct φ rule: QInduct; simp;
      metis L13 SubstitutableForIn.simps(5)
        Suc_n_not_le_n dual_order.refl max.cobounded2)

lemma L23: "x is_subst_for x in φ"
  by (induct φ) auto

lemma L24: "x not_free_in φ  [xz](φ) = φ"
  by (induct φ) auto

lemma L26 [simp]: "z bound_in [xy](φ) = z bound_in φ"
  by (induct φ) auto

text ‹Safe (capture-avoiding) first-order substitution: rename first, then
  substitute.›

definition ren_subst ("[_ r _]'(_')")
  where "[x r z](φ) = [xz](ren_for_subst x z φ)"

lemma L27 [simp]:
  "(I,D,E⟩,g,G d [x r z](φ)) = (I,D,E⟩,g[xg z],G d φ)"
  using L21 L22 ren_subst_def by auto

lemma L28 [simp]: "size ([x r z](φ)) = size φ"
  by (induct φ rule: QInduct) (auto simp: ren_subst_def Let_def)

lemma L29:
  "g onto D  (I,D,E⟩,g,G d (dx. φ)) = (z. I,D,E⟩,g,G d ([x r z](φ)))"
  by (induct φ arbitrary: I g G x rule: QInduct; simp; blast)

subsection ‹Second-order machinery (Part B)›

primrec is_free2 (infix "free2'_in" 900)
  where
    "X free2_in (rd(u,v)) = False"
  | "X free2_in (Yd(z)) = (X = Y)"
  | "X free2_in (¬dφ) = (X free2_in φ)"
  | "X free2_in (φ d ψ) = (X free2_in φ  X free2_in ψ)"
  | "X free2_in (dy. φ) = (X free2_in φ)"
  | "X free2_in (d2Y. φ) = (X free2_in φ  X  Y)"

abbreviation is_not_free2 (infix "not'_free2'_in" 900)
  where "X not_free2_in φ  ¬ (X free2_in φ)"

fun is_bound2 (infix "bound2'_in" 900)
  where
    "X bound2_in (rd(u,v)) = False"
  | "X bound2_in (Yd(z)) = False"
  | "X bound2_in (¬dφ) = (X bound2_in φ)"
  | "X bound2_in (φ d ψ) = (X bound2_in φ  X bound2_in ψ)"
  | "X bound2_in (dy. φ) = (X bound2_in φ)"
  | "X bound2_in (d2Y. φ) = (X = Y  X bound2_in φ)"

abbreviation is_not_bound2 (infix "not'_bound2'_in" 900)
  where "X not_bound2_in φ  ¬ (X bound2_in φ)"

abbreviation not_in2 (infix "not2'_in" 900)
  where "X not2_in φ  X not_free2_in φ  X not_bound2_in φ"

primrec fresh2
  where
    "fresh2 (rd(u,v)) = 0"
  | "fresh2 (Yd(z)) = Y+1"
  | "fresh2 (¬dφ) = fresh2 φ"
  | "fresh2 (φ d ψ) = max (fresh2 φ) (fresh2 ψ)"
  | "fresh2 (dy. φ) = fresh2 φ"
  | "fresh2 (d2Y. φ) = max (Y+1) (fresh2 φ)"

lemma N5: "X bound2_in φ  X < (fresh2 φ)"
  by (induct φ) auto

lemma N6: "X free2_in φ  X < (fresh2 φ)"
  by (induct φ) auto

lemma N7: "(fresh2 φ) not2_in φ"
  using N5 N6 by blast

lemma N8: "max (fresh2 φ) (fresh2 ψ) not_free2_in φ"
  by (metis N6 N7 max.absorb3 max_def)

lemma N9: "max (fresh2 φ) (fresh2 ψ) not_bound2_in φ"
  by (metis N5 N7 max.absorb3 max_def)

lemma N10: "max (fresh2 φ) (fresh2 ψ) not_free2_in ψ"
  by (metis N8 max.commute)

lemma N11: "max (fresh2 φ) (fresh2 ψ) not_bound2_in ψ"
  by (metis N9 max.commute)

text ‹Irrelevance lemma for second-order assignments.›

lemma N12:
  "Y not_free2_in φ  (I,D,E⟩,g,GYS d φ) = (I,D,E⟩,g,G d φ)"
  by (induct φ arbitrary: g G; simp; metis M4 M2)

text ‹Second-order variable-for-variable substitution.  The first-order
  binder descends transparently.›

primrec Subst2 ("[_2_]'(_')")
  where
    "[X2Z](rd(u,v)) = rd(u,v)"
  | "[X2Z](Yd(w)) = (if X = Y then Z else Y)d(w)"
  | "[X2Z](¬dφ) = ¬d([X2Z](φ))"
  | "[X2Z](φ d ψ) = ([X2Z](φ) d [X2Z](ψ))"
  | "[X2Z](dy. φ) = (dy. [X2Z](φ))"
  | "[X2Z](d2Y. φ) = (if X = Y then (d2Y. φ) else (d2Y. [X2Z](φ)))"

lemma N13 [simp]: "size [X2Z](φ) = size φ"
  by (induct φ; auto)

lemma N14 [simp]: "[X2X](φ) = φ"
  by (induct φ; auto)

lemma N16 [simp]:
  assumes "A  X"
  shows "A not_free2_in ([A2X](φ))"
  using assms by (induct φ) auto

primrec SubstitutableForIn2 ("_ is'_subst2'_for _ in _" [999,1,999] 999)
  where
    "Z is_subst2_for X in (rd(u,v)) = True"
  | "Z is_subst2_for X in (Yd(w)) = True"
  | "Z is_subst2_for X in (¬dφ) = (Z is_subst2_for X in φ)"
  | "Z is_subst2_for X in (φ d ψ) = (Z is_subst2_for X in φ  Z is_subst2_for X in ψ)"
  | "Z is_subst2_for X in (dy. φ) = (Z is_subst2_for X in φ)"
  | "Z is_subst2_for X in (d2Y. φ) = (Y = X  (X not_free2_in φ  Y  Z)  Z is_subst2_for X in φ)"

lemma SubstitutionLemma2 [simp]:
  assumes "Z is_subst2_for X in φ"
  shows "(I,D,E⟩,g,G d ([X2Z](φ))) = (I,D,E⟩,g,GX(G Z) d φ)"
  using assms by (induction φ arbitrary: g G; auto simp: N12 M2)

fun ren_for_subst2
  where
    "ren_for_subst2 X Z (rd(u,v)) = rd(u,v)"
  | "ren_for_subst2 X Z (Yd(w)) = Yd(w)"
  | "ren_for_subst2 X Z (¬dφ) = ¬d(ren_for_subst2 X Z φ)"
  | "ren_for_subst2 X Z (φ d ψ) = (ren_for_subst2 X Z φ d ren_for_subst2 X Z ψ)"
  | "ren_for_subst2 X Z (dy. φ) = dy. ren_for_subst2 X Z φ"
  | "ren_for_subst2 X Z (d2Y. φ) =
       (if Y = Z  X free2_in φ
        then let f = max (fresh2 φ) (Z+1); φ' = [Y2f](φ)
             in (d2f. ren_for_subst2 X Z φ')
        else d2Y. ren_for_subst2 X Z φ)"

lemma N17 [simp]: "size (ren_for_subst2 X Z φ) = size φ"
  by (induct φ arbitrary: Z X rule: QInduct; simp add: Let_def)

lemma N18: "α not2_in φ  α is_subst2_for β in φ"
  by (induct φ) auto

lemma N19: "X free2_in ψ  Y  X  X free2_in [Y2Z](ψ)"
  by (induct ψ) auto

lemma N20 [simp]:
  "X free2_in ren_for_subst2 X Z φ = (X free2_in φ)"
  by (induct φ rule: QInduct; simp add: Let_def)
     (metis N16 N6 N7 N19 max.absorb3 max_def_raw)

lemma N21:
  "(I,D,E⟩,g,G d φ) = (I,D,E⟩,g,G d (ren_for_subst2 X Z φ))"
  by (induct φ arbitrary: Z g G rule: QInduct;
      simp add: Let_def;
      smt (verit) N12 N18 M2 M3 N5 N6 SubstitutionLemma2
        max.strict_order_iff max_def)

lemma N22: "Z is_subst2_for X in (ren_for_subst2 X Z φ)"
  by (induct φ rule: QInduct; simp;
      metis N13 SubstitutableForIn2.simps(6)
        Suc_n_not_le_n dual_order.refl max.cobounded2)

definition ren_subst2 ("[_ r2 _]'(_')")
  where "[X r2 Z](φ) = [X2Z](ren_for_subst2 X Z φ)"

lemma N27 [simp]:
  "(I,D,E⟩,g,G d [X r2 Z](φ)) = (I,D,E⟩,g,GXG Z d φ)"
  using N21 N22 ren_subst2_def by auto

lemma N28 [simp]: "size ([X r2 Z](φ)) = size φ"
  by (induct φ rule: QInduct) (auto simp: ren_subst2_def Let_def)

lemma N29:
  "G onto E  (I,D,E⟩,g,G d (d2X. φ)) = (Z. I,D,E⟩,g,G d ([X r2 Z](φ)))"
  by (induct φ arbitrary: I g G X rule: QInduct; simp; blast)

end