Theory Prenex_Normal_Form

(* Title:        Part of the proof of compactness of first-order logic following Harrison's one in
 *               HOL-Light
 * Author:       Sophie Tourret <sophie.tourret at inria.fr>, 2023 *)

theory Prenex_Normal_Form  
imports
    Ground_FOL_Compactness
begin


inductive is_prenex :: "form  bool" where
  qfree φ  is_prenex φ 
| is_prenex φ  is_prenex (x. φ)
| is_prenex φ  is_prenex (x. φ)

inductive_simps is_prenex_simps [simp]:
  "is_prenex Bot"
  "is_prenex (Atom p ts)"
  "is_prenex (φ  ψ)"
  "is_prenex ( x. φ)" 

lemma prenex_formsubst1: is_prenex φ  is_prenex (φ fm σ)
proof (induction φ arbitrary: σ rule: is_prenex.induct)
  case 1
  then show ?case using is_prenex.intros(1) qfree_formsubst
    by blast
next
  case (2 φ x)
  then show ?case
    using formsubst_def_switch by (metis (no_types, lifting) formsubst.simps(4) is_prenex.intros(2))
next
  case (3 φ x)
  then show ?case
    using formsubst_def_switch is_prenex.intros(3)
    by (smt (verit, del_insts) formsubst.simps(1) formsubst.simps(3) formsubst.simps(4))
qed

lemma prenex_formsubst2: is_prenex (φ fm σ)  is_prenex φ
proof (induction φ fm σ arbitrary: φ σ rule: is_prenex.induct)
  case 1
  then show ?case
    using is_prenex.intros(1) qfree_formsubst by auto
next
  case (2 ψ x φ)
  then obtain y φ' where phi_is: φ = y. φ'
    using formsubst_structure_all by metis
  then have σ'. ψ = φ'fm σ'
    using 2(3) by (metis (no_types, lifting) form.sel(6) formsubst.simps(4))
  then obtain σ' where ψ = φ'fm σ'
    by blast
  then have is_prenex φ'
    using 2(2) by blast
  then show ?case
    using phi_is by (simp add: is_prenex.intros(2))
next
  case (3 ψ x φ)
  then obtain y φ' where phi_is: φ = y. φ'
    using formsubst_structure_ex by metis
  then have σ'. ψ = φ'fm σ'
    using 3(3) by (smt (verit, ccfv_threshold) form.inject(2) form.inject(3) formsubst.simps(3)
        formsubst.simps(4))
  then obtain σ' where ψ = φ'fm σ'
    by blast
  then have is_prenex φ'
    using 3(2) by blast
  then show ?case
    using phi_is by (simp add: is_prenex.intros(3))
qed

lemma prenex_formsubst: is_prenex (φ fm σ)  is_prenex φ
  using prenex_formsubst1 prenex_formsubst2 by (smt (verit, ccfv_threshold))

