(*  Title:      HOL/Algebra/Ideal.thy
    Author:     Stephan Hohe, TU Muenchen
*)

theory Ideal
imports Ring AbelCoset
begin

section \<open>Ideals\<close>

subsection \<open>Definitions\<close>

subsubsection \<open>General definition\<close>

locale ideal = additive_subgroup I R + ring R for I and R (structure) +
  assumes I_l_closed: "\<lbrakk>a \<in> I; x \<in> carrier R\<rbrakk> \<Longrightarrow> x \<otimes> a \<in> I"
      and I_r_closed: "\<lbrakk>a \<in> I; x \<in> carrier R\<rbrakk> \<Longrightarrow> a \<otimes> x \<in> I"

sublocale ideal \<subseteq> abelian_subgroup I R
proof (intro abelian_subgroupI3 abelian_group.intro)
  show "additive_subgroup I R"
    by (simp add: is_additive_subgroup)
  show "abelian_monoid R"
    by (simp add: abelian_monoid_axioms)
  show "abelian_group_axioms R"
    using abelian_group_def is_abelian_group by blast
qed

lemma (in ideal) is_ideal: "ideal I R"
  by (rule ideal_axioms)

lemma idealI:
  fixes R (structure)
  assumes "ring R"
  assumes a_subgroup: "subgroup I (add_monoid R)"
    and I_l_closed: "\<And>a x. \<lbrakk>a \<in> I; x \<in> carrier R\<rbrakk> \<Longrightarrow> x \<otimes> a \<in> I"
    and I_r_closed: "\<And>a x. \<lbrakk>a \<in> I; x \<in> carrier R\<rbrakk> \<Longrightarrow> a \<otimes> x \<in> I"
  shows "ideal I R"
proof -
  interpret ring R by fact
  show ?thesis
    by (auto simp: ideal.intro ideal_axioms.intro additive_subgroupI a_subgroup ring_axioms I_l_closed I_r_closed)
qed

subsubsection (in ring) \<open>Ideals Generated by a Subset of \<^term>\<open>carrier R\<close>\<close>

definition genideal :: "_ \<Rightarrow> 'a set \<Rightarrow> 'a set"  ("Idl\<index> _" [80] 79)
  where "genideal R S = \<Inter>{I. ideal I R \<and> S \<subseteq> I}"

subsubsection \<open>Principal Ideals\<close>

locale principalideal = ideal +
  assumes generate: "\<exists>i \<in> carrier R. I = Idl {i}"

lemma (in principalideal) is_principalideal: "principalideal I R"
  by (rule principalideal_axioms)

lemma principalidealI:
  fixes R (structure)
  assumes "ideal I R"
    and generate: "\<exists>i \<in> carrier R. I = Idl {i}"
  shows "principalideal I R"
proof -
  interpret ideal I R by fact
  show ?thesis
    by (intro principalideal.intro principalideal_axioms.intro)
      (rule is_ideal, rule generate)
qed

