Theory Logic

theory Logic
  imports Main
begin

chapter Propositional formulae
text 
  This theory contains syntax and semantics of propositional formulae.
  Furthermore, it contains a function that converts a formula into
  disjunctive normal form. This conversion is justified
  with a proof term. 


datatype (atoms: 'a) fm =
  is_Atom: Atom 'a | And "'a fm" "'a fm" | Or "'a fm" "'a fm" |
  Neg "'a fm"

fun amap_fm :: "('a  'b fm)  'a fm  'b fm" ("amapfm") where
  "amapfm h (Atom a) = h a" |
  "amapfm h (And φ1 φ2) = And (amapfm h φ1) (amapfm h φ2)" |
  "amapfm h (Or φ1 φ2) = Or (amapfm h φ1) (amapfm h φ2)" |
  "amapfm h (Neg φ) = Neg (amapfm h φ)"

fun dnf_and_fm :: "'a fm  'a fm  'a fm" where
  "dnf_and_fm (Or φ1 φ2) ψ = Or (dnf_and_fm φ1 ψ) (dnf_and_fm φ2 ψ)" |
  "dnf_and_fm ψ (Or φ1 φ2) = Or (dnf_and_fm ψ φ1) (dnf_and_fm ψ φ2)" |
  "dnf_and_fm φ1 φ2 = And φ1 φ2"

fun dnf_fm :: "'a fm  'a fm" where
  "dnf_fm (And φ1 φ2) = dnf_and_fm (dnf_fm φ1) (dnf_fm φ2)" |
  "dnf_fm (Or φ1 φ2) = Or (dnf_fm φ1) (dnf_fm φ2)" |
  "dnf_fm x = x"

fun conj_list :: "'a fm  'a list" where
  "conj_list (And φ1 φ2) = conj_list φ1 @ conj_list φ2" |
  "conj_list (Atom a) = [a]"

fun disj_conj_list :: "'a fm  'a list list" where
  "disj_conj_list (Or φ1 φ2) = disj_conj_list φ1 @ disj_conj_list φ2" |
  "disj_conj_list (And φ1 φ2) = [conj_list (And φ1 φ2)]" |
  "disj_conj_list (Atom a) = [[a]]"

definition "dnf  disj_conj_list o dnf_fm"

lemma dnf_and_fm_induct[case_names DistribR DistribL NoDistrib]:
  fixes φ1 φ2 :: "'a fm"
  assumes "φ1 φ2 ψ. P φ1 ψ; P φ2 ψ  P (Or φ1 φ2) ψ"
  assumes "ψ φ1 φ2. P ψ φ1; P ψ φ2; x y. ψ = Or x y  P ψ (Or φ1 φ2)"
  assumes "φ1 φ2. x y. φ1 = Or x y; x y. φ2 = Or x y  P φ1 φ2"
  shows "P φ1 φ2"
  apply(induction φ1 φ2 rule: dnf_and_fm.induct)
  using assms by simp_all

fun is_conj :: "'a fm  bool" where
  "is_conj (And φ1 φ2)  is_conj φ1  is_conj φ2" |
  "is_conj (Or _ _)  False" |
  "is_conj (Neg _)  False" |
  "is_conj (Atom _)  True"

fun dnormal :: "'a fm  bool" where
  "dnormal (Or φ1 φ2)  dnormal φ1  dnormal φ2" |
  "dnormal (And φ1 φ2)  is_conj (And φ1 φ2)" |
  "dnormal (Neg _)  False" |
  "dnormal _  True"

fun nfree :: "'a fm  bool" where
  "nfree (Atom φ)  True" |
  "nfree (And φ1 φ2)  nfree φ1  nfree φ2" |
  "nfree (Or φ1 φ2)  nfree φ1  nfree φ2" |
  "nfree (Neg _)  False"

lemma atoms_amapfm: "atoms (amapfm f φ) = (a  atoms φ. atoms (f a))"
  by (induction φ) auto

lemma atoms_dnf_and_fm[simp]: "atoms (dnf_and_fm φ1 φ2) = atoms (And φ1 φ2)"
  by (induction φ1 φ2 rule: dnf_and_fm.induct) auto

lemma atoms_dnf_fm[simp]: "atoms (dnf_fm φ) = atoms φ"
  using atoms_dnf_and_fm
  by (induction φ) auto

lemma atoms_conj_list: "is_conj φ  set (conj_list φ) = atoms φ"
  by (induction φ) auto

lemma atoms_disj_conj_list: "dnormal φ  (A  set (disj_conj_list φ). set A) = atoms φ"
  by (induction φ) (auto simp: atoms_conj_list)

lemma nfree_amapfm: "(x. nfree (f x))  nfree (amapfm f φ) = nfree φ"
  by (induction φ) auto

lemma nfree_amapfmI: "(x. nfree (f x))  nfree φ  nfree (amapfm f φ)"
  using nfree_amapfm by blast

lemma nfree_dnf_and_fm[simp]: "nfree (dnf_and_fm φ1 φ2) = nfree (And φ1 φ2)"
  apply(induction "φ1" "φ2" rule: dnf_and_fm.induct)
  by auto

lemma nfree_dnf_fm[simp]: "nfree (dnf_fm φ) = nfree φ"
  apply(induction φ)
  by auto

lemma dnormal_conj: "is_conj φ  dnormal φ"
  apply(induction φ)
  by auto

lemma dnormal_dnf_and_fm:
  assumes "nfree φ1" "nfree φ2"
    and "dnormal φ1" "dnormal φ2"
  shows "dnormal (dnf_and_fm φ1 φ2)"
  using assms
proof(induction φ1 φ2 rule: dnf_and_fm_induct)
  case (DistribL ψ φ1 φ2)
  then show ?case
    apply(cases ψ) by auto
next
  case (NoDistrib φ1 φ2)
  then show ?case
    apply(cases φ1; cases φ2) by auto
qed simp

lemma dnormal_dnf_fm[intro]: "nfree φ  dnormal (dnf_fm φ)"
  apply(induction φ) by (auto simp: dnormal_dnf_and_fm)

fun "interp" :: "('model  'a  bool)  'model  'a fm  bool" where
  "interp Ia M (Atom a) = Ia M a" |
  "interp Ia M (And φ1 φ2) = (interp Ia M φ1  interp Ia M φ2)" |
  "interp Ia M (Or φ1 φ2) = (interp Ia M φ1  interp Ia M φ2)" |
  "interp Ia M (Neg φ) = (¬ interp Ia M φ)"

locale ATOM =
  fixes Ia :: "'model  'a  bool"
begin

abbreviation I where "I  interp Ia"

lemma I_amapfm: "x. I M (h x) = Ia M x  I M (amapfm h φ) = I M φ"
  by (induction φ) auto

lemma I_dnf_and_fm: "nfree φ1  nfree φ2  I M (dnf_and_fm φ1 φ2) = I M (And φ1 φ2)"
  apply(induction φ1 φ2 rule: dnf_and_fm.induct)
  by auto

lemma I_dnf_fm: "nfree φ  I M (dnf_fm φ) = I M φ"
  apply(induction φ)
  by (auto simp: I_dnf_and_fm)

lemma I_conj_list: "is_conj φ  (a  set (conj_list φ). Ia M a) = I M φ"
  apply(induction φ)
  by auto

lemma I_disj_conj_list: "dnormal φ 
  (as  set (disj_conj_list φ). a  set as. Ia M a) = I M φ"
proof(induction φ)
  case (And φ1 φ2)
  hence "is_conj (And φ1 φ2)" by simp
  from I_conj_list[OF this] show ?case by simp
qed auto

lemma I_dnf: "nfree φ  (as  set (dnf φ). a  set as. Ia M a) = I M φ"
  unfolding dnf_def
  using I_disj_conj_list[OF dnormal_dnf_fm] I_dnf_fm by simp

end

locale nnf = ATOM Ia for Ia :: "'model  'a  bool" +
  fixes aneg :: "'a  'a fm"
  assumes nfree_aneg: "nfree (aneg a)"
  assumes Ia_aneg: "interp Ia M (aneg a) = (¬ Ia M a)"
begin

fun nnf :: "'a fm  'a fm" where
  "nnf (And φ1 φ2) = And (nnf φ1) (nnf φ2)" |
  "nnf (Or φ1 φ2) = Or (nnf φ1) (nnf φ2)" |
  "nnf (Neg (Neg φ)) = (nnf φ)" |
  "nnf (Neg (And φ1 φ2)) = (Or (nnf (Neg φ1)) (nnf (Neg φ2)))" |
  "nnf (Neg (Or φ1 φ2)) = (And (nnf (Neg φ1)) (nnf (Neg φ2)))" |
  "nnf (Neg (Atom a)) = aneg a" |
  "nnf φ = φ"

lemma nfree_nnf: "nfree (nnf φ)"
  apply(induction φ rule: nnf.induct)
  using nfree_aneg by auto

lemma I_nnf: "I M (nnf φ) = I M φ"
  by (induction rule: nnf.induct) (simp_all add: Ia_aneg)

end

type_synonym var = int

datatype trm = Const String.literal | App trm trm | Var var

datatype prf_trm =
  PThm String.literal |
  Appt prf_trm trm |
  AppP prf_trm prf_trm |
  AbsP trm prf_trm |
  Bound trm |
  Conv trm prf_trm prf_trm

abbreviation "AppPs p ps  foldl AppP p ps"
abbreviation "AppPThm s  AppP (PThm s)"
abbreviation "ApptThm s  Appt (PThm s)"
abbreviation "AppPThms s ps  AppPs (PThm s) ps"

abbreviation "AtomConv  AppPThm (STR ''atom_conv'')"
abbreviation "NegAtomConv  AppPThm (STR ''neg_atom_conv'')"
abbreviation "BinopConv c1 c2 
  AppPThms (STR ''combination_conv'') [AppPThm (STR ''arg_conv'') c1, c2]"
abbreviation "ArgConv  AppPThm (STR ''arg_conv'')"
abbreviation "NegNegConv  PThm (STR ''not_not_conv'')"
abbreviation "NegAndConv  PThm (STR ''de_Morgan_conj_conv'')"
abbreviation "NegOrConv  PThm (STR ''de_Morgan_disj_conv'')"
abbreviation "ConjDisjDistribRConv  PThm (STR ''conj_disj_distribR_conv'')"
abbreviation "ConjDisjDistribLConv  PThm (STR ''conj_disj_distribL_conv'')"
abbreviation "ThenConv c1 c2  AppPThms (STR ''then_conv'') [c1, c2]"
abbreviation "AllConv  PThm (STR ''all_conv'')"

fun trm_of_fm :: "('a  trm)  'a fm  trm" where
"trm_of_fm f (Atom a) = f a" |
"trm_of_fm f (And φ1 φ2) = App (App (Const (STR ''conj'')) (trm_of_fm f φ1)) (trm_of_fm f φ2)" |
"trm_of_fm f (Or φ1 φ2) = App (App (Const (STR ''disj'')) (trm_of_fm f φ1)) (trm_of_fm f φ2)" |
"trm_of_fm f (Neg φ) = App (Const (STR ''Not'')) (trm_of_fm f φ)"

fun thm_free :: "String.literal  prf_trm  bool" where
"thm_free a (PThm n)  a  n" |
"thm_free a (AppP p1 p2)  thm_free a p1  thm_free a p2" |
"thm_free a (Appt p _)  thm_free a p"

abbreviation "a_conv_free  thm_free (STR ''atom_conv'')"
abbreviation "aneg_conv_free  thm_free (STR ''neg_atom_conv'')"

locale fm_conv_system = ATOM Ia for Ia :: "'model  'a  bool" +
  fixes M :: 'model
  fixes a_convs :: "prf_trm  'a  'a fm  bool"
  fixes aneg_convs :: "prf_trm  'a  'a fm  bool"
begin

inductive convs :: "prf_trm  'a fm  'a fm  bool"
  ("_ : _ fm _" [] 60) where
  andC[simplified]: "p1 : A fm C  p2 : B fm D  BinopConv p1 p2 : And A B fm And C D" |
  orC[simplified]: "p1 : A fm C  p2 : B fm D   BinopConv p1 p2 : Or A B fm Or C D" |
  negC: "p : A fm B   ArgConv p : Neg A fm Neg B" |
  negNegC: "NegNegConv : Neg (Neg A) fm A" |
  negAndC: "NegAndConv : Neg (And A B) fm Or (Neg A) (Neg B)" |
  negOrC: "NegOrConv : Neg (Or A B) fm And (Neg A) (Neg B)" |
  AtomC: "a_convs p a B AtomConv p : Atom a fm B" |
  negAtomC: "aneg_convs p a B  NegAtomConv p : Neg (Atom a) fm B" |
  conjDisjDistribRC: "ConjDisjDistribRConv : And (Or A B) C fm Or (And A C) (And B C)" |
  conjDisjDistribLC: "ConjDisjDistribLConv : And A (Or B C) fm Or (And A B) (And A C)" |
  thenC[simplified]: "p1 : A fm B  p2 : B fm C  ThenConv p1 p2 : A fm C" |
  allC: "AllConv : A fm A"

lemma convs_sound:
  assumes "p : φ fm ψ"
  assumes "ap a φa. a_convs ap a φa  Ia M a = I M φa"
  assumes "ap a φa. aneg_convs ap a φa  ¬ (Ia M a) = I M φa"
  shows "I M φ = I M ψ"
  using assms
  apply(induction p φ ψ rule: convs.induct) by auto

lemma convs_aneg_conv_free_sound:
  assumes "p : φ fm ψ"
  assumes "aneg_conv_free p" "ap a φa. a_convs ap a φa  Ia M a = I M φa"
  shows "I M φ = I M ψ"
  using assms
  apply (induction p φ ψ rule: convs.induct) by auto

lemma convs_a_conv_free_sound:
  assumes "p : φ fm ψ"
  assumes "a_conv_free p" "ap a φa. aneg_convs ap a φa  ¬ (Ia M a) = I M φa"
  shows "I M φ = I M ψ"
  using assms
  apply(induction p φ ψ rule: convs.induct) by auto

lemma convs_atom_conv_free_sound:
  assumes "p : φ fm ψ"
  assumes "a_conv_free p" "aneg_conv_free p"
  shows "I M φ = I M ψ"
  using assms
  apply(induction p φ ψ rule: convs.induct) by auto

end

locale fm_conv = fm_conv_system Ia M a_convs aneg_convs for
  Ia :: "'model  'a  bool" and
  M :: 'model and
  a_convs :: "prf_trm  'a  'a fm  bool" and
  aneg_convs :: "prf_trm  'a  'a fm  bool" +

  fixes conv :: "'a fm  'a fm"
  fixes conv_prf :: "'a fm  prf_trm"
  assumes conv_convs: "conv_prf φ : φ fm (conv φ)  I M φ = I M (conv φ)"
begin
end

fun amapfm_prf :: "('a  prf_trm)  'a fm  prf_trm" where
"amapfm_prf ap (Atom a) = AtomConv (ap a)" |
"amapfm_prf ap (And φ1 φ2) = BinopConv (amapfm_prf ap φ1) (amapfm_prf ap φ2)" |
"amapfm_prf ap (Or φ1 φ2) = BinopConv (amapfm_prf ap φ1) (amapfm_prf ap φ2)" |
"amapfm_prf ap (Neg φ) = ArgConv (amapfm_prf ap φ)"

locale amapfm_conv = fm_conv_system Ia M a_convs aneg_convs for
  Ia :: "'model  'a  bool" and
  M :: 'model and
  a_convs :: "prf_trm  'a  'a fm  bool" and
  aneg_convs :: "prf_trm  'a  'a fm  bool" +
  
  fixes atom_conv :: "'a  'a fm"
  fixes atom_conv_prf :: "'a  prf_trm"
  assumes atom_conv_sound: "I M (atom_conv a) = Ia M a"
  assumes aneg_conv_free_atom_conv_prf: "aneg_conv_free (atom_conv_prf a)"
  assumes atom_conv_prf_convs: "a_convs (atom_conv_prf a) a (atom_conv a)"
begin

lemma aneg_conv_free_amapfm_prf: "aneg_conv_free (amapfm_prf atom_conv_prf φ)"
  using aneg_conv_free_atom_conv_prf by (induction φ) auto

lemma amapfm_conv_convs: "amapfm_prf atom_conv_prf A : A fm amapfm atom_conv A"
  apply (induction A)
  using atom_conv_prf_convs AtomC andC orC negC by fastforce+

lemma I_amapfm_conv: "I M (amapfm atom_conv A) = I M A"
  by (simp add: ATOM.I_amapfm atom_conv_sound)

sublocale fm_conv: fm_conv Ia M a_convs aneg_convs "amapfm atom_conv" "amapfm_prf atom_conv_prf"
  using I_amapfm_conv by unfold_locales blast

end
  

fun dnf_and_fm_prf :: "'a fm  'a fm  prf_trm" where
  "dnf_and_fm_prf (Or φ1 φ2) ψ = ThenConv ConjDisjDistribRConv
    (BinopConv (dnf_and_fm_prf φ1 ψ) (dnf_and_fm_prf φ2 ψ))" |
  "dnf_and_fm_prf ψ (Or φ1 φ2) = ThenConv ConjDisjDistribLConv
    (BinopConv (dnf_and_fm_prf ψ φ1) (dnf_and_fm_prf ψ φ2))" |
  "dnf_and_fm_prf φ1 φ2 = AllConv"

fun dnf_fm_prf :: "'a fm  prf_trm" where
  "dnf_fm_prf (And φ1 φ2) = ThenConv (BinopConv (dnf_fm_prf φ1) (dnf_fm_prf φ2))
    (dnf_and_fm_prf (dnf_fm φ1) (dnf_fm φ2))" |
  "dnf_fm_prf (Or φ1 φ2) = BinopConv (dnf_fm_prf φ1) (dnf_fm_prf φ2)" |
  "dnf_fm_prf _ = AllConv"


locale dnf_fm_conv = fm_conv_system
begin

lemma dnf_and_fm_convs: "dnf_and_fm_prf φ1 φ2 : And φ1 φ2 fm dnf_and_fm φ1 φ2"
proof (induction φ1 φ2 rule: dnf_and_fm_induct)
  case (DistribR φ1 φ2 ψ)
  then show ?case
    using thenC[OF conjDisjDistribRC orC[OF DistribR.IH]] by simp
next
  case (DistribL ψ φ1 φ2)
  then show ?case
    using thenC[OF conjDisjDistribLC orC[OF DistribL.IH]] by (cases ψ) simp_all
next
  case (NoDistrib φ1 φ2)
  then show ?case
    apply(cases φ1; cases φ2) by (simp_all add: allC)
qed

lemma a_conv_free_dnf_and_fm_prf: "a_conv_free (dnf_and_fm_prf φ1 φ2)"
  by (induction φ1 φ2 rule: dnf_and_fm_prf.induct) auto

lemma aneg_conv_free_dnf_and_fm_prf: "aneg_conv_free (dnf_and_fm_prf φ1 φ2)"
  by (induction φ1 φ2 rule: dnf_and_fm_prf.induct) auto

lemma dnf_fm_convs: "dnf_fm_prf φ : φ fm dnf_fm φ"
proof(induction φ)
  case (And φ1 φ2)
  then show ?case
    using thenC[where ?B="And (dnf_fm φ1) (dnf_fm φ2)", OF andC]
    by (simp add: dnf_and_fm_convs)
qed (auto simp: convs.intros)

lemma a_conv_free_dnf_fm_prf: "a_conv_free (dnf_fm_prf φ)"
  using a_conv_free_dnf_and_fm_prf
  by (induction φ rule: dnf_fm_prf.induct) auto

lemma aneg_conv_free_dnf_fm_prf: "aneg_conv_free (dnf_fm_prf φ)"
  using aneg_conv_free_dnf_and_fm_prf
  by (induction φ rule: dnf_fm_prf.induct) auto

sublocale fm_conv: fm_conv Ia M a_convs aneg_convs dnf_fm dnf_fm_prf
  apply(unfold_locales)
  using convs_atom_conv_free_sound[OF _ a_conv_free_dnf_fm_prf aneg_conv_free_dnf_fm_prf] .

end


locale nnf_conv = nnf Ia aneg + fm_conv_system Ia M a_convs aneg_convs for
  Ia :: "'model  'a  bool" and aneg :: "'a  'a fm" and
  M :: 'model and
  a_convs :: "prf_trm  'a  'a fm  bool" and
  aneg_convs :: "prf_trm  'a  'a fm  bool" +

  fixes aneg_prf :: "'a  prf_trm"
  assumes a_conv_free_aneg_prf: "a_conv_free (aneg_prf a)"
  assumes aneg_convs: "aneg_convs (aneg_prf a) a (aneg a)"
  assumes a_convs_sound: "aneg_convs p a A  (¬ Ia M a) = I M A"
begin

fun nnf_prf :: "'a fm  prf_trm" where
  "nnf_prf (And φ1 φ2) = BinopConv (nnf_prf φ1) (nnf_prf φ2)" |
  "nnf_prf (Or φ1 φ2) = BinopConv (nnf_prf φ1) (nnf_prf φ2)" |
  "nnf_prf (Neg (Neg φ)) = ThenConv NegNegConv (nnf_prf φ)" |
  "nnf_prf (Neg (And φ1 φ2)) = ThenConv NegAndConv
    (BinopConv (nnf_prf (Neg φ1)) (nnf_prf (Neg φ2)))" |
  "nnf_prf (Neg (Or φ1 φ2)) = ThenConv NegOrConv
    (BinopConv (nnf_prf (Neg φ1)) (nnf_prf (Neg φ2)))" |
  "nnf_prf (Neg (Atom a)) = NegAtomConv (aneg_prf a)" |
  "nnf_prf φ = AllConv"

lemma a_conv_free_nnf_prf: "a_conv_free (nnf_prf φ)"
  using a_conv_free_aneg_prf
  apply(induction φ rule: nnf_prf.induct) by auto

sublocale fm_conv: fm_conv Ia M a_convs aneg_convs nnf nnf_prf
  using a_convs_sound a_conv_free_nnf_prf
  apply(unfold_locales)
  by (simp add: I_nnf)

lemma nnf_convs: "nnf_prf φ : φ fm nnf φ"
proof(induction φ rule: nnf.induct)
  case (3 φ)
  then show ?case 
    by (auto simp: convs.intros intro: thenC[where ?B=φ])
next
  case (4 φ1 φ2)
  then show ?case
    by (auto simp: convs.intros intro: thenC[where ?B="Or (Neg φ1) (Neg φ2)"])
next
  case (5 φ1 φ2)
  then show ?case
    by (auto simp: convs.intros intro: thenC[where ?B="And (Neg φ1) (Neg φ2)"])
qed (auto simp: aneg_convs convs.intros)

end

definition "Atoms A  {a |a. Atom a  A}"

lemma Atoms_Un[simp]: "Atoms (A  B) = Atoms A  Atoms B"
  unfolding Atoms_def by auto

lemma Atoms_mono: "A  B  Atoms A  Atoms B"
  unfolding Atoms_def by auto

abbreviation "ConjE trm_of c d p 
  AppPThms (STR ''conjE'') [Bound (trm_of (And c d)), AbsP (trm_of c) (AbsP (trm_of d) p)]"
abbreviation "DisjE trm_of c d p1 p2 
  AppPThms (STR ''disjE'') [Bound (trm_of (Or c d)), AbsP (trm_of c) p1, AbsP (trm_of d) p2]"

locale prop_fm_system = fm_conv_system Ia M a_convs aneg_convs for 
    Ia :: "'model  'a  bool" and
    M :: 'model and
    a_convs :: "prf_trm  'a  'a fm  bool" and
    aneg_convs :: "prf_trm  'a  'a fm  bool" +

  fixes atom_proves :: "'a set  prf_trm  'a fm  bool"
  fixes trm_of_atom :: "'a  trm"
  assumes atom_proves_sound: "atom_proves A ap φ  a  A. Ia M a  I M φ"
  assumes atom_proves_weaken: "atom_proves A ap φ  A  B  atom_proves B ap φ"
  assumes convs_sound: "convs cp φ ψ  I M φ = I M ψ"
begin

abbreviation "trm_of  trm_of_fm trm_of_atom"

inductive proves :: "'a fm set  prf_trm  'a fm  bool"
  ("_  _ : _" [] 50) where
  conjE[simplified]: "And c d  A  insert c (insert d A)  p : φ
     A  ConjE trm_of c d p : φ" |
  disjE[simplified]: "Or c d  A  insert c A  p1 : φ  insert d A  p2 : φ
     A  DisjE trm_of c d p1 p2 : φ" |
  conv[simplified]: "c  A  cp : c fm d  insert d A  p : φ
     A  Conv (trm_of c) cp p : φ" |
  lift: "atom_proves (Atoms A) p φ  A  p : φ"

lemma lift2: "atom_proves A p φ  Atom ` A  p : φ"
  using proves.lift unfolding Atoms_def
  by (simp add: atom_proves_weaken subsetI)

lemma proves_sound: "A  p : φ  α  A. I M α  I M φ"
proof(induction A p φ rule: proves.induct)
  case (conv c A d cp p φ)
  then show ?case using convs_sound by fastforce
next
  case (lift A p a)
  then show ?case using atom_proves_sound unfolding Atoms_def by fastforce
qed fastforce+

lemma proves_contr:
  assumes "A  p : φ"
  assumes "¬ I M φ"
  obtains ψ where "ψ  A" "¬ I M ψ"
  using assms proves_sound by blast

lemma proves_weaken_Un: "A  p : φ  A  B  p : φ"
proof(induction A p φ arbitrary: B rule: proves.induct)
  case (lift A p φ)
  then have "atom_proves (Atoms (A  B)) p φ"
    unfolding Atoms_def
    using atom_proves_weaken[where ?B="{a |a. Atom a  A  B}"] by auto
  then show ?case by (auto simp: proves.lift)
qed (fastforce intro: proves.intros)+

lemma proves_weaken: "A  p : φ  A  B  B  p : φ"
  using proves_weaken_Un unfolding subset_Un_eq by metis

end

context
  fixes trm_of_atom :: "'a  trm"
begin

fun from_conj_prf :: "prf_trm  'a fm  prf_trm" where
"from_conj_prf p (And a b) = ConjE (trm_of_fm trm_of_atom) a b (from_conj_prf (from_conj_prf p b) a)" |
"from_conj_prf p (Atom a) = p"

end

context
  fixes trm_of_atom :: "'a  trm"
  fixes contr_atom_prf :: "'a list  prf_trm option"
begin

fun contr_fm_prf :: "'a fm  prf_trm option" where
"contr_fm_prf (Or c d) =
  (case contr_fm_prf c of
    Some p1  map_option (DisjE (trm_of_fm trm_of_atom) c d p1) (contr_fm_prf d)
  | _  None)" |
"contr_fm_prf (And a b) =
  (case contr_atom_prf (conj_list (And a b)) of
    None  None
  | Some p  Some (from_conj_prf trm_of_atom p (And a b)))" |
"contr_fm_prf (Atom a) = contr_atom_prf [a]"

end

lemma (in prop_fm_system) from_conj_prf_proves:
  assumes "is_conj φ"
  assumes proves_conj_list: "Atom ` set (conj_list φ)  A  p : ψ"
  shows "insert φ A  from_conj_prf trm_of_atom p φ : ψ"
  using assms
proof(induction p φ arbitrary: A rule: from_conj_prf.induct[of _ trm_of_atom])
  case (1 p a b)
  from "1.IH"[where ?A="Atom ` set (conj_list a)  A"] "1.prems"
  have "Atom ` set (conj_list a)  insert b A  from_conj_prf trm_of_atom p b : ψ"
    by (simp add: image_Un Un_commute sup_left_commute)
  from "1.IH"(2)[OF _ this] "1.prems"
  have "insert a (insert b (insert (And a b) A))
     from_conj_prf trm_of_atom (from_conj_prf trm_of_atom p b) a : ψ"
    using proves_weaken[where ?B="insert a (insert b (insert (And a b) A))"]
    by auto
  with "1.prems" show ?case using proves.conjE by simp
next
  case (2 p a)
  then show ?case
    using proves_weaken_Un[where ?B="A" and ?A="{Atom a}"] by simp
qed simp_all


locale contr_fm = prop_fm_system Ia M a_convs aneg_convs atom_proves for 
    Ia :: "'model  'a  bool" and
    M :: 'model and
    a_convs :: "prf_trm  'a  'a fm  bool" and
    aneg_convs :: "prf_trm  'a  'a fm  bool" and
    atom_proves :: "'a set  prf_trm  'a fm  bool" +
    
  fixes contr_atom_prf :: "'a list  prf_trm option"
  fixes Fls :: 'a
  assumes Ia_Fls: "¬ Ia M Fls"
  assumes contr_atom_prf_proves: "contr_atom_prf A = Some p  atom_proves (set A) p (Atom Fls)"
begin

lemma contr_fm_prf_proves:
  assumes "dnormal φ"
  assumes "contr_fm_prf trm_of_atom contr_atom_prf φ = Some p"
  shows "insert φ A  p : Atom Fls"
  using assms
proof(induction φ arbitrary: p)
  case (Atom x)
  then show ?case
    using contr_atom_prf_proves[THEN lift2] proves_weaken_Un by fastforce
next
  case (And φ1 φ2)
  then obtain pa where
    p: "p = from_conj_prf trm_of_atom pa (And φ1 φ2)" and
    pa: "contr_atom_prf (conj_list (And φ1 φ2)) = Some pa"
    by (auto split: option.splits)
  with contr_atom_prf_proves[OF pa]
  have "Atom ` set (conj_list (And φ1 φ2))  A  pa : (Atom Fls)"
    using lift2 proves_weaken_Un by blast
  from from_conj_prf_proves[OF _ this] dnormal (And φ1 φ2) show ?case
    unfolding p by simp
next
  case (Or φ1 φ2)
  then obtain p1 p2 where
    p: "p = DisjE trm_of φ1 φ2 p1 p2" and
    "insert φ1 A  p1 : Atom Fls" "insert φ2 A  p2 : Atom Fls"
    by (auto split: option.splits)
  then have
    "insert φ1 (insert (Or φ1 φ2) A)  p1 : Atom Fls"
    "insert φ2 (insert (Or φ1 φ2) A)  p2 : Atom Fls"
    by (metis insert_commute proves_weaken subset_insertI)+
  from proves.disjE[OF insertI1 this] p show ?case by simp
qed simp

lemma contr_fm_prf_sound:
  assumes "dnormal φ"
  assumes "contr_fm_prf trm_of_atom contr_atom_prf φ = Some p"
  shows "¬ I M φ"
  using contr_fm_prf_proves[OF assms] Ia_Fls proves_sound by fastforce

lemma contr_dnf_fm_prf_proves:
  assumes "nfree φ"
  assumes "map_option (Conv (trm_of_fm trm_of_atom φ) (dnf_fm_prf φ)) 
    (contr_fm_prf trm_of_atom contr_atom_prf (dnf_fm φ)) = Some p"
  shows "insert φ A  p: Atom Fls"
  using assms
  using contr_fm_prf_proves conv[OF _ dnf_fm_conv.dnf_fm_convs]
  by blast

end

lemma (in ATOM) Ex_conj_contr_atom_prf_None:
  assumes "dnormal φ"
  assumes "contr_fm_prf trm_of_atom contr_atom_prf φ = None"
  shows "A  set (disj_conj_list φ). contr_atom_prf A = None"
  using assms
  by (induction φ) (auto split: option.splits)

lemma (in ATOM) I_fm_if_I_conj:
  assumes "dnormal φ"
  assumes "A  set (disj_conj_list φ)" "a  set A. Ia M a"
  shows "I M φ"
  using assms
proof(induction φ)
  case (And φ1 φ2)
  then show ?case using I_disj_conj_list by blast
qed auto

end