theory Demo02
  imports 
    Main
begin

section \<open>Eigenvariable Condition\<close>

lemma "\<forall> y. y = x"
proof (rule allI)
  fix y
  show "y = x" 
    oops

lemma "\<forall> y. y = x"
proof (rule allI)
  fix z
  show "z = x" (* we can choose a different name than y *)
    oops

text \<open>The upcoming examples just show that it is not possible to cheat.
  They are definitely not good style!\<close>

lemma "\<forall> y. y = x"
proof (rule allI)
  fix x
  show "x = x" (* it is not allowed to choose x *)
    oops

lemma "\<forall> y. y = x"
proof (rule allI)
  let ?blue_x = x (* let can be used to abbreviate terms within proofs *)
  fix x 
  show "x = ?blue_x" (* orange x \<noteq> blue x *)
    using refl[of x] refl[of ?blue_x]
    oops

lemma "\<forall> x y. x = y"
proof (rule allI)
  fix x :: 'a
  let ?orange_x = x
  show "\<forall>y. x = y" 
  proof (rule allI)
    fix x  
    show "?orange_x = x" (* automatic renaming *)
    oops



section \<open>The rule method (in introduction mode)\<close>

lemma "x < 5 \<Longrightarrow> x < 3 \<and> x < 2" 
proof (rule conjI)
  oops

lemma "\<exists> y. 5 < y"  
proof (rule exI)
  oops




section \<open>Equality of Terms\<close>

thm refl (* reflexivity of equality: t = t *)

text \<open>\<alpha>-conversion is implicitly done\<close>

lemma alpha: "(\<forall> x. P x) = (\<forall> y. P y)" by (rule refl)

text \<open>\<beta>-reduction "(\<lambda> x. t) s \<longrightarrow>\<beta>  t [ x / s]" is implicitly done\<close>
lemma beta: "(\<lambda> x. P (x + x)) y = P (y + y)" by (rule refl)

text \<open>\<eta>-expansion "f = (\<lambda> x. f x)" if f has function-type 
  is implicitly invoked\<close>
lemma eta: "(\<lambda> x. f x) = f" by (rule refl)


section \<open>Drinker's paradox\<close>

lemma drinkers_paradox_v1: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
proof -
  have "\<not> (\<exists> x. \<not> drinks x) \<or> (\<exists> x. \<not> drinks x)" 
    by (rule excluded_middle)
  from this show "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)"
  proof (rule disjE) 
    {
      assume "\<exists> x. \<not> drinks x" 
      from this show ?thesis 
      proof (rule exE)
        fix y 
        assume ny: "\<not> drinks y"  
        show ?thesis
        proof (rule exI)
          show "drinks y \<longrightarrow> (\<forall>x. drinks x)" 
          proof (rule impI)
            assume "drinks y" 
            from ny this 
            show "\<forall>x. drinks x" by (rule notE)
          qed
        qed
      qed
    }
    {
      assume "\<not> (\<exists> x. \<not> drinks x)" 
      show ?thesis 
      proof (intro exI impI allI) 
        fix x
        show "drinks x" 
        proof (rule ccontr)
          assume "\<not> drinks x" 
          from this have "\<exists> x. \<not> drinks x" by (rule exI)
          from \<open>\<not> (\<exists> x. \<not> drinks x)\<close> this show False by (rule notE)
        qed
      qed
    }
  qed
qed