(* NEW ====== *)
lemma (in ideal) rcos_const_imp_mem:
  assumes "i \<in> carrier R" and "I +> i = I" shows "i \<in> I"
  using additive_subgroup.zero_closed[OF ideal.axioms(1)[OF ideal_axioms]] assms
  by (force simp add: a_r_coset_def')
(* ========== *)

(* NEW ====== *)
lemma (in ring) a_rcos_zero:
  assumes "ideal I R" "i \<in> I" shows "I +> i = I"
  using abelian_subgroupI3[OF ideal.axioms(1) is_abelian_group]
  by (simp add: abelian_subgroup.a_rcos_const assms)
(* ========== *)

(* NEW ====== *)
lemma (in ring) ideal_is_normal:
  assumes "ideal I R" shows "I \<lhd> (add_monoid R)"
  using abelian_subgroup.a_normal[OF abelian_subgroupI3[OF ideal.axioms(1)]]
        abelian_group_axioms assms
  by auto
(* ========== *)

(* NEW ====== *)
lemma (in ideal) a_rcos_sum:
  assumes "a \<in> carrier R" and "b \<in> carrier R" shows "(I +> a) <+> (I +> b) = I +> (a \<oplus> b)"
  using normal.rcos_sum[OF ideal_is_normal[OF ideal_axioms]] assms
  unfolding set_add_def a_r_coset_def by simp
(* ========== *)

(* NEW ====== *)
lemma (in ring) set_add_comm:
  assumes "I \<subseteq> carrier R" "J \<subseteq> carrier R" shows "I <+> J = J <+> I"
proof -
  { fix I J assume "I \<subseteq> carrier R" "J \<subseteq> carrier R" hence "I <+> J \<subseteq> J <+> I"
      using a_comm unfolding set_add_def' by (auto, blast) }
  thus ?thesis
    using assms by auto
qed
(* ========== *)

subsubsection \<open>Maximal Ideals\<close>

locale maximalideal = ideal +
  assumes I_notcarr: "carrier R \<noteq> I"
    and I_maximal: "\<lbrakk>ideal J R; I \<subseteq> J; J \<subseteq> carrier R\<rbrakk> \<Longrightarrow> (J = I) \<or> (J = carrier R)"

lemma (in maximalideal) is_maximalideal: "maximalideal I R"
  by (rule maximalideal_axioms)

lemma maximalidealI:
  fixes R
  assumes "ideal I R"
    and I_notcarr: "carrier R \<noteq> I"
    and I_maximal: "\<And>J. \<lbrakk>ideal J R; I \<subseteq> J; J \<subseteq> carrier R\<rbrakk> \<Longrightarrow> (J = I) \<or> (J = carrier R)"
  shows "maximalideal I R"
proof -
  interpret ideal I R by fact
  show ?thesis
    by (intro maximalideal.intro maximalideal_axioms.intro)
      (rule is_ideal, rule I_notcarr, rule I_maximal)
qed

subsubsection \<open>Prime Ideals\<close>

locale primeideal = ideal + cring +
  assumes I_notcarr: "carrier R \<noteq> I"
    and I_prime: "\<lbrakk>a \<in> carrier R; b \<in> carrier R; a \<otimes> b \<in> I\<rbrakk> \<Longrightarrow> a \<in> I \<or> b \<in> I"

lemma (in primeideal) primeideal: "primeideal I R"
  by (rule primeideal_axioms)

lemma primeidealI:
  fixes R (structure)
  assumes "ideal I R"
    and "cring R"
    and I_notcarr: "carrier R \<noteq> I"
    and I_prime: "\<And>a b. \<lbrakk>a \<in> carrier R; b \<in> carrier R; a \<otimes> b \<in> I\<rbrakk> \<Longrightarrow> a \<in> I \<or> b \<in> I"
  shows "primeideal I R"
proof -
  interpret ideal I R by fact
  interpret cring R by fact
  show ?thesis
    by (intro primeideal.intro primeideal_axioms.intro)
      (rule is_ideal, rule is_cring, rule I_notcarr, rule I_prime)
qed

lemma primeidealI2:
  fixes R (structure)
  assumes "additive_subgroup I R"
    and "cring R"
    and I_l_closed: "\<And>a x. \<lbrakk>a \<in> I; x \<in> carrier R\<rbrakk> \<Longrightarrow> x \<otimes> a \<in> I"
    and I_r_closed: "\<And>a x. \<lbrakk>a \<in> I; x \<in> carrier R\<rbrakk> \<Longrightarrow> a \<otimes> x \<in> I"
    and I_notcarr: "carrier R \<noteq> I"
    and I_prime: "\<And>a b. \<lbrakk>a \<in> carrier R; b \<in> carrier R; a \<otimes> b \<in> I\<rbrakk> \<Longrightarrow> a \<in> I \<or> b \<in> I"
  shows "primeideal I R"
proof -
  interpret additive_subgroup I R by fact
  interpret cring R by fact
  show ?thesis apply intro_locales
    apply (intro ideal_axioms.intro)
    apply (erule (1) I_l_closed)
    apply (erule (1) I_r_closed)
    by (simp add: I_notcarr I_prime primeideal_axioms.intro)
qed

subsection \<open>Special Ideals\<close>

lemma (in ring) zeroideal: "ideal {\<zero>} R"
  by (intro idealI subgroup.intro) (simp_all add: ring_axioms)

lemma (in ring) oneideal: "ideal (carrier R) R"
  by (rule idealI) (auto intro: ring_axioms add.subgroupI)

lemma (in "domain") zeroprimeideal: "primeideal {\<zero>} R"
proof -
  have "carrier R \<noteq> {\<zero>}"
    by (simp add: carrier_one_not_zero)
  then show ?thesis
    by (metis (no_types, lifting) domain_axioms domain_def integral primeidealI singleton_iff zeroideal)
qed

subsection \<open>General Ideal Properties\<close>

lemma (in ideal) one_imp_carrier:
  assumes I_one_closed: "\<one> \<in> I"
  shows "I = carrier R"
proof
  show "carrier R \<subseteq> I"
    using I_r_closed assms by fastforce
  show "I \<subseteq> carrier R"
    by (rule a_subset)
qed

lemma (in ideal) Icarr:
  assumes iI: "i \<in> I"
  shows "i \<in> carrier R"
  using iI by (rule a_Hcarr)

lemma (in ring) quotient_eq_iff_same_a_r_cos:
  assumes "ideal I R" and "a \<in> carrier R" and "b \<in> carrier R"
  shows "a \<ominus> b \<in> I \<longleftrightarrow> I +> a = I +> b"
proof
  assume "I +> a = I +> b"
  then obtain i where "i \<in> I" and "\<zero> \<oplus> a = i \<oplus> b"
    using additive_subgroup.zero_closed[OF ideal.axioms(1)[OF assms(1)]] assms(2)
    unfolding a_r_coset_def' by blast
  hence "a \<ominus> b = i"
    using assms(2-3) by (metis a_minus_def add.inv_solve_right assms(1) ideal.Icarr l_zero)
  with \<open>i \<in> I\<close> show "a \<ominus> b \<in> I"
    by simp
next
  assume "a \<ominus> b \<in> I"
  then obtain i where "i \<in> I" and "a = i \<oplus> b"
    using ideal.Icarr[OF assms(1)] assms(2-3)
    by (metis a_minus_def add.inv_solve_right)
  hence "I +> a = (I +> i) +> b"
    using ideal.Icarr[OF assms(1)] assms(3)
    by (simp add: a_coset_add_assoc subsetI)
  with \<open>i \<in> I\<close> show "I +> a = I +> b"
    using a_rcos_zero[OF assms(1)] by simp
qed

subsection \<open>Intersection of Ideals\<close>

paragraph \<open>Intersection of two ideals\<close>
text \<open>The intersection of any two ideals is again an ideal in \<^term>\<open>R\<close>\<close>

lemma (in ring) i_intersect:
  assumes "ideal I R"
  assumes "ideal J R"
  shows "ideal (I \<inter> J) R"
proof -
  interpret ideal I R by fact
  interpret ideal J R by fact
  have IJ: "I \<inter> J \<subseteq> carrier R"
    by (force simp: a_subset)
  show ?thesis
    apply (intro idealI subgroup.intro)
    apply (simp_all add: IJ ring_axioms I_l_closed assms ideal.I_l_closed ideal.I_r_closed flip: a_inv_def)
    done
qed

text \<open>The intersection of any Number of Ideals is again an Ideal in \<^term>\<open>R\<close>\<close>

lemma (in ring) i_Intersect:
  assumes Sideals: "\<And>I. I \<in> S \<Longrightarrow> ideal I R" and notempty: "S \<noteq> {}"
  shows "ideal (\<Inter>S) R"
proof -
  { fix x y J
    assume "\<forall>I\<in>S. x \<in> I" "\<forall>I\<in>S. y \<in> I" and JS: "J \<in> S"
    interpret ideal J R by (rule Sideals[OF JS])
    have "x \<oplus> y \<in> J"
      by (simp add: JS \<open>\<forall>I\<in>S. x \<in> I\<close> \<open>\<forall>I\<in>S. y \<in> I\<close>) }
  moreover
    have "\<zero> \<in> J" if "J \<in> S" for J
      by (simp add: that Sideals additive_subgroup.zero_closed ideal.axioms(1))
  moreover
  { fix x J
    assume "\<forall>I\<in>S. x \<in> I" and JS: "J \<in> S"
    interpret ideal J R by (rule Sideals[OF JS])
    have "\<ominus> x \<in> J"
      by (simp add: JS \<open>\<forall>I\<in>S. x \<in> I\<close>) }
  moreover
  { fix x y J
    assume "\<forall>I\<in>S. x \<in> I" and ycarr: "y \<in> carrier R" and JS: "J \<in> S"
    interpret ideal J R by (rule Sideals[OF JS])
    have "y \<otimes> x \<in> J" "x \<otimes> y \<in> J"
      using I_l_closed I_r_closed JS \<open>\<forall>I\<in>S. x \<in> I\<close> ycarr by blast+ }
  moreover
  { fix x
    assume "\<forall>I\<in>S. x \<in> I"
    obtain I0 where I0S: "I0 \<in> S"
      using notempty by blast
    interpret ideal I0 R by (rule Sideals[OF I0S])
    have "x \<in> I0"
      by (simp add: I0S \<open>\<forall>I\<in>S. x \<in> I\<close>)
    with a_subset have "x \<in> carrier R" by fast }
  ultimately show ?thesis
    by unfold_locales (auto simp: Inter_eq simp flip: a_inv_def)
qed

subsection \<open>Addition of Ideals\<close>

lemma (in ring) add_ideals:
  assumes idealI: "ideal I R" and idealJ: "ideal J R"
  shows "ideal (I <+> J) R"
proof (rule ideal.intro)
  show "additive_subgroup (I <+> J) R"
    by (intro ideal.axioms[OF idealI] ideal.axioms[OF idealJ] add_additive_subgroups)
  show "ring R"
    by (rule ring_axioms)
  show "ideal_axioms (I <+> J) R"
  proof -
    { fix x i j
      assume xcarr: "x \<in> carrier R" and iI: "i \<in> I" and jJ: "j \<in> J"
      from xcarr ideal.Icarr[OF idealI iI] ideal.Icarr[OF idealJ jJ]
      have "\<exists>h\<in>I. \<exists>k\<in>J. (i \<oplus> j) \<otimes> x = h \<oplus> k"
        by (meson iI ideal.I_r_closed idealJ jJ l_distr local.idealI) }
    moreover
    { fix x i j
      assume xcarr: "x \<in> carrier R" and iI: "i \<in> I" and jJ: "j \<in> J"
      from xcarr ideal.Icarr[OF idealI iI] ideal.Icarr[OF idealJ jJ]
      have "\<exists>h\<in>I. \<exists>k\<in>J. x \<otimes> (i \<oplus> j) = h \<oplus> k"
        by (meson iI ideal.I_l_closed idealJ jJ local.idealI r_distr) }
    ultimately show "ideal_axioms (I <+> J) R"
      by (intro ideal_axioms.intro) (auto simp: set_add_defs)
  qed
qed

subsection (in ring) \<open>Ideals generated by a subset of \<^term>\<open>carrier R\<close>\<close>

text \<open>\<^term>\<open>genideal\<close> generates an ideal\<close>
lemma (in ring) genideal_ideal:
  assumes Scarr: "S \<subseteq> carrier R"
  shows "ideal (Idl S) R"
unfolding genideal_def
proof (rule i_Intersect, fast, simp)
  from oneideal and Scarr
  show "\<exists>I. ideal I R \<and> S \<le> I" by fast
qed

lemma (in ring) genideal_self:
  assumes "S \<subseteq> carrier R"
  shows "S \<subseteq> Idl S"
  unfolding genideal_def by fast

lemma (in ring) genideal_self':
  assumes carr: "i \<in> carrier R"
  shows "i \<in> Idl {i}"
  by (simp add: genideal_def)

text \<open>\<^term>\<open>genideal\<close> generates the minimal ideal\<close>
lemma (in ring) genideal_minimal:
  assumes "ideal I R" "S \<subseteq> I"
  shows "Idl S \<subseteq> I"
  unfolding genideal_def by rule (elim InterD, simp add: assms)

text \<open>Generated ideals and subsets\<close>
lemma (in ring) Idl_subset_ideal:
  assumes Iideal: "ideal I R"
    and Hcarr: "H \<subseteq> carrier R"
  shows "(Idl H \<subseteq> I) = (H \<subseteq> I)"
proof
  assume a: "Idl H \<subseteq> I"
  from Hcarr have "H \<subseteq> Idl H" by (rule genideal_self)
  with a show "H \<subseteq> I" by simp
next
  fix x
  assume "H \<subseteq> I"
  with Iideal have "I \<in> {I. ideal I R \<and> H \<subseteq> I}" by fast
  then show "Idl H \<subseteq> I" unfolding genideal_def by fast
qed

lemma (in ring) subset_Idl_subset:
  assumes Icarr: "I \<subseteq> carrier R"
    and HI: "H \<subseteq> I"
  shows "Idl H \<subseteq> Idl I"
proof -
  from Icarr have Iideal: "ideal (Idl I) R"
    by (rule genideal_ideal)
  from HI and Icarr have "H \<subseteq> carrier R"
    by fast
  with Iideal have "(H \<subseteq> Idl I) = (Idl H \<subseteq> Idl I)"
    by (rule Idl_subset_ideal[symmetric])
  then show "Idl H \<subseteq> Idl I"
    by (meson HI Icarr genideal_self order_trans)
qed

lemma (in ring) Idl_subset_ideal':
  assumes acarr: "a \<in> carrier R" and bcarr: "b \<in> carrier R"
  shows "Idl {a} \<subseteq> Idl {b} \<longleftrightarrow> a \<in> Idl {b}"
proof -
  have "Idl {a} \<subseteq> Idl {b} \<longleftrightarrow> {a} \<subseteq> Idl {b}"
    by (simp add: Idl_subset_ideal acarr bcarr genideal_ideal)
  also have "\<dots> \<longleftrightarrow> a \<in> Idl {b}"
    by blast
  finally show ?thesis .
qed

lemma (in ring) genideal_zero: "Idl {\<zero>} = {\<zero>}"
proof
  show "Idl {\<zero>} \<subseteq> {\<zero>}"
    by (simp add: genideal_minimal zeroideal)
  show "{\<zero>} \<subseteq> Idl {\<zero>}"
    by (simp add: genideal_self')
qed

lemma (in ring) genideal_one: "Idl {\<one>} = carrier R"
proof -
  interpret ideal "Idl {\<one>}" "R" by (rule genideal_ideal) fast
  show "Idl {\<one>} = carrier R"
    using genideal_self' one_imp_carrier by blast
qed

text \<open>Generation of Principal Ideals in Commutative Rings\<close>

definition cgenideal :: "_ \<Rightarrow> 'a \<Rightarrow> 'a set"  ("PIdl\<index> _" [80] 79)
  where "cgenideal R a = {x \<otimes>\<^bsub>R\<^esub> a | x. x \<in> carrier R}"

text \<open>genhideal (?) really generates an ideal\<close>
lemma (in cring) cgenideal_ideal:
  assumes acarr: "a \<in> carrier R"
  shows "ideal (PIdl a) R"
  unfolding cgenideal_def
proof (intro subgroup.intro idealI[OF ring_axioms], simp_all)
  show "{x \<otimes> a |x. x \<in> carrier R} \<subseteq> carrier R"
    by (blast intro: acarr)
  show "\<And>x y. \<lbrakk>\<exists>u. x = u \<otimes> a \<and> u \<in> carrier R; \<exists>x. y = x \<otimes> a \<and> x \<in> carrier R\<rbrakk>
              \<Longrightarrow> \<exists>v. x \<oplus> y = v \<otimes> a \<and> v \<in> carrier R"
    by (metis assms cring.cring_simprules(1) is_cring l_distr)
  show "\<exists>x. \<zero> = x \<otimes> a \<and> x \<in> carrier R"
    by (metis assms l_null zero_closed)
  show "\<And>x. \<exists>u. x = u \<otimes> a \<and> u \<in> carrier R
            \<Longrightarrow> \<exists>v. inv\<^bsub>add_monoid R\<^esub> x = v \<otimes> a \<and> v \<in> carrier R"
    by (metis a_inv_def add.inv_closed assms l_minus)
  show "\<And>b x. \<lbrakk>\<exists>x. b = x \<otimes> a \<and> x \<in> carrier R; x \<in> carrier R\<rbrakk>
       \<Longrightarrow> \<exists>z. x \<otimes> b = z \<otimes> a \<and> z \<in> carrier R"
    by (metis assms m_assoc m_closed)
  show "\<And>b x. \<lbrakk>\<exists>x. b = x \<otimes> a \<and> x \<in> carrier R; x \<in> carrier R\<rbrakk>
       \<Longrightarrow> \<exists>z. b \<otimes> x = z \<otimes> a \<and> z \<in> carrier R"
    by (metis assms m_assoc m_comm m_closed)
qed

lemma (in ring) cgenideal_self:
  assumes icarr: "i \<in> carrier R"
  shows "i \<in> PIdl i"
  unfolding cgenideal_def
proof simp
  from icarr have "i = \<one> \<otimes> i"
    by simp
  with icarr show "\<exists>x. i = x \<otimes> i \<and> x \<in> carrier R"
    by fast
qed

text \<open>\<^const>\<open>cgenideal\<close> is minimal\<close>

lemma (in ring) cgenideal_minimal:
  assumes "ideal J R"
  assumes aJ: "a \<in> J"
  shows "PIdl a \<subseteq> J"
proof -
  interpret ideal J R by fact
  show ?thesis
    unfolding cgenideal_def
    using I_l_closed aJ by blast
qed

lemma (in cring) cgenideal_eq_genideal:
  assumes icarr: "i \<in> carrier R"
  shows "PIdl i = Idl {i}"
proof
  show "PIdl i \<subseteq> Idl {i}"
    by (simp add: cgenideal_minimal genideal_ideal genideal_self' icarr)
  show "Idl {i} \<subseteq> PIdl i"
    by (simp add: cgenideal_ideal cgenideal_self genideal_minimal icarr)
qed

lemma (in cring) cgenideal_eq_rcos: "PIdl i = carrier R #> i"
  unfolding cgenideal_def r_coset_def by fast

lemma (in cring) cgenideal_is_principalideal:
  assumes "i \<in> carrier R"
  shows "principalideal (PIdl i) R"
proof -
  have "\<exists>i'\<in>carrier R. PIdl i = Idl {i'}"
    using cgenideal_eq_genideal assms by auto
  then show ?thesis
    by (simp add: cgenideal_ideal assms principalidealI)
qed

subsection \<open>Union of Ideals\<close>

lemma (in ring) union_genideal:
  assumes idealI: "ideal I R" and idealJ: "ideal J R"
  shows "Idl (I \<union> J) = I <+> J"
proof
  show "Idl (I \<union> J) \<subseteq> I <+> J"
  proof (rule ring.genideal_minimal [OF ring_axioms])
    show "ideal (I <+> J) R"
      by (rule add_ideals[OF idealI idealJ])
    have "\<And>x. x \<in> I \<Longrightarrow> \<exists>xa\<in>I. \<exists>xb\<in>J. x = xa \<oplus> xb"
      by (metis additive_subgroup.zero_closed ideal.Icarr idealJ ideal_def local.idealI r_zero)
    moreover have "\<And>x. x \<in> J \<Longrightarrow> \<exists>xa\<in>I. \<exists>xb\<in>J. x = xa \<oplus> xb"
      by (metis additive_subgroup.zero_closed ideal.Icarr idealJ ideal_def l_zero local.idealI)
    ultimately show "I \<union> J \<subseteq> I <+> J"
      by (auto simp: set_add_defs)
  qed
next
  show "I <+> J \<subseteq> Idl (I \<union> J)"
    by (auto simp: set_add_defs genideal_def additive_subgroup.a_closed ideal_def subsetD)
qed

subsection \<open>Properties of Principal Ideals\<close>

text \<open>The zero ideal is a principal ideal\<close>
corollary (in ring) zeropideal: "principalideal {\<zero>} R"
  using genideal_zero principalidealI zeroideal by blast

text \<open>The unit ideal is a principal ideal\<close>
corollary (in ring) onepideal: "principalideal (carrier R) R"
  using genideal_one oneideal principalidealI by blast

text \<open>Every principal ideal is a right coset of the carrier\<close>
lemma (in principalideal) rcos_generate:
  assumes "cring R"
  shows "\<exists>x\<in>I. I = carrier R #> x"
proof -
  interpret cring R by fact
  from generate obtain i where icarr: "i \<in> carrier R" and I1: "I = Idl {i}"
    by fast+
  then have "I = PIdl i"
    by (simp add: cgenideal_eq_genideal)
  moreover have "i \<in> I"
    by (simp add: I1 genideal_self' icarr)
  moreover have "PIdl i = carrier R #> i"
    unfolding cgenideal_def r_coset_def by fast
  ultimately show "\<exists>x\<in>I. I = carrier R #> x"
    by fast
qed

text \<open>This next lemma would be trivial if placed in a theory that imports QuotRing,
      but it makes more sense to have it here (easier to find and coherent with the
      previous developments).\<close>

lemma (in cring) cgenideal_prod: \<^marker>\<open>contributor \<open>Paulo Emílio de Vilhena\<close>\<close>
  assumes "a \<in> carrier R" "b \<in> carrier R"
  shows "(PIdl a) <#> (PIdl b) = PIdl (a \<otimes> b)"
proof -
  have "(carrier R #> a) <#> (carrier R #> b) = carrier R #> (a \<otimes> b)"
  proof
    show "(carrier R #> a) <#> (carrier R #> b) \<subseteq> carrier R #> a \<otimes> b"
    proof
      fix x assume "x \<in> (carrier R #> a) <#> (carrier R #> b)"
      then obtain r1 r2 where r1: "r1 \<in> carrier R" and r2: "r2 \<in> carrier R"
                          and "x = (r1 \<otimes> a) \<otimes> (r2 \<otimes> b)"
        unfolding set_mult_def r_coset_def by blast
      hence "x = (r1 \<otimes> r2) \<otimes> (a \<otimes> b)"
        by (simp add: assms local.ring_axioms m_lcomm ring.ring_simprules(11))
      thus "x \<in> carrier R #> a \<otimes> b"
        unfolding r_coset_def using r1 r2 assms by blast
    qed
  next
    show "carrier R #> a \<otimes> b \<subseteq> (carrier R #> a) <#> (carrier R #> b)"
    proof
      fix x assume "x \<in> carrier R #> a \<otimes> b"
      then obtain r where r: "r \<in> carrier R" "x = r \<otimes> (a \<otimes> b)"
        unfolding r_coset_def by blast
      hence "x = (r \<otimes> a) \<otimes> (\<one> \<otimes> b)"
        using assms by (simp add: m_assoc)
      thus "x \<in> (carrier R #> a) <#> (carrier R #> b)"
        unfolding set_mult_def r_coset_def using assms r by blast
    qed
  qed
  thus ?thesis
    using cgenideal_eq_rcos[of a] cgenideal_eq_rcos[of b] cgenideal_eq_rcos[of "a \<otimes> b"] by simp
qed

subsection \<open>Prime Ideals\<close>

lemma (in ideal) primeidealCD:
  assumes "cring R"
  assumes notprime: "\<not> primeideal I R"
  shows "carrier R = I \<or> (\<exists>a b. a \<in> carrier R \<and> b \<in> carrier R \<and> a \<otimes> b \<in> I \<and> a \<notin> I \<and> b \<notin> I)"
proof (rule ccontr, clarsimp)
  interpret cring R by fact
  assume InR: "carrier R \<noteq> I"
    and "\<forall>a. a \<in> carrier R \<longrightarrow> (\<forall>b. a \<otimes> b \<in> I \<longrightarrow> b \<in> carrier R \<longrightarrow> a \<in> I \<or> b \<in> I)"
  then have I_prime: "\<And> a b. \<lbrakk>a \<in> carrier R; b \<in> carrier R; a \<otimes> b \<in> I\<rbrakk> \<Longrightarrow> a \<in> I \<or> b \<in> I"
    by simp
  have "primeideal I R"
    by (simp add: I_prime InR is_cring is_ideal primeidealI)
  with notprime show False by simp
qed

lemma (in ideal) primeidealCE:
  assumes "cring R"
  assumes notprime: "\<not> primeideal I R"
  obtains "carrier R = I"
    | "\<exists>a b. a \<in> carrier R \<and> b \<in> carrier R \<and> a \<otimes> b \<in> I \<and> a \<notin> I \<and> b \<notin> I"
proof -
  interpret R: cring R by fact
  assume "carrier R = I ==> thesis"
    and "\<exists>a b. a \<in> carrier R \<and> b \<in> carrier R \<and> a \<otimes> b \<in> I \<and> a \<notin> I \<and> b \<notin> I \<Longrightarrow> thesis"
  then show thesis using primeidealCD [OF R.is_cring notprime] by blast
qed

text \<open>If \<open>{\<zero>}\<close> is a prime ideal of a commutative ring, the ring is a domain\<close>
lemma (in cring) zeroprimeideal_domainI:
  assumes pi: "primeideal {\<zero>} R"
  shows "domain R"
proof (intro domain.intro is_cring domain_axioms.intro)
  show "\<one> \<noteq> \<zero>"
    using genideal_one genideal_zero pi primeideal.I_notcarr by force
  show "a = \<zero> \<or> b = \<zero>" if ab: "a \<otimes> b = \<zero>" and carr: "a \<in> carrier R" "b \<in> carrier R" for a b
  proof -
    interpret primeideal "{\<zero>}" "R" by (rule pi)
    show "a = \<zero> \<or> b = \<zero>"
      using I_prime ab carr by blast
  qed
qed

corollary (in cring) domain_eq_zeroprimeideal: "domain R = primeideal {\<zero>} R"
  using domain.zeroprimeideal zeroprimeideal_domainI by blast

subsection \<open>Maximal Ideals\<close>

lemma (in ideal) helper_I_closed:
  assumes carr: "a \<in> carrier R" "x \<in> carrier R" "y \<in> carrier R"
    and axI: "a \<otimes> x \<in> I"
  shows "a \<otimes> (x \<otimes> y) \<in> I"
proof -
  from axI and carr have "(a \<otimes> x) \<otimes> y \<in> I"
    by (simp add: I_r_closed)
  also from carr have "(a \<otimes> x) \<otimes> y = a \<otimes> (x \<otimes> y)"
    by (simp add: m_assoc)
  finally show "a \<otimes> (x \<otimes> y) \<in> I" .
qed

lemma (in ideal) helper_max_prime:
  assumes "cring R"
  assumes acarr: "a \<in> carrier R"
  shows "ideal {x\<in>carrier R. a \<otimes> x \<in> I} R"
proof -
  interpret cring R by fact
  show ?thesis
  proof (rule idealI, simp_all)
    show "ring R"
      by (simp add: local.ring_axioms)
    show "subgroup {x \<in> carrier R. a \<otimes> x \<in> I} (add_monoid R)"
      by (rule subgroup.intro) (auto simp: r_distr acarr r_minus simp flip: a_inv_def)
    show "\<And>b x. \<lbrakk>b \<in> carrier R \<and> a \<otimes> b \<in> I; x \<in> carrier R\<rbrakk>
                 \<Longrightarrow> a \<otimes> (x \<otimes> b) \<in> I"
      using acarr helper_I_closed m_comm by auto
    show "\<And>b x. \<lbrakk>b \<in> carrier R \<and> a \<otimes> b \<in> I; x \<in> carrier R\<rbrakk>
                \<Longrightarrow> a \<otimes> (b \<otimes> x) \<in> I"
      by (simp add: acarr helper_I_closed)
  qed
qed

text \<open>In a cring every maximal ideal is prime\<close>
lemma (in cring) maximalideal_prime:
  assumes "maximalideal I R"
  shows "primeideal I R"
proof -
  interpret maximalideal I R by fact
  show ?thesis
  proof (rule ccontr)
    assume neg: "\<not> primeideal I R"
    then obtain a b where acarr: "a \<in> carrier R" and bcarr: "b \<in> carrier R"
      and abI: "a \<otimes> b \<in> I" and anI: "a \<notin> I" and bnI: "b \<notin> I"
      using primeidealCE [OF is_cring]
      by (metis I_notcarr)
    define J where "J = {x\<in>carrier R. a \<otimes> x \<in> I}"
    from is_cring and acarr have idealJ: "ideal J R"
      unfolding J_def by (rule helper_max_prime)
    have IsubJ: "I \<subseteq> J"
      using I_l_closed J_def a_Hcarr acarr by blast
    from abI and acarr bcarr have "b \<in> J"
      unfolding J_def by fast
    with bnI have JnI: "J \<noteq> I" by fast
    have "\<one> \<notin> J"
      unfolding J_def by (simp add: acarr anI)
    then have Jncarr: "J \<noteq> carrier R" by fast
    interpret ideal J R by (rule idealJ)
    have "J = I \<or> J = carrier R"
      by (simp add: I_maximal IsubJ a_subset is_ideal)
    with JnI and Jncarr show False by simp
  qed
qed

subsection \<open>Derived Theorems\<close>

text \<open>A non-zero cring that has only the two trivial ideals is a field\<close>
lemma (in cring) trivialideals_fieldI:
  assumes carrnzero: "carrier R \<noteq> {\<zero>}"
    and haveideals: "{I. ideal I R} = {{\<zero>}, carrier R}"
  shows "field R"
proof (intro cring_fieldI equalityI)
  show "Units R \<subseteq> carrier R - {\<zero>}"
    by (metis Diff_empty Units_closed Units_r_inv_ex carrnzero l_null one_zeroD subsetI subset_Diff_insert)
  show "carrier R - {\<zero>} \<subseteq> Units R"
  proof
    fix x
    assume xcarr': "x \<in> carrier R - {\<zero>}"
    then have xcarr: "x \<in> carrier R" and xnZ: "x \<noteq> \<zero>" by auto
    from xcarr have xIdl: "ideal (PIdl x) R"
      by (intro cgenideal_ideal) fast
    have "PIdl x \<noteq> {\<zero>}"
      using xcarr xnZ cgenideal_self by blast
    with haveideals have "PIdl x = carrier R"
      by (blast intro!: xIdl)
    then have "\<one> \<in> PIdl x" by simp
    then have "\<exists>y. \<one> = y \<otimes> x \<and> y \<in> carrier R"
      unfolding cgenideal_def by blast
    then obtain y where ycarr: " y \<in> carrier R" and ylinv: "\<one> = y \<otimes> x"
      by fast
    have "\<exists>y \<in> carrier R. y \<otimes> x = \<one> \<and> x \<otimes> y = \<one>"
      using m_comm xcarr ycarr ylinv by auto
    with xcarr show "x \<in> Units R"
      unfolding Units_def by fast
  qed
qed

lemma (in field) all_ideals: "{I. ideal I R} = {{\<zero>}, carrier R}"
proof (intro equalityI subsetI)
  fix I
  assume a: "I \<in> {I. ideal I R}"
  then interpret ideal I R by simp

  show "I \<in> {{\<zero>}, carrier R}"
  proof (cases "\<exists>a. a \<in> I - {\<zero>}")
    case True
    then obtain a where aI: "a \<in> I" and anZ: "a \<noteq> \<zero>"
      by fast+
    have aUnit: "a \<in> Units R"
      by (simp add: aI anZ field_Units)
    then have a: "a \<otimes> inv a = \<one>" by (rule Units_r_inv)
    from aI and aUnit have "a \<otimes> inv a \<in> I"
      by (simp add: I_r_closed del: Units_r_inv)
    then have oneI: "\<one> \<in> I" by (simp add: a[symmetric])
    have "carrier R \<subseteq> I"
      using oneI one_imp_carrier by auto
    with a_subset have "I = carrier R" by fast
    then show "I \<in> {{\<zero>}, carrier R}" by fast
  next
    case False
    then have IZ: "\<And>a. a \<in> I \<Longrightarrow> a = \<zero>" by simp
    have a: "I \<subseteq> {\<zero>}"
      using False by auto
    have "\<zero> \<in> I" by simp
    with a have "I = {\<zero>}" by fast
    then show "I \<in> {{\<zero>}, carrier R}" by fast
  qed
qed (auto simp: zeroideal oneideal)

\<comment>\<open>"Jacobson Theorem 2.2"\<close>
lemma (in cring) trivialideals_eq_field:
  assumes carrnzero: "carrier R \<noteq> {\<zero>}"
  shows "({I. ideal I R} = {{\<zero>}, carrier R}) = field R"
  by (fast intro!: trivialideals_fieldI[OF carrnzero] field.all_ideals)

text \<open>Like zeroprimeideal for domains\<close>
lemma (in field) zeromaximalideal: "maximalideal {\<zero>} R"
proof (intro maximalidealI zeroideal)
  from one_not_zero have "\<one> \<notin> {\<zero>}" by simp
  with one_closed show "carrier R \<noteq> {\<zero>}" by fast
next
  fix J
  assume Jideal: "ideal J R"
  then have "J \<in> {I. ideal I R}" by fast
  with all_ideals show "J = {\<zero>} \<or> J = carrier R"
    by simp
qed

lemma (in cring) zeromaximalideal_fieldI:
  assumes zeromax: "maximalideal {\<zero>} R"
  shows "field R"
proof (intro trivialideals_fieldI maximalideal.I_notcarr[OF zeromax])
  have "J = carrier R" if Jn0: "J \<noteq> {\<zero>}" and idealJ: "ideal J R" for J
  proof -
    interpret ideal J R by (rule idealJ)
    have "{\<zero>} \<subseteq> J"
      by force
    from zeromax idealJ this a_subset
    have "J = {\<zero>} \<or> J = carrier R"
      by (rule maximalideal.I_maximal)
    with Jn0 show "J = carrier R"
      by simp
  qed
  then show "{I. ideal I R} = {{\<zero>}, carrier R}"
    by (auto simp: zeroideal oneideal)
qed

lemma (in cring) zeromaximalideal_eq_field: "maximalideal {\<zero>} R = field R"
  using field.zeromaximalideal zeromaximalideal_fieldI by blast

end


theory Ex1_1
  imports Main
begin

primrec snoc :: "'a list \<Rightarrow> 'a \<Rightarrow> 'a list" where
  "snoc [] val = [val]"|
  "snoc (x # xs) val = x # snoc xs val"

theorem helper : "snoc (xs @ ys) x = xs @ snoc ys x" by (induct xs , auto)

theorem rev_cons : "rev (x # xs) = snoc (rev xs) x" by (induct xs , auto simp add : helper)

theorem rev_cons_metis : "rev (x # xs) = snoc (rev xs) x"
proof (induction xs)
  case Nil
  then show ?case by simp
next
  case (Cons a xs)
  then show ?case by (metis fold_Cons_rev helper rev.simps(2) rev_conv_fold snoc.simps(1))
qed


theory FMap
  imports Main "~~/src/HOL/Library/DAList"
begin

subsubsection {* The type of finite maps *}

typedef ('a, 'b) fmap  (infixr "f\<rightharpoonup>" 1) = "{x :: 'a \<rightharpoonup> 'b. finite (dom x) }"
  proof show "empty \<in> {x. finite (dom x)}" by simp qed

setup_lifting type_definition_fmap

lift_definition fdom :: "'key f\<rightharpoonup> 'value \<Rightarrow> 'key set" is "dom" .

lift_definition fran :: "'key f\<rightharpoonup> 'value \<Rightarrow> 'value set" is "ran" .

lift_definition lookup :: "'key f\<rightharpoonup> 'value \<Rightarrow> 'key \<Rightarrow> 'value option" is "(\<lambda> x. x)" .

abbreviation the_lookup (infix "f!" 55)
  where "m f! x \<equiv> the (lookup m x)"

lift_definition fempty :: "'key f\<rightharpoonup> 'value" ("f\<emptyset>") is Map.empty by simp

lemma fempty_fdom[simp]: "fdom f\<emptyset> = {}"
  by (transfer, auto)

lemma fdomIff: "(a : fdom m) = (lookup m a \<noteq> None)"
 by (transfer, auto)

lemma lookup_not_fdom: "x \<notin> fdom m \<Longrightarrow> lookup m x = None"
  by (auto iff:fdomIff)

lemma finite_range:
  assumes "finite (dom m)"
  shows "finite (ran m)"
  apply (rule finite_subset[OF _ finite_imageI[OF assms, of "\<lambda> x . the (m x)"]])
  by (auto simp add: ran_def dom_def image_def)

lemma finite_fdom[simp]: "finite (fdom m)"
  by transfer

lemma finite_fran[simp]: "finite (fran m)"
  by (transfer, rule finite_range)

lemma fmap_eqI[intro]:
  assumes "fdom a = fdom b"
  and "\<And> x. x \<in> fdom a \<Longrightarrow> a f! x = b f! x"
  shows "a = b"
using assms
proof(transfer)
  fix a b :: "'a \<rightharpoonup> 'b"
  assume d: "dom a = dom b"
  assume eq: "\<And> x. x \<in> dom a \<Longrightarrow> the (a x) = the (b x)"
  show "a = b"
  proof
    fix x
    show "a x = b x"
    proof(cases "a x")
    case None
      hence "x \<notin> dom a" by (simp add: dom_def)
      hence "x \<notin> dom b" using d by simp
      hence " b x = None"  by (simp add: dom_def)
      thus ?thesis using None by simp
    next
    case (Some y)
      hence d': "x \<in> dom ( a)" by (simp add: dom_def)
      hence "the ( a x) = the ( b x)" using eq by auto
      moreover
      have "x \<in> dom ( b)" using Some d' d by simp
      then obtain y' where " b x = Some y'" by (auto simp add: dom_def)
      ultimately
      show " a x =  b x" using Some by auto
    qed
  qed
qed

subsubsection {* Updates *}

lift_definition
  fmap_upd :: "'key f\<rightharpoonup> 'value \<Rightarrow> 'key \<Rightarrow> 'value \<Rightarrow> 'key f\<rightharpoonup> 'value" ("_'(_ f\<mapsto> _')" [900,900]900)
  is "\<lambda> m x v. m( x \<mapsto> v)"  by simp

lemma fmap_upd_fdom[simp]: "fdom (h (x f\<mapsto> v)) = insert x (fdom h)"
  by (transfer, auto)

lemma the_lookup_fmap_upd[simp]: "lookup (h (x f\<mapsto> v)) x = Some v"
  by (transfer, auto)

lemma the_lookup_fmap_upd_other[simp]: "x' \<noteq> x \<Longrightarrow> lookup (h (x f\<mapsto> v)) x' = lookup h x'"
  by (transfer, auto)

lemma fmap_upd_overwrite[simp]: "f (x f\<mapsto> y) (x f\<mapsto> z) = f (x f\<mapsto> z)"
  by (transfer, auto)

lemma fmap_upd_twist: "a \<noteq> c \<Longrightarrow> (m(a f\<mapsto> b))(c f\<mapsto> d) = (m(c f\<mapsto> d))(a f\<mapsto> b)"
  apply (rule fmap_eqI)
  apply auto[1]
  apply (case_tac "x = a", auto)
  apply (case_tac "x = c", auto)
  done

subsubsection {* Restriction *}

lift_definition fmap_restr :: "'a set \<Rightarrow> 'a f\<rightharpoonup> 'b \<Rightarrow> 'a f\<rightharpoonup> 'b"
  is "\<lambda> S m. (if finite S then (restrict_map m S) else empty)" by auto

lemma lookup_fmap_restr[simp]: "finite S \<Longrightarrow> x \<in> S \<Longrightarrow> lookup (fmap_restr S m) x = lookup m x"
  by (transfer, auto)

lemma fdom_fmap_restr[simp]: "finite S \<Longrightarrow> fdom (fmap_restr S m) = fdom m \<inter> S"
  by (transfer, simp)

lemma fmap_restr_fmap_restr[simp]:
 "finite d1 \<Longrightarrow> finite d2 \<Longrightarrow> fmap_restr d1 (fmap_restr d2 x) = fmap_restr (d1 \<inter> d2) x"
 by (transfer, auto simp add: restrict_map_def)

lemma fmap_restr_fmap_restr_subset:
 "finite d2 \<Longrightarrow> d1 \<subseteq> d2 \<Longrightarrow> fmap_restr d1 (fmap_restr d2 x) = fmap_restr d1 x"
 by (metis Int_absorb2 finite_subset fmap_restr_fmap_restr)

lemma fmap_restr_useless: "finite S \<Longrightarrow> fdom m \<subseteq> S \<Longrightarrow> fmap_restr S m = m"
  by (rule fmap_eqI, auto)

lemma fmap_restr_not_finite:
  "\<not> finite S \<Longrightarrow> fmap_restr S \<rho> = f\<emptyset>"
  by (transfer, simp)

lemma fmap_restr_fmap_upd: "x \<in> S \<Longrightarrow> finite S \<Longrightarrow> fmap_restr S (m1(x f\<mapsto> v)) = (fmap_restr S m1)(x f\<mapsto> v)"
  apply (rule fmap_eqI)
  apply auto[1]
  apply (case_tac "xa = x")
  apply auto
  done

subsubsection {* Deleting *}

lift_definition fmap_delete :: "'a \<Rightarrow> 'a f\<rightharpoonup> 'b \<Rightarrow> 'a f\<rightharpoonup> 'b"
  is "\<lambda> x m. m(x := None)" by auto

lemma fdom_fmap_delete[simp]:
  "fdom (fmap_delete x m) = fdom m - {x}"
  by (transfer, auto)

lemma fmap_delete_fmap_upd[simp]:
  "fmap_delete x (m(x f\<mapsto> v)) = fmap_delete x m"
  by (transfer, simp)

lemma fmap_delete_noop:
  "x \<notin> fdom m \<Longrightarrow> fmap_delete x m = m"
  by (transfer, auto)

lemma fmap_upd_fmap_delete[simp]: "x \<in> fdom \<Gamma> \<Longrightarrow> (fmap_delete x \<Gamma>)(x f\<mapsto> \<Gamma> f! x) = \<Gamma>"
  by (transfer, auto)

lemma fran_fmap_upd[simp]:
  "fran (m(x f\<mapsto> v)) = insert v (fran (fmap_delete x m))"
by (transfer, auto simp add: ran_def)

subsubsection {* Addition (merging) of finite maps *}

lift_definition fmap_add :: "'a f\<rightharpoonup> 'b \<Rightarrow> 'a f\<rightharpoonup> 'b \<Rightarrow> 'a f\<rightharpoonup> 'b" (infixl "f++" 100)
  is "map_add" by auto

lemma fdom_fmap_add[simp]: "fdom (m1 f++ m2) = fdom m1 \<union> fdom m2"
  by (transfer, auto)

lemma lookup_fmap_add1[simp]: "x \<in> fdom m2 \<Longrightarrow> lookup (m1 f++ m2) x = lookup m2 x"
  by (transfer, auto)

lemma lookup_fmap_add2[simp]:  "x \<notin> fdom m2 \<Longrightarrow> lookup (m1 f++ m2) x = lookup m1 x"
  apply transfer
  by (metis map_add_dom_app_simps(3))

lemma fmap_add_overwrite: "fdom m1 \<subseteq> fdom m2 \<Longrightarrow> m1 f++ m2 = m2"
  apply transfer
  apply rule
  apply (case_tac "x \<in> dom m2")
  apply (auto simp add: map_add_dom_app_simps(1))
  done

lemma fmap_add_upd_swap:
  "x \<notin> fdom \<rho>' \<Longrightarrow> \<rho>(x f\<mapsto> z) f++ \<rho>' = (\<rho> f++ \<rho>')(x f\<mapsto> z)"
  apply transfer
  by (metis map_add_upd_left)

lemma fmap_add_upd:
  "\<rho> f++ (\<rho>'(x f\<mapsto> z)) = (\<rho> f++ \<rho>')(x f\<mapsto> z)"
  apply transfer
  by (metis map_add_upd)

lemma fmap_restr_add: "fmap_restr S (m1 f++ m2) = fmap_restr S m1 f++ fmap_restr S m2"
  apply (cases "finite S")
  apply (rule fmap_eqI)
  apply auto[1]
  apply (case_tac "x \<in> fdom m2")
  apply auto
  apply (simp add: fmap_restr_not_finite)
  done

subsubsection {* Conversion from associative lists *}

lift_definition fmap_of :: "('a \<times> 'b) list \<Rightarrow> 'a f\<rightharpoonup> 'b"
  is map_of by (rule finite_dom_map_of)

lemma fmap_of_Nil[simp]: "fmap_of [] = f\<emptyset>"
 by (transfer, simp)

lemma fmap_of_Cons[simp]: "fmap_of (p # l) = (fmap_of l)(fst p f\<mapsto> snd p)"
  by (transfer, simp)

lemma fmap_of_append[simp]: "fmap_of (l1 @ l2) = fmap_of l2 f++ fmap_of l1"
  by (transfer, simp)

lemma lookup_fmap_of[simp]:
  "lookup (fmap_of m) x = map_of m x"
  by (transfer, auto)

lemma fmap_delete_fmap_of[simp]:
  "fmap_delete x (fmap_of m) = fmap_of (AList.delete x m)"
  by (transfer, metis delete_conv')

subsubsection {* Less-than-relation for extending finite maps *}

instantiation fmap :: (type,type) order
begin
  definition "\<rho> \<le> \<rho>' = ((fdom \<rho> \<subseteq> fdom \<rho>') \<and> (\<forall>x \<in> fdom \<rho>. lookup \<rho> x = lookup \<rho>' x))"
  definition "(\<rho>::'a f\<rightharpoonup> 'b) < \<rho>' = (\<rho> \<noteq> \<rho>' \<and> \<rho> \<le> \<rho>')"

  lemma fmap_less_eqI[intro]:
    assumes assm1: "fdom \<rho> \<subseteq> fdom \<rho>'"
      and assm2:  "\<And> x. \<lbrakk> x \<in> fdom \<rho> ; x \<in> fdom \<rho>'  \<rbrakk> \<Longrightarrow> \<rho> f! x = \<rho>' f! x "
     shows "\<rho> \<le> \<rho>'"
   unfolding less_eq_fmap_def
   apply rule
   apply fact
   apply rule+
   apply (frule subsetD[OF `_ \<subseteq> _`])
   apply (frule  assm2)
   apply (auto iff: fdomIff)
   done

  lemma fmap_less_eqD:
    assumes "\<rho> \<le> \<rho>'"
    assumes "x \<in> fdom \<rho>"
    shows "lookup \<rho> x = lookup \<rho>' x"
    using assms
    unfolding less_eq_fmap_def by auto

  lemma fmap_antisym: assumes  "(x:: 'a f\<rightharpoonup> 'b) \<le> y" and "y \<le> x" shows "x = y "
  proof(rule fmap_eqI[rule_format])
      show "fdom x = fdom y" using `x \<le> y` and `y \<le> x` unfolding less_eq_fmap_def by auto

      fix v
      assume "v \<in> fdom x"
      hence "v \<in> fdom y" using `fdom _ = _` by simp

      thus "x f! v = y f! v"
        using `x \<le> y` `v \<in> fdom x` unfolding less_eq_fmap_def by simp
    qed

  lemma fmap_trans: assumes  "(x:: 'a f\<rightharpoonup> 'b) \<le> y" and "y \<le> z" shows "x \<le> z"
  proof
    show "fdom x \<subseteq> fdom z" using `x \<le> y` and `y \<le> z` unfolding less_eq_fmap_def
      by -(rule order_trans [of _ "fdom y"], auto)

    fix v
    assume "v \<in> fdom x" and "v \<in> fdom z"
    hence "v \<in> fdom y" using `x \<le> y`  unfolding less_eq_fmap_def by auto
    hence "lookup y v = lookup x v"
      using `x \<le> y` `v \<in> fdom x` unfolding less_eq_fmap_def by auto
    moreover
    have "lookup y v = lookup z v"
      by (rule fmap_less_eqD[OF `y \<le> z`  `v \<in> fdom y`])
    ultimately
    show "x f! v = z f! v" by auto
  qed

  instance
    apply default
    using fmap_antisym apply (auto simp add: less_fmap_def)[1]
    apply (auto simp add: less_eq_fmap_def)[1]
    using fmap_trans apply assumption
    using fmap_antisym apply assumption
    done
end

lemma fmap_less_fdom:
  "\<rho>1 \<le> \<rho>2 \<Longrightarrow> fdom \<rho>1 \<subseteq> fdom \<rho>2"
  by (metis less_eq_fmap_def)

lemma fmap_less_restrict:
  "\<rho>1 \<le> \<rho>2 \<longleftrightarrow> \<rho>1 = fmap_restr (fdom \<rho>1) \<rho>2"
  unfolding less_eq_fmap_def
  apply transfer
  apply (auto simp add:restrict_map_def split:option.split_asm)
  by (metis option.simps(3))+

lemma fmap_restr_less:
  "fmap_restr S \<rho> \<le> \<rho>"
  unfolding less_eq_fmap_def
  by (transfer, auto)

lemma fmap_upd_less[simp, intro]:
  "\<rho>1 \<le> \<rho>2 \<Longrightarrow> v1 = v2 \<Longrightarrow> \<rho>1(x f\<mapsto> v1) \<le> \<rho>2(x f\<mapsto> v2)"
  apply (rule fmap_less_eqI)
  apply (auto dest: fmap_less_fdom)[1]
  apply (case_tac "xa = x")
  apply (auto dest: fmap_less_eqD)
  done

lemma fmap_update_less[simp, intro]:
  "\<rho>1 \<le> \<rho>1' \<Longrightarrow> \<rho>2 \<le> \<rho>2' \<Longrightarrow>  (fdom \<rho>2' - fdom \<rho>2) \<inter> fdom \<rho>1 = {} \<Longrightarrow> \<rho>1 f++ \<rho>2 \<le> \<rho>1' f++ \<rho>2'"
  apply (rule fmap_less_eqI)
  apply (auto dest: fmap_less_fdom)[1]
  apply (case_tac "x \<in> fdom \<rho>2")
  apply (auto dest: fmap_less_eqD fmap_less_fdom)
  apply (metis fmap_less_eqD fmap_less_fdom lookup_fmap_add1 set_mp)
  by (metis Diff_iff Diff_triv fmap_less_eqD lookup_fmap_add2)

lemma fmap_restr_le:
  assumes "\<rho>1 \<le> \<rho>2"
  assumes "S1 \<subseteq> S2"
  assumes [simp]:"finite S2"
  shows "fmap_restr S1 \<rho>1 \<le> fmap_restr S2 \<rho>2"
proof-
  have [simp]: "finite S1"
    by (rule finite_subset[OF `S1 \<subseteq> S2` `finite S2`])
  show ?thesis
  proof (rule fmap_less_eqI)
    have "fdom \<rho>1 \<subseteq> fdom \<rho>2"
      by (metis assms(1) less_eq_fmap_def)
    thus "fdom (fmap_restr S1 \<rho>1) \<subseteq> fdom (fmap_restr S2 \<rho>2)"
      using `S1 \<subseteq> S2`
      by auto
  next
    fix x
    assume "x \<in> fdom (fmap_restr S1 \<rho>1) "
    hence [simp]:"x \<in> fdom \<rho>1" and [simp]:"x \<in> S1" and [simp]: "x \<in> S2"
      by (auto intro: set_mp[OF `S1 \<subseteq> S2`])
    have "\<rho>1 f! x = \<rho>2 f! x"
      by (metis `x \<in> fdom \<rho>1` assms(1) less_eq_fmap_def)
    thus "(fmap_restr S1 \<rho>1) f! x = (fmap_restr S2 \<rho>2) f! x"
      by simp
  qed
qed

end


section {* Fixed-points and Recursion *}

theory utp_recursion
  imports utp_pred_laws
begin

subsection {* Fixed-point Laws *}

lemma mu_id: "(\<mu> X \<bullet> X) = true"
  by (simp add: antisym gfp_upperbound)

lemma mu_const: "(\<mu> X \<bullet> P) = P"
  by (simp add: gfp_const)

lemma nu_id: "(\<nu> X \<bullet> X) = false"
  by (meson lfp_lowerbound utp_pred_laws.bot.extremum_unique)

lemma nu_const: "(\<nu> X \<bullet> P) = P"
  by (simp add: lfp_const)

lemma mu_refine_intro:
  assumes "(C \<Rightarrow> S) \<sqsubseteq> F(C \<Rightarrow> S)" "`C \<Rightarrow> (\<mu> F \<Leftrightarrow> \<nu> F)`"
  shows "(C \<Rightarrow> S) \<sqsubseteq> \<mu> F"
proof -
  from assms have "(C \<Rightarrow> S) \<sqsubseteq> \<nu> F"
    by (simp add: lfp_lowerbound)
  with assms show ?thesis
    by (pred_auto)
qed

subsection {* Obtaining Unique Fixed-points *}

text {* Obtaining termination proofs via approximation chains. Theorems and proofs adapted
  from Chapter 2, page 63 of the UTP book~\cite{Hoare&98}.  *}

type_synonym 'a chain = "nat \<Rightarrow> 'a upred"

definition chain :: "'a chain \<Rightarrow> bool" where
  "chain Y = ((Y 0 = false) \<and> (\<forall> i. Y (Suc i) \<sqsubseteq> Y i))"

lemma chain0 [simp]: "chain Y \<Longrightarrow> Y 0 = false"
  by (simp add:chain_def)

lemma chainI:
  assumes "Y 0 = false" "\<And> i. Y (Suc i) \<sqsubseteq> Y i"
  shows "chain Y"
  using assms by (auto simp add: chain_def)

lemma chainE:
  assumes "chain Y" "\<And> i. \<lbrakk> Y 0 = false; Y (Suc i) \<sqsubseteq> Y i \<rbrakk> \<Longrightarrow> P"
  shows "P"
  using assms by (simp add: chain_def)

lemma L274:
  assumes "\<forall> n. ((E n \<and>\<^sub>p X) = (E n \<and> Y))"
  shows "(\<Sqinter> (range E) \<and> X) = (\<Sqinter> (range E) \<and> Y)"
  using assms by (pred_auto)

text {* Constructive chains *}

definition constr ::
  "('a upred \<Rightarrow> 'a upred) \<Rightarrow> 'a chain \<Rightarrow> bool" where
"constr F E \<longleftrightarrow> chain E \<and> (\<forall> X n. ((F(X) \<and> E(n + 1)) = (F(X \<and> E(n)) \<and> E (n + 1))))"

text {* This lemma gives a way of showing that there is a unique fixed-point when
        the predicate function can be built using a constructive function F
        over an approximation chain E *}

lemma chain_pred_terminates:
  assumes "constr F E" "mono F"
  shows "(\<Sqinter> (range E) \<and> \<mu> F) = (\<Sqinter> (range E) \<and> \<nu> F)"
proof -
  from assms have "\<forall> n. (E n \<and> \<mu> F) = (E n \<and> \<nu> F)"
  proof (rule_tac allI)
    fix n
    from assms show "(E n \<and> \<mu> F) = (E n \<and> \<nu> F)"
    proof (induct n)
      case 0 thus ?case by (simp add: constr_def)
    next
      case (Suc n)
      note hyp = this
      thus ?case
      proof -
        have "(E (n + 1) \<and> \<mu> F) = (E (n + 1) \<and> F (\<mu> F))"
          using gfp_unfold[OF hyp(3), THEN sym] by (simp add: constr_def)
        also from hyp have "... = (E (n + 1) \<and> F (E n \<and> \<mu> F))"
          by (metis conj_comm constr_def)
        also from hyp have "... = (E (n + 1) \<and> F (E n \<and> \<nu> F))"
          by simp
        also from hyp have "... = (E (n + 1) \<and> \<nu> F)"
          by (metis (no_types, lifting) conj_comm constr_def lfp_unfold)
        ultimately show ?thesis
          by simp
      qed
    qed
  qed
  thus ?thesis
    by (auto intro: L274)
qed

theorem constr_fp_uniq:
  assumes "constr F E" "mono F" "\<Sqinter> (range E) = C"
  shows "(C \<and> \<mu> F) = (C \<and> \<nu> F)"
  using assms(1) assms(2) assms(3) chain_pred_terminates by blast

end


theory Refine_Mono_Prover
imports Main "../Automatic_Refinement/Lib/Refine_Lib"
begin
  ML_file \<open>refine_mono_prover.ML\<close>

  setup Refine_Mono_Prover.setup
  declaration Refine_Mono_Prover.decl_setup

  method_setup refine_mono =
    \<open>Scan.succeed (fn ctxt => SIMPLE_METHOD' (
      Refine_Mono_Prover.untriggered_mono_tac ctxt
    ))\<close>
    "Refinement framework: Monotonicity prover"

  locale mono_setup_loc =
    \<comment> \<open>Locale to set up monotonicity prover for given ordering operator\<close>
    fixes le :: "'a \<Rightarrow> 'a \<Rightarrow> bool"
    assumes refl: "le x x"
  begin
    lemma monoI: "(\<And>f g x. (\<And>x. le (f x) (g x)) \<Longrightarrow> le (B f x) (B g x))
      \<Longrightarrow> monotone (fun_ord le) (fun_ord le) B"
      unfolding monotone_def fun_ord_def by blast

    lemma mono_if: "\<lbrakk>le t t'; le e e'\<rbrakk> \<Longrightarrow> le (If b t e) (If b t' e')" by auto
    lemma mono_let: "(\<And>x. le (f x) (f' x)) \<Longrightarrow> le (Let x f) (Let x f')" by auto

    lemmas mono_thms[refine_mono] = monoI mono_if mono_let refl

    declaration \<open>Refine_Mono_Prover.declare_mono_triggers @{thms monoI}\<close>

  end

  interpretation order_mono_setup: mono_setup_loc "(\<le>) :: 'a::preorder \<Rightarrow> _"
    by standard auto

  declaration \<open>Refine_Mono_Prover.declare_mono_triggers @{thms monoI}\<close>

  lemmas [refine_mono] =
    lfp_mono[OF le_funI, THEN le_funD]
    gfp_mono[OF le_funI, THEN le_funD]

end


(* Copyright 2021 (C) Mihails Milehins *)

section\<open>Further results about ordinal numbers\<close>
theory CZH_Sets_Ordinals
  imports
    CZH_Sets_Nat
    CZH_Sets_IF
    Complex_Main
begin

subsection\<open>Background\<close>

text\<open>
The subsection presents several results about ordinal
numbers. The primary general reference for this section
is \<^cite>\<open>"takeuti_introduction_1971"\<close>.
\<close>

lemmas [intro] = Limit_is_Ord Ord_in_Ord

subsection\<open>Further ordinal arithmetic and inequalities\<close>

lemma Ord_succ_mono:
  assumes "Ord \<beta>" and "\<alpha> \<in>\<^sub>\<circ> \<beta>"
  shows "succ \<alpha> \<in>\<^sub>\<circ> succ \<beta>"
proof-
  from assms have "Ord \<alpha>" by blast
  from assms \<open>Ord \<alpha>\<close> have "\<alpha> < \<beta>" by (auto dest: Ord_mem_iff_lt)
  from assms(1,2) this have "succ \<alpha> < succ \<beta>"
    by (meson assms \<open>Ord \<alpha>\<close> Ord_linear2 Ord_succ leD le_succ_iff)
  with assms(1) \<open>Ord \<alpha>\<close> Ord_mem_iff_lt show "succ \<alpha> \<in>\<^sub>\<circ> succ \<beta>" by blast
qed

lemma Limit_right_Limit_mult:
  \<comment>\<open>Based on Theorem 8.23 in \cite{takeuti_introduction_1971}.\<close>
  assumes "Ord \<alpha>" and "Limit \<beta>" and "0 \<in>\<^sub>\<circ> \<alpha>"
  shows "Limit (\<alpha> * \<beta>)"
proof-
  have \<alpha>\<beta>: "\<alpha> * \<beta> = (\<Union>\<^sub>\<circ>\<xi>\<in>\<^sub>\<circ>\<beta>. \<alpha> * \<xi>)" by (rule mult_Limit[OF assms(2), of \<alpha>])
  from assms(1,2) Ord_mult have "Ord (\<alpha> * \<beta>)" by blast
  then show ?thesis
  proof(cases rule: Ord_cases)
    case (succ \<gamma>)
    from succ(1) have "\<gamma> \<in>\<^sub>\<circ> \<alpha> * \<beta>" unfolding succ(2)[symmetric] by simp
    then obtain \<xi> where "\<xi> \<in>\<^sub>\<circ> \<beta>" and "\<gamma> \<in>\<^sub>\<circ> \<alpha> * \<xi>" unfolding \<alpha>\<beta> by auto
    moreover with assms(2) have "Ord \<xi>" by auto
    ultimately have s\<gamma>_s\<alpha>\<xi>: "succ \<gamma> \<in>\<^sub>\<circ> succ (\<alpha> * \<xi>)"
      using assms(1) Ord_succ_mono by simp
    from assms(2,3) have "succ (\<alpha> * \<xi>) \<subseteq>\<^sub>\<circ> \<alpha> * \<xi> + \<alpha>"
      unfolding succ_eq_add1 by force
    with s\<gamma>_s\<alpha>\<xi> have "succ \<gamma> \<in>\<^sub>\<circ> \<alpha> * succ \<xi>"
      unfolding mult_succ[symmetric] by auto
    moreover have "succ \<xi> \<in>\<^sub>\<circ> \<beta>"
      by (simp add: succ_in_Limit_iff \<open>\<xi> \<in>\<^sub>\<circ> \<beta>\<close> assms(2))
    ultimately have "succ \<gamma> \<in>\<^sub>\<circ> \<alpha> * \<beta>" unfolding \<alpha>\<beta> by force
    with succ(2) show ?thesis by simp
  qed (use assms(2,3) in auto)
qed

lemma Limit_left_Limit_mult:
  assumes "Limit \<alpha>" and "Ord \<beta>" and "0 \<in>\<^sub>\<circ> \<beta>"
  shows "Limit (\<alpha> * \<beta>)"
proof(cases \<open>Limit \<beta>\<close>)
  case False
  then obtain \<beta>' where "Ord \<beta>'" and \<beta>_def: "\<beta> = succ \<beta>'"
    by (metis Ord_cases assms(2,3) eq0_iff)
  have \<alpha>_s\<beta>': "\<alpha> * succ \<beta>' = \<alpha> * \<beta>' + \<alpha>" by (simp add: mult_succ)
  from assms(1) have "Limit (\<alpha> * \<beta>' + \<alpha>)" by (simp add: Limit_is_Ord \<open>Ord \<beta>'\<close>)
  then show "Limit (\<alpha> * \<beta>)" unfolding \<beta>_def \<alpha>_s\<beta>' by simp
qed (use assms in \<open>auto simp: Limit_def dest: Limit_right_Limit_mult\<close>)

lemma zero_if_Limit_eq_Limit_plus_vnat:
  assumes "Limit \<alpha>" and "Limit \<beta>" and "\<alpha> = \<beta> + n" and "n \<in>\<^sub>\<circ> \<omega>"
  shows "n = 0"
proof(rule ccontr)
  assume prems: "n \<noteq> 0"
  from assms(1,2,4) have "Ord \<alpha>" and "Ord \<beta>" and "Ord 0" and "Ord n" by auto
  have "0 \<in>\<^sub>\<circ> n" by (simp add: mem_0_Ord prems assms(4))
  with assms(4) obtain m where n_def: "n = succ m" by (auto elim: omega_prev)
  from assms(1,3) show False by (simp add: n_def plus_V_succ_right)
qed

lemma Ord_vsubset_closed:
  assumes "Ord \<alpha>" and "Ord \<gamma>" and "\<alpha> \<subseteq>\<^sub>\<circ> \<beta>" and "\<beta> \<in>\<^sub>\<circ> \<gamma>"
  shows "\<alpha> \<in>\<^sub>\<circ> \<gamma>"
proof-
  from assms have "Ord \<beta>" by auto
  with assms show ?thesis by (simp add: Ord_mem_iff_lt)
qed

lemma
  \<comment>\<open>Based on Exercise 1, page 53 in \cite{takeuti_introduction_1971}.\<close>
  assumes "Ord \<alpha>" and "Ord \<gamma>" and "\<alpha> + \<beta> \<in>\<^sub>\<circ> \<gamma>"
  shows Ord_plus_Ord_closed_augend: "\<alpha> \<in>\<^sub>\<circ> \<gamma>"
    and Ord_plus_Ord_closed_addend: "\<beta> \<in>\<^sub>\<circ> \<gamma>"
proof-
  from assms have "\<alpha> + \<beta> \<in>\<^sub>\<circ> \<alpha> + \<gamma>" by (meson vsubsetD add_le_left)
  from add_mem_right_cancel[THEN iffD1, OF this] show "\<beta> \<in>\<^sub>\<circ> \<gamma>" .
  from assms have "\<alpha> \<subseteq>\<^sub>\<circ> \<alpha> + \<beta>" by simp
  from Ord_vsubset_closed[OF assms(1,2) this assms(3)] show "\<alpha> \<in>\<^sub>\<circ> \<gamma>" .
qed

lemma Ord_ex1_Limit_plus_in_omega:
  \<comment>\<open>Based on Theorem 8.13 in \cite{takeuti_introduction_1971}.\<close>
  assumes "Ord \<alpha>" and "\<omega> \<subseteq>\<^sub>\<circ> \<alpha>"
  shows "\<exists>!\<beta>. \<exists>!n. n \<in>\<^sub>\<circ> \<omega> \<and> Limit \<beta> \<and> \<alpha> = \<beta> + n"
proof-
  let ?A = \<open>set {\<gamma>. Limit \<gamma> \<and> \<gamma> \<subseteq>\<^sub>\<circ> \<alpha>}\<close>
  have small[simp]: "small {\<gamma>. Limit \<gamma> \<and> \<gamma> \<subseteq>\<^sub>\<circ> \<alpha>}"
  proof-
    from Ord_mem_iff_lt  have "{\<gamma>. Limit \<gamma> \<and> \<gamma> \<subseteq>\<^sub>\<circ> \<alpha>} \<subseteq> elts (succ \<alpha>)"
      by (auto dest: order.not_eq_order_implies_strict intro: assms(1))
    then show "small {\<gamma>. Limit \<gamma> \<and> \<gamma> \<subseteq>\<^sub>\<circ> \<alpha>}" by (meson down)
  qed
  let ?\<beta> = \<open>\<Union>\<^sub>\<circ>?A\<close>
  have "?\<beta> \<subseteq>\<^sub>\<circ> \<alpha>" by auto
  moreover have L_\<beta>: "Limit ?\<beta>"
  proof(subst Limit_def, intro conjI allI impI)
    show "Ord ?\<beta>" by (fastforce intro: Ord_Sup)
    from assms(2) show "0 \<in>\<^sub>\<circ> ?\<beta>" by auto
    fix y assume "y \<in>\<^sub>\<circ> ?\<beta>"
    then obtain \<gamma> where "y \<in>\<^sub>\<circ> \<gamma>" and "\<gamma> \<in>\<^sub>\<circ> ?A" by clarsimp
    then show "succ y \<in>\<^sub>\<circ> ?\<beta>" by (auto simp: succ_in_Limit_iff)
  qed
  ultimately obtain \<gamma> where "Ord \<gamma>" and \<alpha>_def: "\<alpha> = ?\<beta> + \<gamma>"
    by (metis assms(1) le_Ord_diff Limit_is_Ord)
  from L_\<beta> have L_\<beta>\<omega>: "Limit (?\<beta> + \<omega>)" by (blast intro: Limit_add_Limit)
  have "\<gamma> \<subset>\<^sub>\<circ> \<omega>"
  proof(rule ccontr)
    assume "~\<gamma> \<subset>\<^sub>\<circ> \<omega>"
    with \<open>Ord \<gamma>\<close> Ord_linear2 have "\<omega> \<subseteq>\<^sub>\<circ> \<gamma>" by auto
    then obtain \<delta> where \<gamma>_def: "\<gamma> = \<omega> + \<delta>"
      by (blast dest: Ord_odiff_eq intro: \<open>Ord \<gamma>\<close>)
    from \<alpha>_def have "\<alpha> = (?\<beta> + \<omega>) + \<delta>" by (simp add: add.assoc \<gamma>_def)
    then have "?\<beta> + \<omega> \<subseteq>\<^sub>\<circ> \<alpha>" by (metis add_le_cancel_left0)
    with L_\<beta>\<omega> have "?\<beta> + \<omega> \<subseteq>\<^sub>\<circ> ?\<beta>" by auto
    with add_le_cancel_left[of ?\<beta> \<omega> 0, THEN iffD1] show False by simp
  qed
  with \<alpha>_def have "\<gamma> \<in>\<^sub>\<circ> \<omega>" by (auto simp: Ord_mem_iff_lt \<open>Ord \<gamma>\<close>)
  show ?thesis
  proof
    (
      intro ex1I conjI;
      (elim conjE ex1E allE conjE impE | tactic\<open>all_tac\<close>);
      (intro conjI | tactic\<open>all_tac\<close>)
    )
    show "\<gamma> \<in>\<^sub>\<circ> \<omega>" by (rule \<open>\<gamma> \<in>\<^sub>\<circ> \<omega>\<close>)
    show "Limit ?\<beta>" by (rule \<open>Limit ?\<beta>\<close>)
    show "\<alpha> = ?\<beta> + \<gamma>" by (rule \<alpha>_def)
    from \<alpha>_def show "\<alpha> = ?\<beta> + n \<Longrightarrow> n = \<gamma>" for n by auto
    show "n \<in>\<^sub>\<circ> \<omega> \<Longrightarrow> Limit \<beta> \<Longrightarrow> \<alpha> = \<beta> + n \<Longrightarrow> \<beta> = ?\<beta>" for n \<beta>
    proof-
      assume prems: "n \<in>\<^sub>\<circ> \<omega>" "Limit \<beta>" "\<alpha> = \<beta> + n"
      from L_\<beta> prems(2,3) have "\<beta> \<subseteq>\<^sub>\<circ> ?\<beta>" by auto
      then obtain \<eta> where \<beta>_def: "?\<beta> = \<beta> + \<eta>" and "Ord \<eta>"
        by (metis (lifting) L_\<beta> Limit_is_Ord le_Ord_diff prems(2))
      moreover have "\<eta> \<in>\<^sub>\<circ> \<omega>"
      proof-
        from \<alpha>_def \<beta>_def have "\<beta> + \<eta> + \<gamma> = \<beta> + n" by (simp add: prems(3))
        then have "\<eta> + \<gamma> = n" by (simp add: add.assoc)
        with \<open>\<gamma> \<in>\<^sub>\<circ> \<omega>\<close> \<open>n \<in>\<^sub>\<circ> \<omega>\<close> \<open>Ord \<gamma>\<close> show "\<eta> \<in>\<^sub>\<circ> \<omega>"
          by (blast intro: calculation(2) Ord_plus_Ord_closed_augend)
      qed
      ultimately show ?thesis
        using prems(2) L_\<beta> by (force dest: zero_if_Limit_eq_Limit_plus_vnat)
    qed
  qed
qed

lemma not_Limit_if_in_Limit_plus_omega:
  assumes "Limit \<alpha>" and "\<alpha> \<in>\<^sub>\<circ> \<beta>" and "\<beta> \<in>\<^sub>\<circ> \<alpha> + \<omega>"
  shows "~Limit \<beta>"
proof-
  from assms Ord_add have "Ord \<beta>" by blast
  show ?thesis
    using assms(3)
  proof(cases rule: mem_plus_V_E)
    case 1 with mem_not_sym show ?thesis by (auto simp: assms(2,3))
  next
    case (2 z)
    from zero_if_Limit_eq_Limit_plus_vnat[OF _ assms(1) 2(2) 2(1)] 2(2) assms(2)
    show ?thesis
      by force
  qed
qed

lemma Limit_plus_omega_vsubset_Limit:
  assumes "Limit \<alpha>" and "Limit \<beta>" and "\<alpha> \<in>\<^sub>\<circ> \<beta>"
  shows "\<alpha> + \<omega> \<subseteq>\<^sub>\<circ> \<beta>"
proof-
  from assms(1) have L\<alpha>\<omega>: "Limit (\<alpha> + \<omega>)" by (simp add: Limit_is_Ord)
  from not_Limit_if_in_Limit_plus_omega[OF assms(1,3)] assms(2) have
    "\<beta> \<notin>\<^sub>\<circ> \<alpha> + \<omega>"
    by clarsimp
  with assms(2) have "~\<beta> \<subset>\<^sub>\<circ> \<alpha> + \<omega>"
    by (blast intro: L\<alpha>\<omega> dest: Ord_mem_iff_lt Limit_is_Ord)
  then show "\<alpha> + \<omega> \<subseteq>\<^sub>\<circ> \<beta>" by (meson assms L\<alpha>\<omega> Limit_is_Ord Ord_linear2)
qed

lemma Limit_plus_nat_in_Limit:
  assumes "Limit \<alpha>" and "Limit \<beta>" and "\<alpha> \<in>\<^sub>\<circ> \<beta>"
  shows "\<alpha> + a\<^sub>\<nat> \<in>\<^sub>\<circ> \<beta>"
  using assms Limit_plus_omega_vsubset_Limit[OF assms] by auto

lemma omega2_vsubset_Limit:
  assumes "Limit \<alpha>" and "\<omega> \<in>\<^sub>\<circ> \<alpha>"
  shows "\<omega> + \<omega> \<subseteq>\<^sub>\<circ> \<alpha>"
  using assms by (simp add: Limit_plus_omega_vsubset_Limit)

text\<open>\newpage\<close>

end


(*  Title:      HOL/Induct/PropLog.thy
    Author:     Tobias Nipkow
    Copyright   1994  TU Muenchen & University of Cambridge
*)

section {* Meta-theory of propositional logic *}

theory PropLog imports Main begin

text {*
  Datatype definition of propositional logic formulae and inductive
  definition of the propositional tautologies.

  Inductive definition of propositional logic.  Soundness and
  completeness w.r.t.\ truth-tables.

  Prove: If @{text "H |= p"} then @{text "G |= p"} where @{text "G \<in>
  Fin(H)"}
*}

subsection {* The datatype of propositions *}

datatype 'a pl =
  false |
  var 'a ("#_" [1000]) |
  imp "'a pl" "'a pl" (infixr "->" 90)

subsection {* The proof system *}

inductive thms :: "['a pl set, 'a pl] => bool"  (infixl "|-" 50)
  for H :: "'a pl set"
where
  H: "p\<in>H ==> H |- p"
| K: "H |- p->q->p"
| S: "H |- (p->q->r) -> (p->q) -> p->r"
| DN: "H |- ((p->false) -> false) -> p"
| MP: "[| H |- p->q; H |- p |] ==> H |- q"

subsection {* The semantics *}

subsubsection {* Semantics of propositional logic. *}

primrec eval :: "['a set, 'a pl] => bool"  ("_[[_]]" [100,0] 100)
where
  "tt[[false]] = False"
| "tt[[#v]] = (v \<in> tt)"
| eval_imp: "tt[[p->q]] = (tt[[p]] --> tt[[q]])"

text {*
  A finite set of hypotheses from @{text t} and the @{text Var}s in
  @{text p}.
*}

primrec hyps :: "['a pl, 'a set] => 'a pl set"
where
  "hyps false  tt = {}"
| "hyps (#v)   tt = {if v \<in> tt then #v else #v->false}"
| "hyps (p->q) tt = hyps p tt Un hyps q tt"

subsubsection {* Logical consequence *}

text {*
  For every valuation, if all elements of @{text H} are true then so
  is @{text p}.
*}

definition sat :: "['a pl set, 'a pl] => bool"  (infixl "|=" 50)
  where "H |= p = (\<forall>tt. (\<forall>q\<in>H. tt[[q]]) --> tt[[p]])"

subsection {* Proof theory of propositional logic *}

lemma thms_mono: "G<=H ==> thms(G) <= thms(H)"
apply (rule predicate1I)
apply (erule thms.induct)
apply (auto intro: thms.intros)
done

lemma thms_I: "H |- p->p"
  -- {*Called @{text I} for Identity Combinator, not for Introduction. *}
by (best intro: thms.K thms.S thms.MP)

subsubsection {* Weakening, left and right *}

lemma weaken_left: "[| G \<subseteq> H;  G|-p |] ==> H|-p"
  -- {* Order of premises is convenient with @{text THEN} *}
  by (erule thms_mono [THEN predicate1D])

lemma weaken_left_insert: "G |- p \<Longrightarrow> insert a G |- p"
by (rule weaken_left) (rule subset_insertI)

lemma weaken_left_Un1: "G |- p \<Longrightarrow> G \<union> B |- p"
by (rule weaken_left) (rule Un_upper1)

lemma weaken_left_Un2: "G |- p \<Longrightarrow> A \<union> G |- p"
by (rule weaken_left) (rule Un_upper2)

lemma weaken_right: "H |- q ==> H |- p->q"
by (fast intro: thms.K thms.MP)

subsubsection {* The deduction theorem *}

theorem deduction: "insert p H |- q  ==>  H |- p->q"
apply (induct set: thms)
apply (fast intro: thms_I thms.H thms.K thms.S thms.DN
                   thms.S [THEN thms.MP, THEN thms.MP] weaken_right)+
done

subsubsection {* The cut rule *}

lemma cut: "insert p H |- q \<Longrightarrow> H |- p \<Longrightarrow> H |- q"
by (rule thms.MP) (rule deduction)

lemma thms_falseE: "H |- false \<Longrightarrow> H |- q"
by (rule thms.MP, rule thms.DN) (rule weaken_right)

lemma thms_notE: "H |- p -> false \<Longrightarrow> H |- p \<Longrightarrow> H |- q"
by (rule thms_falseE) (rule thms.MP)

subsubsection {* Soundness of the rules wrt truth-table semantics *}

theorem soundness: "H |- p ==> H |= p"
by (induct set: thms) (auto simp: sat_def)

subsection {* Completeness *}

subsubsection {* Towards the completeness proof *}

lemma false_imp: "H |- p->false ==> H |- p->q"
apply (rule deduction)
apply (metis H insert_iff weaken_left_insert thms_notE)
done

lemma imp_false:
    "[| H |- p;  H |- q->false |] ==> H |- (p->q)->false"
apply (rule deduction)
apply (metis H MP insert_iff weaken_left_insert)
done

lemma hyps_thms_if: "hyps p tt |- (if tt[[p]] then p else p->false)"
  -- {* Typical example of strengthening the induction statement. *}
apply simp
apply (induct p)
apply (simp_all add: thms_I thms.H)
apply (blast intro: weaken_left_Un1 weaken_left_Un2 weaken_right
                    imp_false false_imp)
done

lemma sat_thms_p: "{} |= p ==> hyps p tt |- p"
  -- {* Key lemma for completeness; yields a set of assumptions
        satisfying @{text p} *}
unfolding sat_def
by (metis (full_types) empty_iff hyps_thms_if)

text {*
  For proving certain theorems in our new propositional logic.
*}

declare deduction [intro!]
declare thms.H [THEN thms.MP, intro]

text {*
  The excluded middle in the form of an elimination rule.
*}

lemma thms_excluded_middle: "H |- (p->q) -> ((p->false)->q) -> q"
apply (rule deduction [THEN deduction])
apply (rule thms.DN [THEN thms.MP], best intro: H)
done

lemma thms_excluded_middle_rule:
    "[| insert p H |- q;  insert (p->false) H |- q |] ==> H |- q"
  -- {* Hard to prove directly because it requires cuts *}
by (rule thms_excluded_middle [THEN thms.MP, THEN thms.MP], auto)

subsection{* Completeness -- lemmas for reducing the set of assumptions*}

text {*
  For the case @{prop "hyps p t - insert #v Y |- p"} we also have @{prop
  "hyps p t - {#v} \<subseteq> hyps p (t-{v})"}.
*}

lemma hyps_Diff: "hyps p (t-{v}) <= insert (#v->false) ((hyps p t)-{#v})"
by (induct p) auto

text {*
  For the case @{prop "hyps p t - insert (#v -> Fls) Y |- p"} we also have
  @{prop "hyps p t-{#v->Fls} \<subseteq> hyps p (insert v t)"}.
*}

lemma hyps_insert: "hyps p (insert v t) <= insert (#v) (hyps p t-{#v->false})"
by (induct p) auto

text {* Two lemmas for use with @{text weaken_left} *}

lemma insert_Diff_same: "B-C <= insert a (B-insert a C)"
by fast

lemma insert_Diff_subset2: "insert a (B-{c}) - D <= insert a (B-insert c D)"
by fast

text {*
  The set @{term "hyps p t"} is finite, and elements have the form
  @{term "#v"} or @{term "#v->Fls"}.
*}

lemma hyps_finite: "finite(hyps p t)"
by (induct p) auto

lemma hyps_subset: "hyps p t <= (UN v. {#v, #v->false})"
by (induct p) auto

lemma Diff_weaken_left: "A \<subseteq> C \<Longrightarrow> A - B |- p \<Longrightarrow> C - B |- p"
  by (rule Diff_mono [OF _ subset_refl, THEN weaken_left])

subsubsection {* Completeness theorem *}

text {*
  Induction on the finite set of assumptions @{term "hyps p t0"}.  We
  may repeatedly subtract assumptions until none are left!
*}

lemma completeness_0_lemma:
    "{} |= p ==> \<forall>t. hyps p t - hyps p t0 |- p"
apply (rule hyps_subset [THEN hyps_finite [THEN finite_subset_induct]])
 apply (simp add: sat_thms_p, safe)
 txt{*Case @{text"hyps p t-insert(#v,Y) |- p"} *}
 apply (iprover intro: thms_excluded_middle_rule
                     insert_Diff_same [THEN weaken_left]
                     insert_Diff_subset2 [THEN weaken_left]
                     hyps_Diff [THEN Diff_weaken_left])
txt{*Case @{text"hyps p t-insert(#v -> false,Y) |- p"} *}
 apply (iprover intro: thms_excluded_middle_rule
                     insert_Diff_same [THEN weaken_left]
                     insert_Diff_subset2 [THEN weaken_left]
                     hyps_insert [THEN Diff_weaken_left])
done

text{*The base case for completeness*}
lemma completeness_0:  "{} |= p ==> {} |- p"
  by (metis Diff_cancel completeness_0_lemma)

text{*A semantic analogue of the Deduction Theorem*}
lemma sat_imp: "insert p H |= q ==> H |= p->q"
by (auto simp: sat_def)

theorem completeness: "finite H ==> H |= p ==> H |- p"
apply (induct arbitrary: p rule: finite_induct)
 apply (blast intro: completeness_0)
apply (iprover intro: sat_imp thms.H insertI1 weaken_left_insert [THEN thms.MP])
done

theorem syntax_iff_semantics: "finite H ==> (H |- p) = (H |= p)"
by (blast intro: soundness completeness)

end