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[y←d],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
    "[x←z](rd(u,v)) = rd((if x = u then z else u), (if x = v then z else v))"
  | "[x←z](Xd(w)) = Xd(if x = w then z else w)"
  | "[x←z](¬dφ) = ¬d([x←z](φ))"
  | "[x←z](φ ∧d ψ) = ([x←z](φ) ∧d [x←z](ψ))"
  | "[x←z](∃dy. φ) = (if x = y then (∃dy. φ) else (∃dy. [x←z](φ)))"
  | "[x←z](∃d2Y. φ) = (∃d2Y. [x←z](φ))"

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

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

lemma L15:
  assumes "x ≠ a"
  shows "[a←z]([a←x](φ)) = [a←x](φ)"
  using assms by (induct φ) auto

lemma L16 [simp]:
  assumes "a ≠ x"
  shows "a not_free_in ([a←x](φ))"
  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 ([x←z](φ))) = (⟨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); φ' = [y←f](φ)
             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 [y←z](ψ)"
  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 φ ⟹ [x←z](φ) = φ"
  by (induct φ) auto

lemma L26 [simp]: "z bound_in [x←y](φ) = 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](φ) = [x←z](ren_for_subst x z φ)"

lemma L27 [simp]:
  "(⟨I,D,E⟩,g,G ⊨d [x ←r z](φ)) = (⟨I,D,E⟩,g[x←g 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,G⟨Y←S⟩ ⊨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
    "[X←2Z](rd(u,v)) = rd(u,v)"
  | "[X←2Z](Yd(w)) = (if X = Y then Z else Y)d(w)"
  | "[X←2Z](¬dφ) = ¬d([X←2Z](φ))"
  | "[X←2Z](φ ∧d ψ) = ([X←2Z](φ) ∧d [X←2Z](ψ))"
  | "[X←2Z](∃dy. φ) = (∃dy. [X←2Z](φ))"
  | "[X←2Z](∃d2Y. φ) = (if X = Y then (∃d2Y. φ) else (∃d2Y. [X←2Z](φ)))"

lemma N13 [simp]: "size [X←2Z](φ) = size φ"
  by (induct φ; auto)

lemma N14 [simp]: "[X←2X](φ) = φ"
  by (induct φ; auto)

lemma N16 [simp]:
  assumes "A ≠ X"
  shows "A not_free2_in ([A←2X](φ))"
  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 ([X←2Z](φ))) = (⟨I,D,E⟩,g,G⟨X←(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); φ' = [Y←2f](φ)
             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 [Y←2Z](ψ)"
  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](φ) = [X←2Z](ren_for_subst2 X Z φ)"

lemma N27 [simp]:
  "(⟨I,D,E⟩,g,G ⊨d [X ←r2 Z](φ)) = (⟨I,D,E⟩,g,G⟨X←G 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