lemma drinkers_paradox_v1_with_comments: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
proof -
  have "\<not> (\<exists> x. \<not> drinks x) \<or> (\<exists> x. \<not> drinks x)" 
    by (rule excluded_middle)
  from this show "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)"
  proof (rule disjE) (* rule can also apply elimination rules *)
    thm disjE (* ?P \<or> ?Q \<Longrightarrow> (?P \<Longrightarrow> ?R) \<Longrightarrow> (?Q \<Longrightarrow> ?R) \<Longrightarrow> ?R *)
    (* at this point we get two proof obligations,
       since they use different assumptions we 
       restrict the scope of the assumptions via raw proof blocks *)
    term ?thesis
    {
      assume "\<exists> x. \<not> drinks x" 
      from this show ?thesis (* don't copy statement, but use ?thesis *)
      proof (rule exE)
        fix y (* we freely choose a name *)
        assume ny: "\<not> drinks y"  (* we label this assumption *)
        show ?thesis
        proof (rule exI)
          (* here we get a schematic variable ?p in the proof obligation,
           this means that we can choose the witness. 
           We do this by just typing the corresponding term,
           i.e., ?p = y in the upcoming proof *)
          show "drinks y \<longrightarrow> (\<forall>x. drinks x)" 
          proof (rule impI)
            assume "drinks y" 
            from ny this 
            show "\<forall>x. drinks x" by (rule notE)
          qed
        qed
      qed
    }
    (* here the second part of the exE-rule starts *)
    {
      assume "\<not> (\<exists> x. \<not> drinks x)" 
      show ?thesis 
      proof (intro exI impI allI) (* intro applies several introd. rules *)
        fix x
          (* we just ignore the assumption "drinks ?p" *)
        show "drinks x" 
        proof (rule ccontr)
          assume "\<not> drinks x" 
          from this have "\<exists> x. \<not> drinks x" by (rule exI)
          (* in the following line we refer to the assumption literally *)
          from \<open>\<not> (\<exists> x. \<not> drinks x)\<close> this show False by (rule notE)
        qed
      qed
    }
  qed
qed

text \<open>After finalizing the proof, the free variable @{term drinks} becomes
  schematic, so it is now universally quantified.
  We can easily instantiate schematic variables in theorems via of:\<close>

thm drinkers_paradox_v1[of is_running]
thm drinkers_paradox_v1[of even]
thm drinkers_paradox_v1[of "\<lambda> x. x \<le> 5"]

text \<open>In the proof above no automation was used, but just basic
  proof rules have been applied. For this you need to know the 
  names of the basic proof rules such as the following ones:\<close>

thm excluded_middle ccontr

text \<open>elimination rules\<close>
thm disjE conjE exE allE impE notE

text \<open>introduction rules\<close>
thm disjI1 disjI2 conjI exI allI impI notI

text \<open>In the following proof we build raw proof blocks that
  do not correspond to the goal statement, but just state 
  intermediate facts, and we use automation via "auto" instead of 
  just applying all basic rules.\<close>

lemma drinkers_paradox_v2: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
proof -
  {
    assume "\<exists> x. \<not> drinks x"  
    from this obtain x where nex: "\<not> drinks x" by auto
    have ?thesis
    proof 
      from nex show "drinks x \<longrightarrow> (\<forall>y. drinks y)" 
        by auto
    qed
  } 
  from this have part1: "(\<exists> x. \<not> drinks x) \<Longrightarrow> ?thesis" by auto
  {
    assume "\<not> (\<exists> x. \<not> drinks x)" 
    from this have "\<forall> x. drinks x" by auto
    from this have ?thesis by auto
  }
  from this have part2: "\<not> (\<exists> x. \<not> drinks x) \<Longrightarrow> ?thesis" by auto
  from part1 part2 show ?thesis by auto
qed

lemma drinkers_paradox_v2_with_comments: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
proof -
  {
    (* we just assume that we treat one of the cases *)
    assume "\<exists> x. \<not> drinks x"  
    (* get the existential witness *)
    from this obtain x where nex: "\<not> drinks x" by auto
    have ?thesis
    (* we cannot write "show ?thesis" in the previous line,
       since we assumed the existential statement. 
       "show" is only permitted if this would finish a proof obligation by 
       the surrounding proof-qed block *)
    proof (* standard proof method is exI, since goal is of shape \<exists> x. P x *)
      (* in the following line we choose the existential witness: ?p = x *)
      from nex show "drinks x \<longrightarrow> (\<forall>y. drinks y)" 
        by auto
    qed
  } 
  from this have part1: "(\<exists> x. \<not> drinks x) \<Longrightarrow> ?thesis" by auto
  (* now we have solved one part of the proof *) 
  {
    assume "\<not> (\<exists> x. \<not> drinks x)" 
    from this have "\<forall> x. drinks x" by auto
    from this have ?thesis by auto
  }
  from this have part2: "\<not> (\<exists> x. \<not> drinks x) \<Longrightarrow> ?thesis" by auto

  (* finally prove the lemma from both parts *)
  from part1 part2 show ?thesis by auto
  (* here the automation will trigger the case-analysis *) 
qed

text \<open>Actually the drinker's paradox is so simple, that automation
  can do it on its own.\<close>

lemma drinkers_paradox_v3: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
  by auto

end 