lemma prenex_imp: is_prenex (φ  ψ)  
  qfree (φ  ψ)  (ψ =   (x φ'. is_prenex φ'  φ = (x. φ'  )))
  by (metis form.distinct(11) form.inject(2) is_prenex.cases)

inductive universal :: "form  bool" where
  qfree φ  universal φ
| universal φ  universal (x. φ)

inductive_simps universal_simps [simp]:
  "universal Bot"
  "universal (Atom p ts)"
  "universal (φ  ψ)"
  "universal ( x. φ)" 

fun size :: "form  nat" where
  size  = 1
| size (Atom p ts) = 1
| size (φ  ψ) = size φ + size ψ
| size ( x. φ) = 1 + size φ

lemma wf_size: wfP (λφ ψ. size φ < size ψ)
  by (simp add: wfP_if_convertible_to_nat)

lemma size_indep_subst: size (φ fm σ) = size φ
proof (induction φ arbitrary: σ)
  case (Forall x φ)
  have z σ'.(x. φ) fm σ = z. (φ fm σ')
    by (meson formsubst.simps(4))
  then obtain z σ' where (x. φ) fm σ = z. (φ fm σ')
    by blast
  then have size ((x. φ) fm σ) = size (z. (φ fm σ'))
    by argo
  also have ... = size (x. φ)
    using Forall by auto
  finally show ?case .
qed auto


lemma prenex_distinct: (x. φ)  (y. ψ)
  by auto

lemma uniq_all_x: "Uniq (λx. p. r = x. p)" (* necessaire pour décharger le "THE" *)
  using Uniq_def by blast

lemma uniq_all_p: Uniq ((λp. r = (THE x. p. r = x. p). p))
  using uniq_all_x Uniq_def
  by (smt (verit, ccfv_threshold) form.inject(3))

lemma uniq_ex_x: "Uniq (λx. p. r = x. p)"
  using Uniq_def by blast

lemma uniq_ex_p: Uniq ((λp. r = (THE x. p. r = x. p). p))
  using uniq_ex_x Uniq_def
  by (smt (verit, best) form.inject(2) form.inject(3))

definition ppat :: "(nat  form  form)  (nat  form  form)  (form  form)  form  form" where
  ppat A B C r = (if (x p. r = x. p) then
    A (THE x. p. r = x. p) (THE p. r = (THE x. p. r = x. p). p)
  else (if x p. r = x. p then
    B (THE x. p. r = x. p) (THE p. r = (THE x. p. r = x. p). p) 
   else C r))

lemma ppat_simpA: x p. ppat A B C (x. p) = A x p
  unfolding ppat_def by simp

lemma ppat_simpB: x p. ppat A B C (x. p) = B x p
  unfolding ppat_def by simp

(* simplified unneeded hypotheses: (∀x p. ppat A B C (x. p) = A x p) ⟹ (∀x p. ppat A B C (x. p) = B x p) *)
lemma ppat_last: (r. ¬(x p. r = x. p)  ¬(x p. r = x. p))  ppat A B C r = C r
  by blast

(* idem here *)
lemma ppat_last_qfree: qfree r  ppat A B C r = C r
  using qfree_no_quantif ppat_last by (simp add: ppat_def)

(* holds but useless because not recursive *)
lemma ppat_to_ex_qfree:
  (f. (x p q. f p (x. q) = ((A :: form  nat  form  form) p) x q) 
  (x p q. f p (x. q) = (B p) x q)  
  (p q. qfree q  f p q = (C p) q))
proof
  define f where f = (λp q. ppat (A p) (B p) (C p) q)
  have A_eq: (x p q. ppat (A p) (B p) (C p) (x. q) = (A p) x q) and 
    B_eq: (x p q. ppat (A p) (B p) (C p) (x. q) = (B p) x q)
    unfolding ppat_def by simp+
  have  C_eq: (p q. qfree q  ppat (A p) (B p) (C p) q = (C p) q)
    using ppat_last_qfree by blast
  show (x p q. f p ( x. q) = A p x q)  (x p q. f p (x. q) = B p x q)  (p q. qfree q  f p q = (C p) q)
    using A_eq B_eq C_eq unfolding f_def by blast
qed

lemma size_rec: 
  f g x. ((z::form). size z < size x  (f z = g z))  (H f x = H g x)  (f. x. f x = H f x)
  using wfrec [of "measure size" H] by (metis cut_apply in_measure wf_measure)

abbreviation prenex_right_forall :: "(form  form  form)  form  nat  form  form" where 
  prenex_right_forall  
    (λp φ x ψ. (let y = variant(FV φ  FV (x. ψ)) in (y. p φ (ψ fm (subst x (Var y))))))

abbreviation prenex_right_exists :: "(form  form  form)  form  nat  form  form" where 
  prenex_right_exists  
    (λp φ x ψ. (let y = variant(FV φ  FV (x. ψ)) in (y. p φ (ψ fm (subst x (Var y))))))

lemma prenex_right_ex: 
  prenex_right. (φ x ψ. prenex_right φ (x. ψ) = prenex_right_forall prenex_right φ x ψ)
     (φ x ψ. prenex_right φ (x. ψ) = prenex_right_exists prenex_right φ x ψ)
     (φ ψ. qfree ψ  prenex_right φ ψ = (φ  ψ))
proof -
  have φ. prenex_right_only. ψ. prenex_right_only ψ = ppat 
    (λx ψ. (let y = variant(FV φ  FV (x. ψ)) in (y. prenex_right_only (ψ fm (subst x (Var y))))))
    (λx ψ. (let y = variant(FV φ  FV (x. ψ)) in (y. prenex_right_only (ψ fm (subst x (Var y))))))
    (λψ. (φ  ψ)) ψ
  proof
    fix φ
    define A where A = (λg x ψ. (let y = variant(FV φ  FV (x. ψ)) in (y. g (ψ fm (subst x (Var y))))))
    define B where B = (λp x ψ. (let y = variant(FV φ  FV (x. ψ)) in (y. p (ψ fm (subst x (Var y))))))
    show prenex_right_only. ψ. prenex_right_only ψ = 
      ppat (A prenex_right_only) (B prenex_right_only) (λψ. (φ  ψ)) ψ
    proof (rule size_rec, (rule allI)+, (rule impI))
      fix prenex_right_only g:: "form  form" and ψ
      assume IH: z. size z < size ψ  prenex_right_only z = g z
      show ppat (A prenex_right_only) (B prenex_right_only) (λψ. (φ  ψ)) ψ =
        ppat (A g) (B g) (λψ. (φ  ψ)) ψ
      proof (cases "x ψ'. ψ = x. ψ'")
        case True
        then obtain x ψ' where psi_is: "ψ = x. ψ'"
          by blast
        then have smaller: size (ψ' fm σ) < size ψ for σ
          using size_indep_subst by simp
        have ppat (A prenex_right_only) (B prenex_right_only) (λψ. (φ  ψ)) ψ = 
          A prenex_right_only x ψ'
          unfolding ppat_def by (simp add: psi_is)
        also have ... = A g x ψ'
          unfolding A_def using IH smaller by presburger
        also have ... = ppat (A g) (B g) (λψ. (φ  ψ)) ψ
          unfolding ppat_def by (simp add: psi_is)
        finally show ?thesis .
      next
        case False
        assume falseAll: ¬(x ψ'. ψ =  x. ψ')
        then show ?thesis
        proof (cases "x ψ'. ψ = x. ψ'")
          case True
          then obtain x ψ' where psi_is: "ψ = x. ψ'"
            by blast
          then have smaller: size (ψ' fm σ) < size ψ for σ
            using size_indep_subst by simp
        have ppat (A prenex_right_only) (B prenex_right_only) (λψ. (φ  ψ)) ψ = 
          B prenex_right_only x ψ'
          unfolding ppat_def by (simp add: psi_is)
        also have ... = B g x ψ'
          unfolding B_def using IH smaller by presburger
        also have ... = ppat (A g) (B g) (λψ. (φ  ψ)) ψ
          unfolding ppat_def by (simp add: psi_is)
        finally show ?thesis .
        next
          case False
          then show ?thesis
            using falseAll ppat_last unfolding ppat_def by argo
        qed 
      qed
    qed
  qed
  then have prenex_right. φ ψ. prenex_right φ ψ = ppat
                (prenex_right_forall prenex_right φ)
                (prenex_right_exists prenex_right φ) 
                (() φ) ψ
    using choice[of "λφ p. ψ. p ψ =
            ppat (λx ψ. let y = variant (FV φ  FV (x. ψ)) in y. p (ψ fm subst x (Var y)))
              (λx ψ. let y = variant (FV φ  FV (x. ψ)) in (y. p (ψ fm subst x (Var y))))
              (() φ) ψ"] by blast
  then obtain prenex_right where prenex_right_is: φ ψ. prenex_right φ ψ = 
    ppat (prenex_right_forall prenex_right φ) (prenex_right_exists prenex_right φ) (() φ) ψ
    by blast
(* then show each property separately *)
  have φ x ψ. prenex_right φ (x. ψ) = prenex_right_forall prenex_right φ x ψ
    using prenex_right_is unfolding ppat_def by simp
  moreover have φ x ψ. prenex_right φ (x. ψ) = prenex_right_exists prenex_right φ x ψ
    using prenex_right_is unfolding ppat_def by simp
  moreover have φ ψ. qfree ψ  prenex_right φ ψ = (φ  ψ)
    using prenex_right_is by (metis (no_types, lifting) ppat_last_qfree)
  ultimately show ?thesis
    by blast
qed

(* is it unique? → No, it is undefined in the last case if ¬qfree φ. Use SOME, not THE  *)
consts prenex_right :: "form  form  form"
specification (prenex_right) (φ x ψ. prenex_right φ (x. ψ) = prenex_right_forall prenex_right φ x ψ) 
  (φ x ψ. prenex_right φ (x. ψ) = prenex_right_exists prenex_right φ x ψ) 
  (φ ψ. qfree ψ  prenex_right φ ψ = (φ  ψ))
  using prenex_right_ex by blast

lemma prenex_right_qfree_case: qfree ψ  prenex_right φ ψ = (φ  ψ)
proof -
  assume qfree_psi: "qfree ψ"
  have ((φ x ψ. p φ (x. ψ) = prenex_right_forall p φ x ψ) 
  (φ x ψ. p φ (x. ψ) = prenex_right_exists p φ x ψ) 
  (φ ψ. qfree ψ  p φ ψ = (φ  ψ)))  (φ ψ. qfree ψ  p φ ψ = (φ  ψ)) (is "?P p  ?Q p") for p 
    by argo
  then have (φ ψ. qfree ψ  prenex_right φ ψ = (φ  ψ))
    using someI2_ex[of ?P ?Q] prenex_right_def prenex_right_ex by presburger
  then show ?thesis
    using qfree_psi by blast
qed

lemma prenex_right_all_case: prenex_right φ (x. ψ) = prenex_right_forall prenex_right φ x ψ
proof -
  have all_cases_imp_all_case: ((φ x ψ. p φ (x. ψ) = prenex_right_forall p φ x ψ) 
   (φ x ψ. p φ (x. ψ) = prenex_right_exists p φ x ψ) 
   (φ ψ. qfree ψ  p φ ψ = (φ  ψ))) 
   (p φ (x. ψ) = prenex_right_forall p φ x ψ) (is "?P p  ?Q p") for p
    by meson
  then have prenex_right φ (x. ψ) = prenex_right_forall prenex_right φ x ψ
    using someI2_ex[of ?P ?Q] prenex_right_def prenex_right_ex by presburger
  then show ?thesis .
qed

lemma prenex_right_exist_case: prenex_right φ (x. ψ) = prenex_right_exists prenex_right φ x ψ
proof -
  have ex_cases_imp_ex_case: ((φ x ψ. p φ (x. ψ) = prenex_right_forall p φ x ψ) 
   (φ x ψ. p φ (x. ψ) = prenex_right_exists p φ x ψ) 
   (φ ψ. qfree ψ  p φ ψ = (φ  ψ))) 
   (p φ (x. ψ) = prenex_right_exists p φ x ψ) (is "?P p  ?Q p") for p
    by meson
  then have prenex_right φ (x. ψ) = prenex_right_exists prenex_right φ x ψ
    using someI2_ex[of ?P ?Q] prenex_right_def prenex_right_ex by presburger
  then show ?thesis .
qed

lemma prenex_right_exists_shape_case: 
  x2 σ. prenex_right φ (x. ψ) = x2. prenex_right φ (ψ fm σ)
proof -
  have all_cases_imp_all_case: ((φ x ψ. p φ (x. ψ) = prenex_right_forall p φ x ψ) 
   (φ x ψ. p φ (x. ψ) = prenex_right_exists p φ x ψ) 
   (φ ψ. qfree ψ  p φ ψ = (φ  ψ))) 
   (x2 σ. p φ (x. ψ) = x2. p φ (ψ fm σ)) (is "?P p  ?Q p") for p
    by meson
  then have x2 σ. prenex_right φ (x. ψ) = x2. prenex_right φ (ψ fm σ)
    using someI2_ex[of ?P ?Q] prenex_right_def prenex_right_ex by presburger
  then show ?thesis .
qed


abbreviation prenex_left_forall :: "(form  form  form)  form  nat  form  form" where 
  prenex_left_forall  
    (λp φ x ψ. (let y = variant(FV (x. φ)  FV ψ) in (y. p (φ fm (subst x (Var y))) ψ)))

abbreviation prenex_left_exists :: "(form  form  form)  form  nat  form  form" where 
  prenex_left_exists  
    (λp φ x ψ. (let y = variant(FV (x. φ)  FV ψ) in (y. p (φ fm (subst x (Var y))) ψ)))

lemma prenex_left_ex:
  prenex_left. (φ x ψ. prenex_left (x. φ) ψ = prenex_left_forall prenex_left φ x ψ)
     (φ x ψ. prenex_left (x. φ) ψ = prenex_left_exists prenex_left φ x ψ)
     (φ ψ. qfree φ  prenex_left φ ψ = prenex_right φ ψ)
proof -
  have ψ. prenex_left_only. φ. prenex_left_only φ = ppat
    (λx φ. (let y = variant(FV (x. φ)  FV ψ) in (y. prenex_left_only (φ fm (subst x (Var y))))))
    (λx φ. (let y = variant(FV (x. φ)  FV ψ) in (y. prenex_left_only (φ fm (subst x (Var y))))))
    (λφ. prenex_right φ ψ) φ
  proof
    fix ψ
    define A where A = (λg x φ. (let y = variant(FV (x. φ)  FV ψ) in (y. g (φ fm (subst x (Var y))))))
    define B where B = (λp x φ. (let y = variant(FV (x. φ)  FV ψ) in (y. p (φ fm (subst x (Var y))))))
    show prenex_left_only. φ. prenex_left_only φ =
      ppat (A prenex_left_only) (B prenex_left_only) (λφ. prenex_right φ ψ) φ
    proof (rule size_rec, (rule allI)+, (rule impI))
      fix prenex_left_only g:: "form  form" and φ
      assume IH: z. size z < size φ  prenex_left_only z = g z
      show ppat (A prenex_left_only) (B prenex_left_only) (λφ. prenex_right φ ψ) φ =
        ppat (A g) (B g) (λφ. prenex_right φ ψ) φ
      proof (cases "x φ'. φ = x. φ'")
        case True
        then obtain x φ' where phi_is: "φ = x. φ'"
          by blast
        then have smaller: size (φ' fm σ) < size φ for σ
          using size_indep_subst by simp
        have ppat (A prenex_left_only) (B prenex_left_only) (λφ. prenex_right φ ψ) φ = 
          A prenex_left_only x φ'
          unfolding ppat_def by (simp add: phi_is)
        also have ... = A g x φ'
          unfolding A_def using IH smaller by presburger
        also have ... = ppat (A g) (B g) (λφ. prenex_right φ ψ) φ
          unfolding ppat_def by (simp add: phi_is)
        finally show ?thesis .
      next
        case False
        assume falseAll: ¬(x φ'. φ =  x. φ')
        then show ?thesis
        proof (cases "x φ'. φ = x. φ'")
          case True
          then obtain x φ' where phi_is: "φ = x. φ'"
            by blast
          then have smaller: size (φ' fm σ) < size φ for σ
            using size_indep_subst by simp
        have ppat (A prenex_left_only) (B prenex_left_only) (λφ. prenex_right φ ψ) φ = 
          B prenex_left_only x φ'
          unfolding ppat_def by (simp add: phi_is)
        also have ... = B g x φ'
          unfolding B_def using IH smaller by presburger
        also have ... = ppat (A g) (B g) (λφ. prenex_right φ ψ) φ
          unfolding ppat_def by (simp add: phi_is)
        finally show ?thesis .
        next
          case False
          then show ?thesis
            using falseAll ppat_last unfolding ppat_def by argo
        qed 
      qed
    qed
  qed
  then have prenex_left_argswap. ψ φ. prenex_left_argswap ψ φ = ppat
    (λx φ. let y = variant (FV (x. φ)  FV ψ) in (y. prenex_left_argswap ψ (φ fm subst x (Var y))))
    (λx φ. let y = variant (FV (x. φ)  FV ψ) in  y. prenex_left_argswap ψ (φ fm subst x (Var y)))
    (λφ. prenex_right φ ψ) φ
    using choice[of "λψ p. φ. p φ =
            ppat (λx φ. let y = variant (FV (x. φ)  FV ψ) in (y. p (φ fm subst x (Var y))))
              (λx φ. let y = variant (FV (x. φ)  FV ψ) in y. p (φ fm subst x (Var y)))
              (λφ. prenex_right φ ψ) φ"] by blast
  then have prenex_left. φ ψ. prenex_left φ ψ = ppat
    (λx φ. let y = variant (FV (x. φ)  FV ψ) in (y. prenex_left (φ fm subst x (Var y)) ψ))
    (λx φ. let y = variant (FV (x. φ)  FV ψ) in  y. prenex_left (φ fm subst x (Var y)) ψ)
    (λφ. prenex_right φ ψ) φ
    by force
  then obtain prenex_left where prenex_left_is: φ ψ. prenex_left φ ψ = ppat
    (λx φ. prenex_left_forall prenex_left φ x ψ)
    (λx φ. prenex_left_exists prenex_left φ x ψ)
    (λφ. prenex_right φ ψ) φ
    by blast
  have φ x ψ. prenex_left (x. φ) ψ =  prenex_left_forall prenex_left φ x ψ
    using prenex_left_is unfolding ppat_def by simp
  moreover have φ x ψ. prenex_left (x. φ) ψ = prenex_left_exists prenex_left φ x ψ
    using prenex_left_is unfolding ppat_def by simp
  moreover have φ ψ. qfree φ  prenex_left φ ψ = prenex_right φ ψ
    using prenex_left_is by (metis (no_types, lifting) ppat_last_qfree)
  ultimately show ?thesis
    by blast
qed

definition prenex_left where prenex_left = (SOME prenex_left.
  (φ x ψ. prenex_left (x. φ) ψ = prenex_left_forall prenex_left φ x ψ) 
  (φ x ψ. prenex_left (x. φ) ψ = prenex_left_exists prenex_left φ x ψ) 
  (φ ψ. qfree φ  prenex_left φ ψ = prenex_right φ ψ))

lemma prenex_left_forall_case: prenex_left (x. φ) ψ = prenex_left_forall prenex_left φ x ψ
  unfolding prenex_left_def by (smt (verit, del_insts) prenex_left_ex some_eq_ex)

lemma prenex_left_qfree_case: qfree φ  prenex_left φ ψ = prenex_right φ ψ
  unfolding prenex_left_def by (smt (verit, del_insts) prenex_left_ex some_eq_ex)

lemma prenex_left_exists_case: prenex_left (x. φ) ψ = prenex_left_exists prenex_left φ x ψ
  unfolding prenex_left_def by (smt (verit, del_insts) prenex_left_ex some_eq_ex)

lemma prenex_left_exists_shape_case: 
  x2 σ. prenex_left (x. φ) ψ = x2. prenex_left (φ fm σ) ψ
  using prenex_left_exists_case by metis


fun prenex where
  prenex  = 
| prenex (Atom p ts) = Atom p ts
| prenex (φ  ψ) = prenex_left (prenex φ) (prenex ψ)
| prenex (x. φ) = x. (prenex φ)

lemma holds_indep_forall:
  assumes y_notin: y  FV (x. φ)
  shows (I,β  (x. φ)  I,β  (y. φ fm (subst x (Var y))))
proof (cases y = x)
  case False
  then have y_notin_phi: y  FV φ using y_notin by simp
  have beta_equiv: w  FV φ. (λv. termsubst I (β(y := a)) (subst x (Var y)) v) w = (β(x := a)) w for a
  proof
    fix w
    assume w_in: w  FV φ
    have w = x  (λv. termsubst I (β(y := a)) (subst x (Var y)) v) w = (β(x := a)) w
      by simp
    moreover have w  x  (λv. termsubst I (β(y := a)) (subst x (Var y)) v) w = (β(x := a)) w
      using y_notin_phi by (metis w_in eval.simps(1) fun_upd_other subst_def)
    ultimately show (λv. termsubst I (β(y := a)) (subst x (Var y)) v) w = (β(x := a)) w
      by argo
  qed
  have I,β  (x. φ)  (a  dom I. I,β(x := a)  φ)
    by simp
  also have ...  (a  dom I. I,(λv. termsubst I (β(y := a)) (subst x (Var y)) v)  φ)
    using holds_indep_β_if[OF beta_equiv] by presburger
  also have ...  (a  dom I. I,β(y := a)  (φ fm (subst x (Var y))))
    using swap_subst_eval[of I _ φ "subst x (Var y)"] by presburger
  also have ...  (I,β  (y. φ fm (subst x (Var y))))
    by simp
  finally show ?thesis
      by argo
qed auto

lemma forall_imp_commute:
  assumes y_notin: y  FV φ
  shows ((I :: 'a intrp), β  (φ  (y. ψ))    I, β  (y. φ  ψ))
proof -
  have ((I, β  φ)  (a  dom I. I,β(y := a)  ψ))  
    (a  dom I. (I,β(y := a)  φ  I,β(y := a)  ψ))
    by (smt (verit, del_insts) fun_upd_other holds_indep_β_if assms)
  then show ?thesis
    by simp
qed

lemma forall_imp_exists: 
  assumes y_notin: y  FV ψ
  shows ((I :: 'a intrp), β  ((y.φ)   ψ)    I, β  (y. (φ  ψ)))
proof -
  have ((a  dom I. I,β(y := a)  φ)  (I, β  ψ))   (a  dom I. (I,β(y := a)  φ  I,β  ψ))
    using empty_iff list.set(1)
    by (smt (verit, best) equals0I intrp_is_struct struct_def)
  also have ...  (a  dom I. (I,β(y := a)  φ  I,β(y := a)  ψ))
    using holds_indep_β_if by (smt (verit, del_insts) fun_upd_other y_notin)
  finally show ?thesis
    by simp
qed

lemma exists_imp_forall: 
  assumes y_notin: y  FV ψ
  shows (I, β  ((y.φ)   ψ)    I, β  (y. (φ  ψ)))
proof -
  have (a  dom I. I,β(y := a)  φ)  (I, β  ψ) 
    (a  dom I. (I,β(y := a)  φ  I,β  ψ))
    using empty_iff list.set(1) by (smt (verit, ccfv_threshold))
  also have ...  (a  dom I. (I,β(y := a)  φ  I,β(y := a)  ψ))
    using holds_indep_β_if by (smt (verit, del_insts) fun_upd_other y_notin)
  finally show ?thesis
    by simp
qed

lemma exists_imp_commute: 
  assumes y_notin: y  FV φ
  shows ((I :: 'a intrp), β  (φ  (y. ψ))  I, β  (y. φ  ψ))
proof -
  have ((I, β  φ)  (a  dom I. I,β(y := a)  ψ)) 
   (a  dom I. (I, β  φ)  (I,β(y := a)  ψ))
    by (smt (verit) equals0I intrp_is_struct struct_def)
  also have ...  (a  dom I. (I,β(y := a)  φ  I,β(y := a)  ψ))
    using y_notin by (smt (verit, ccfv_threshold) fun_upd_other holds_indep_β_if)
  finally show ?thesis
    using holds_exists by simp
qed


lemma holds_indep_exists: 
  y  FV (x. φ)  (I,β  (x. φ)    I,β  (y. φ fm (subst x (Var y))))
  by (metis FV.simps(1,3) formsubst.simps(1,3) holds.simps(3) holds_indep_forall sup_bot.right_neutral)

(* sublemmas for is_prenex(prenex _)*)

(* holds M (v:num->A) (p --> !!y (formsubst (valmod (x,V y) V) q)) *)
lemma prenex_right_forall_is:
  assumes dom I  {} 
  shows ((I, β  φ  (x. ψ))  
  (I, β  ((variant (FV φ  FV (x. ψ))). 
             (φ  (ψ fm (subst x (Var (variant (FV φ  FV (x. ψ)))))))))) (is "?lhs = ?rhs")
proof -
  define y where y = variant (FV φ  FV (x. ψ))
  then have y_notin1: y  FV φ and y_notin2: y  FV (x. ψ)
  using variant_finite finite_FV by (meson UnCI finite_UnI)+
  have ?lhs  I, β  (φ  (y. ψ fm (subst x (Var y))))
    using holds_indep_forall y_notin2
    by (smt (verit, ccfv_SIG) holds.simps(3))
  also have ...  I, β  (y. φ  (ψ fm (subst x (Var y))))
    using forall_imp_commute[OF y_notin1, of I β "ψ fm (subst x (Var y))"] .
  finally show ?thesis
    unfolding y_def .
qed

lemma prenex_right_exists_is:
  assumes dom I  {} 
  shows ((I, β  φ  (x. ψ))  
  (I, β  ((variant (FV φ  FV (x. ψ))). 
             (φ  (ψ fm (subst x (Var (variant (FV φ  FV (x. ψ)))))))))) (is "?lhs = ?rhs")
proof -
  define y where y = variant (FV φ  FV (x. ψ))
  then have y_notin1: y  FV φ and y_notin2: y  FV (x. ψ)
  using variant_finite finite_FV by (meson UnCI finite_UnI)+
  have ?lhs  I, β  (φ  (y. ψ fm (subst x (Var y))))
    using holds_indep_exists y_notin2 holds_exists by (smt (verit) holds.simps(3))
  also have ...  I, β  (y. φ  (ψ fm (subst x (Var y))))
    using exists_imp_commute[OF y_notin1, of I β "ψ fm (subst x (Var y))"] .
  finally show ?thesis
    unfolding y_def .
qed

lemma prenex_left_forall_is:
  assumes dom I  {} 
  shows (I, β  ((x. φ)  ψ))  (I, β  ((variant (FV (x. φ)  FV ψ)).
               ((φ fm (subst x (Var (variant (FV (x. φ)  FV ψ)))))  ψ)))
  using forall_imp_exists holds_indep_forall holds.simps(3)
  by (smt (verit, del_insts) FV.simps(3) UnI2 sup.commute variant_form)

lemma prenex_left_exists_is:
  assumes dom I  {} 
  shows (I, β  ((x. φ)  ψ))  (I, β  ((variant (FV (x. φ)  FV ψ)).
               ((φ fm (subst x (Var (variant (FV (x. φ)  FV ψ)))))  ψ)))
  using exists_imp_forall holds_indep_exists holds.simps(3)
  by (smt (verit, ccfv_SIG) FV.simps(3) UnCI finite_FV variant_finite)

(* sublemmas for prenex prop on FV *)
lemma prenex_right_forall_FV: FV (φ  (x. ψ)) =
  FV ((variant (FV φ  FV (x. ψ))). (φ  (ψ fm (subst x (Var (variant (FV φ  FV (x. ψ))))))))
  using formsubst_rename
  by (metis Diff_empty Diff_insert0 FV.simps(3) FV.simps(4) Un_Diff finite_FV variant_finite)

lemma prenex_right_exists_FV: FV (φ  (x. ψ)) =
  FV ((variant (FV φ  FV (x. ψ))). (φ  (ψ fm (subst x (Var (variant (FV φ  FV (x. ψ))))))))
  using formsubst_rename prenex_right_forall_FV by force

lemma prenex_left_forall_FV: FV ((x. φ)  ψ) =
  FV ((variant (FV (x. φ)  FV ψ)). ((φ fm (subst x (Var (variant (FV (x. φ)  FV ψ)))))  ψ))
  using formsubst_rename
  by (metis Diff_idemp Diff_insert_absorb FV.simps(3) FV.simps(4) Un_Diff finite_FV variant_finite FV_exists)

lemma prenex_left_exists_FV: FV ((x. φ)  ψ) =
  FV ((variant (FV (x. φ)  FV ψ)). ((φ fm (subst x (Var (variant (FV (x. φ)  FV ψ)))))  ψ))
  using formsubst_rename FV_exists prenex_left_forall_FV by auto

(* sublemmas for prenex prop on language *)
lemma prenex_right_forall_language: language {φ  (x. ψ)} =
  language {(variant (FV φ  FV (x. ψ))). (φ  (ψ fm (subst x (Var (variant (FV φ  FV (x. ψ)))))))}
  using lang_singleton formsubst_functions_form formsubst_predicates formsubst_language_rename by auto

lemma prenex_right_exists_language: language {φ  (x. ψ)} =
  language {(variant (FV φ  FV (x. ψ))). (φ  (ψ fm (subst x (Var (variant (FV φ  FV (x. ψ)))))))}
  using lang_singleton formsubst_functions_form formsubst_predicates formsubst_language_rename by auto

lemma prenex_left_forall_language: language {(x. φ)  ψ} =
  language {(variant (FV (x. φ)  FV ψ)). ((φ fm (subst x (Var (variant (FV (x. φ)  FV ψ)))))  ψ)}
  using lang_singleton formsubst_functions_form formsubst_predicates formsubst_language_rename by auto

lemma prenex_left_exists_language: language {(x. φ)  ψ} =
  language {(variant (FV (x. φ)  FV ψ)). ((φ fm (subst x (Var (variant (FV (x. φ)  FV ψ)))))  ψ)}
  using lang_singleton formsubst_functions_form formsubst_predicates formsubst_language_rename by auto

(* prenex properties lemmas *)
lemma prenex_props_forall: P  FV φ = FV ψ  language {φ} = language {ψ} 
  ((I :: 'a intrp) β. dom I  {}  (I,β  φ  I,β  ψ)) 
  P  FV (x. φ) = FV (x. ψ)  language {(x. φ)} = language {(x. ψ)} 
  ((I :: 'a intrp) β. dom I  {}  (I,β  (x. φ)  I,β  (x. ψ)))
  using lang_singleton by simp

lemma prenex_props_exists: P  FV φ = FV ψ  language {φ} = language {ψ} 
  ((I :: 'a intrp) β. dom I  {}  (I,β  φ  I,β  ψ)) 
  P  FV (x. φ) = FV (x. ψ)  language {(x. φ)} = language {(x. ψ)} 
  ((I :: 'a intrp) β. dom I  {}  (I,β  (x. φ)  I,β  (x. ψ)))
  using lang_singleton by simp

lemma prenex_right_props_imp0:
  assumes qfree φ
  shows is_prenex ψ  is_prenex (prenex_right φ ψ)
proof (induction ψ rule: measure_induct_rule [of size])
  case (less ψ)
  show ?case
  proof (cases rule: is_prenex.cases[OF is_prenex ψ])
    case (1 ξ)
    then show ?thesis
      by (simp add: assms prenex_right_qfree_case)
  next
    case (2 ξ x)
    then have prenex_right φ ψ = prenex_right_forall prenex_right φ x ξ
      using prenex_right_all_case by blast
    then show is_prenex (prenex_right φ ψ)
      using less 2 by (auto simp: Let_def prenex_formsubst1 size_indep_subst)
  next
    case (3 ξ x)
    then have y σ. prenex_right φ ψ = y. prenex_right φ (ξ fm σ)
      using prenex_right_exists_shape_case by presburger
    then obtain y σ where pr_is: prenex_right φ ψ = y. prenex_right φ (ξ fm σ)
      by blast
    have size_xp: size (ξ fm σ) < size ψ 
      using 3(1) size_indep_subst by auto
    have is_prenex (ξ fm σ)
      using 3(2) prenex_formsubst1 by blast
    then have is_prenex (prenex_right φ (ξ fm σ))
      using less size_xp by blast
    then show ?thesis
      using is_prenex.intros(3) pr_is by presburger
  qed
qed

lemma prenex_right_props_imp: 
  assumes qfree φ
  shows is_prenex ψ  
         is_prenex (prenex_right φ ψ) 
         FV (prenex_right φ ψ) = FV (φ  ψ) 
         language {prenex_right φ ψ} = language {(φ  ψ)} 
         ((I :: 'a intrp) β. dom I  {}  ((I,β  (prenex_right φ ψ))  (I,β  (φ  ψ))))
        (is is_prenex ψ  ?P ψ)
proof (induction ψ rule: measure_induct_rule [of size])
  case (less ψ)
  show ?case
  proof (cases rule: is_prenex.cases[OF is_prenex ψ])
    case (1 ξ)
    then show ?thesis
      using prenex_right_qfree_case qfree φ is_prenex.intros(1) qfree.simps(3) by presburger
  next
    case (2 ξ x)
    have pr_is1:prenex_right φ ψ = prenex_right_forall prenex_right φ x ξ
      using 2 prenex_right_all_case by blast
    define y where y = variant (FV φ  FV ( x. ξ))
    then have pr_is2: prenex_right φ ψ = y. prenex_right φ (ξ fm subst x (Var y))
      using qfree φ 2(2) pr_is1  unfolding y_def by meson
    have is_prenex (ξ fm subst x (Var y))
      using prenex_formsubst1 2(2) by presburger
    then have p_xps: ?P (ξ fm subst x (Var y))
      using less 2(1) less_Suc_eq plus_1_eq_Suc size.simps(4) size_indep_subst by presburger
    have is_prenex (prenex_right φ ψ)
      using prenex_right_props_imp0 is_prenex ψ qfree φ by blast
    moreover have FV (prenex_right φ ψ) = FV (φ  ψ)
      using prenex_right_forall_FV[of φ x ξ] by (metis 2(1) FV.simps(4) p_xps pr_is1 y_def)
    moreover have language {prenex_right φ ψ} = language {φ  ψ}
      using prenex_right_forall_language
      by (metis "2"(1) functions_form.simps(4) lang_singleton p_xps pr_is1 predicates_form.simps(4) y_def)
    moreover have ((I :: 'a intrp) β. dom I  {}  I,β  prenex_right φ ψ = I,β  φ  ψ)
      by (metis "2"(1) holds.simps(4) p_xps pr_is2 prenex_right_forall_is y_def)
    ultimately show ?thesis
      by blast
  next
    case (3 ξ x)
    have pr_is1:prenex_right φ ψ = prenex_right_exists prenex_right φ x ξ
      using 3 prenex_right_exist_case by blast
    define y where y = variant (FV φ  FV ( x. ξ))
    then have pr_is2: prenex_right φ ψ = y. prenex_right φ (ξ fm subst x (Var y))
      using qfree φ 3(2) pr_is1  unfolding y_def by meson
    have is_prenex (ξ fm subst x (Var y))
      using prenex_formsubst1 3(2) by presburger
    then have p_xps: ?P (ξ fm subst x (Var y))
      using less 3(1) less_Suc_eq plus_1_eq_Suc size.simps size_indep_subst by simp
    have is_prenex (prenex_right φ ψ)
      using prenex_right_props_imp0 is_prenex ψ qfree φ by blast
    moreover have FV (prenex_right φ ψ) = FV (φ  ψ)
      using prenex_right_exists_FV[of φ x ξ] by (metis 3(1) FV.simps(4) FV_exists p_xps
          pr_is1 y_def)
    moreover have language {prenex_right φ ψ} = language {φ  ψ}
      using prenex_right_forall_language by (smt (verit) "3"(1) p_xps pr_is1 
          prenex_props_exists prenex_right_exists_language y_def)
    moreover have ((I :: 'a intrp) β. dom I  {}  I,β  prenex_right φ ψ = I,β  φ  ψ)
      by (smt (verit, best) "3"(1) p_xps pr_is2 prenex_props_exists prenex_right_exists_is y_def)
    ultimately show ?thesis
      by blast
  qed
qed
    
lemma prenex_right_props: 
  qfree φ  is_prenex ψ 
  is_prenex (prenex_right φ ψ) 
  FV (prenex_right φ ψ) = FV (φ  ψ) 
  language {prenex_right φ ψ} = language {(φ  ψ)} 
  ((I :: 'a intrp) β. dom I  {}  ((I,β  (prenex_right φ ψ))  (I,β  (φ  ψ))))
  using prenex_right_props_imp by meson


lemma prenex_left_props_imp0:
  assumes is_prenex ψ
  shows is_prenex φ  is_prenex (prenex_left φ ψ)
proof (induction φ rule: measure_induct_rule [of size])
  case (less φ)
  show ?case
  proof (cases rule: is_prenex.cases[OF is_prenex φ])
    case (1 ξ)
    then show ?thesis
      using is_prenex φ prenex_right_props prenex_left_qfree_case is_prenex ψ by presburger
  next
    case (2 ξ x)
    then have prenex_left φ ψ = prenex_left_forall prenex_left ξ x ψ
      using prenex_left_forall_case by blast
    then show is_prenex (prenex_left φ ψ)
      using less 2 by (metis is_prenex.intros(3) lessI plus_1_eq_Suc prenex_formsubst1 size.simps(4) size_indep_subst)
  next
    case (3 ξ x)
    then have y σ. prenex_left φ ψ = y. prenex_left (ξ fm σ) ψ
      using prenex_left_exists_shape_case by presburger
    then obtain y σ where pr_is: prenex_left φ ψ = y. prenex_left (ξ fm σ) ψ
      by blast
    have size_xp: size (ξ fm σ) < size φ 
      using 3(1) size_indep_subst by auto
    have is_prenex (ξ fm σ)
      using 3(2) prenex_formsubst1 by blast
    then have is_prenex (prenex_left (ξ fm σ) ψ)
      using less size_xp by blast
    then show ?thesis
      using is_prenex.intros pr_is by presburger
  qed
qed

lemma prenex_left_props_imp:
  assumes is_prenex ψ
  shows is_prenex φ 
        is_prenex (prenex_left φ ψ) 
        FV (prenex_left φ ψ) = FV (φ  ψ) 
        (language {(prenex_left φ ψ)} = language {(φ  ψ)}) 
        ((I :: 'a intrp) β. dom I  {}  (I,β  prenex_left φ ψ  I,β  φ  ψ))
    (is is_prenex φ  ?P φ)
proof (induction φ rule: measure_induct_rule [of size])
  case (less ξ)
  show ?case 
  proof (cases rule: is_prenex.cases[OF is_prenex ξ])
    case (1 ξ')
    then show ?thesis
      using prenex_right_qfree_case is_prenex ψ 
      by (simp add: prenex_left_qfree_case prenex_right_props)
  next
    case (2 ξ' x)
    have pr_is1:prenex_left ξ ψ = prenex_left_forall prenex_left ξ' x ψ
      using 2 prenex_left_forall_case by blast
    define y where y = variant (FV (x. ξ')  FV ψ)
    then have pr_is2: prenex_left ξ ψ = y. prenex_left (ξ' fm subst x (Var y)) ψ
      using is_prenex ψ 2(2) pr_is1  unfolding y_def by meson
    have is_prenex (ξ' fm subst x (Var y))
      using prenex_formsubst1 2(2) by presburger
    then have p_xps: ?P (ξ' fm subst x (Var y))
      using less 2(1) less_Suc_eq plus_1_eq_Suc size.simps(4) size_indep_subst by presburger
    have is_prenex (prenex_left ξ ψ)
      using prenex_left_props_imp0 is_prenex ξ is_prenex ψ by blast
    moreover have FV (prenex_left ξ ψ) = FV (ξ  ψ)
      using prenex_left_forall_FV[of x ξ' ψ] by (metis 2(1) FV_exists p_xps pr_is1 y_def)
    moreover have language {prenex_left ξ ψ} = language {ξ  ψ}
      using prenex_left_forall_language 
      by (smt (verit, ccfv_threshold) 2(1) p_xps pr_is1 prenex_props_exists y_def)
    moreover have ((I :: 'a intrp) β. dom I  {}  I,β  prenex_left ξ ψ = I,β  ξ  ψ)
      by (metis "2"(1) holds_exists p_xps pr_is2 prenex_left_forall_is y_def)
    ultimately show ?P ξ
      by blast
  next
    case (3 ξ' x)
    have pr_is1:prenex_left ξ ψ = prenex_left_exists prenex_left ξ' x ψ
      using 3 prenex_left_exists_case by blast
    define y where y = variant (FV ( x. ξ')  FV ψ)
    then have pr_is2: prenex_left ξ ψ = y. prenex_left (ξ' fm subst x (Var y)) ψ
      using is_prenex ψ 3(2) pr_is1  unfolding y_def by meson
    have is_prenex (ξ' fm subst x (Var y))
      using prenex_formsubst1 3(2) by presburger
    then have p_xps: ?P (ξ' fm subst x (Var y))
      using less 3(1) less_Suc_eq plus_1_eq_Suc size.simps size_indep_subst by simp
    have is_prenex (prenex_left ξ ψ)
      using prenex_left_props_imp0 is_prenex ξ is_prenex ψ by blast
    moreover have FV (prenex_left ξ ψ) = FV (ξ  ψ)
      using prenex_left_exists_FV[of x ξ' ψ] by (metis 3(1) FV.simps(4) p_xps
          pr_is1 y_def)
    moreover have language {prenex_left ξ ψ} = language {ξ  ψ}
      using prenex_left_exists_language[of x ξ' ψ] 
      by (smt (verit) 3(1) p_xps pr_is2 prenex_props_forall y_def)
    moreover have ((I :: 'a intrp) β. dom I  {}  
        I,β  prenex_left ξ ψ = I,β  ξ  ψ)
      by (metis "3"(1) holds.simps(4) p_xps pr_is2 prenex_left_exists_is y_def)
    ultimately show ?P ξ
      by blast
  qed
qed

lemma prenex_left_props: 
  is_prenex φ  is_prenex ψ  
        is_prenex (prenex_left φ ψ) 
        FV (prenex_left φ ψ) = FV (φ  ψ) 
        (language {(prenex_left φ ψ)} = language {(φ  ψ)}) 
        ((I :: 'a intrp) β. dom I  {}  (I,β  prenex_left φ ψ  I,β  φ  ψ))
  using prenex_left_props_imp by meson

theorem prenex_props: is_prenex (prenex φ)  (FV (prenex φ) = FV φ)  
  (language {prenex φ} = language {φ}) 
  ((I :: 'a intrp) β. dom I  {}  (I, β  (prenex φ))  (I, β  φ))
proof (induction φ rule: form.induct)
  case Bot
  then show ?case
    by (metis is_prenex.simps prenex.simps(1) qfree.simps(1))
next
  case (Atom p ts)
  then show ?case
    using is_prenex.intros(1) prenex.simps(2) qfree.simps(2) by presburger
next
  case (Implies φ ψ)
  have is_prenex (prenex (φ  ψ))
    using Implies prenex_left_props prenex.simps(3) by presburger
  moreover have FV (prenex (φ  ψ)) = FV (φ  ψ)
    using Implies prenex_left_props prenex.simps(3) FV.simps(3) by presburger
  moreover have language {prenex (φ  ψ)} = language {φ  ψ}
    using Implies prenex_left_props prenex.simps(3) lang_singleton 
      functions_form.simps(3) predicates_form.simps(3) by (metis prod.inject)
  moreover have (I::'a intrp) β. FOL_Semantics.dom I  {} 
    I,β  prenex (φ  ψ) = I,β  φ  ψ
    using Implies prenex_left_props holds.simps(3) prenex.simps(3) by metis
  ultimately show ?case by blast
next
  case (Forall x φ)
  have is_prenex (prenex (x. φ))
    using Forall using is_prenex.intros(2) prenex.simps(4) by presburger
  moreover have fv_indep_prenex: FV (prenex (x. φ)) = FV (x. φ)
    using Forall FV.simps(4) prenex.simps(4) by presburger
  moreover have language {prenex (x. φ)} = language {x. φ}
    using Forall prenex.simps(4) functions_form.simps(4) predicates_form.simps(4)
    unfolding language_def functions_forms_def predicates_def by simp
  moreover have ((I :: 'a intrp) β. dom I  {}  I,β  prenex (x. φ) = I,β  (x. φ))
    using Forall holds.simps(4) by simp
  ultimately show ?case by blast
qed

corollary is_prenex_prenex [simp]: is_prenex (prenex φ) 
  and FV_prenex [simp]: FV (prenex φ) = FV φ
  and language_prenex [simp]: language {prenex φ} = language {φ}
  by (auto simp: prenex_props)

corollary prenex_holds [simp]: dom I  {}  (I,β  (prenex φ))  (I,β  φ)
  by (simp add: prenex_props)

lemma prenex_satisfies [simp]:
  assumes "dom M  {}" 
  shows "satisfies M {prenex φ}  satisfies M {φ}"
  using assms prenex_holds by (fastforce simp: satisfies_def)

end