theory LiveDemo03
  imports 
    Main
begin

section \<open>Exercise Discussion\<close>

lemma bool_diffE: "b = (\<not> b) \<Longrightarrow> P" 
proof -
  assume id: "b = (\<not> b)" 
  have "\<not> b \<or> b" ..
  from this have False
  proof
    {
      assume b
      thm subst
      from id this have "\<not> b" by (rule subst)
      from this \<open>b\<close> show False ..
    }
    {
      assume "\<not> b" 
      from id this have b ..
      from \<open>\<not> b\<close> this show False ..
    }
  qed
  from this show P ..
qed


section \<open>Finding Theorems and Constants\<close>

find_theorems                            (* by far too many results *)
find_theorems "_ * (_ + _) = _"          (* find distributive law *)
find_theorems "?x * ?x = _"              (* use same value *)
find_theorems "_ \<longrightarrow> _"                  (* too many results *)
find_theorems "_ \<longrightarrow> _" name: HOL        (* restrict by name *)
find_theorems (100) "_ \<longrightarrow> _" name: HOL  (* show up to 100 results *)
find_theorems "_ \<longrightarrow> _" name: HOL -"_ \<or> _" -"_ \<and> _" -All -Ex  (* restrict by exclusion *)

find_consts "?'a \<Rightarrow> ?'a \<Rightarrow> _ list" 

term "upt 0 100" 
value "[0..100]" 

section \<open>Drinker's Paradox -- Tuning Proofs\<close>

text \<open>previous version copied from Demo02\<close>

lemma drinkers_paradox_v2: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
proof -
  {
    assume "\<exists> x. \<not> drinks x"  
    then 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
  } 
  moreover
  {
    assume "\<not> (\<exists> x. \<not> drinks x)" 
    hence "\<forall> x. drinks x" by auto
    hence ?thesis by auto
  }
  ultimately show ?thesis by auto
qed

text \<open>Usage of case-analysis on Booleans with cases True/False separated by "next".\<close>


lemma drinkers_paradox_v3: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
proof (cases "\<exists> x. \<not> drinks x")
  case True
  then obtain x where "\<not> drinks x" by auto
  thus ?thesis by auto
next
  case False
  then show ?thesis by auto
qed


text \<open>Omit trivial cases via final method\<close>

lemma drinkers_paradox_v5: "\<exists> p. drinks p \<longrightarrow> (\<forall> x. drinks x)" 
proof (cases "\<exists> x. \<not> drinks x")
  case True
  then obtain x where "\<not> drinks x" by auto 
  thus ?thesis by auto                    
qed auto                                  (* solve trivial cases by auto after qed *)




section \<open>Intro and Elim: apply several introduction / elimination rules\<close>

lemma "A \<and> (\<exists> x :: nat. B x \<and> (C \<or> D x))" 
proof (intro conjI exI)
  \<comment> \<open>three subgoals: A, B ?x, C \<or> D ?x\<close>
  show A sorry
  show "B 5" sorry        (* here we choose witness 5 *) 
  show "C \<or> D 5" sorry    (* no choice of witness anymore *)
qed

lemma True  (* True is used as dummy property *) 
proof -
  fix A B C D
  assume "\<exists> x :: nat. A \<and> (B x \<or> C x)" 
  hence D
  proof (elim exE conjE disjE) (* apply multiple elimination rules *)
    (* inspect proof goals at this point *)
    oops 



section \<open>Case-Analysis and Induction\<close>

text \<open>A copy of the list datatype to illustrate print-theorems\<close>
datatype 'a ls = Empty | Con 'a "'a ls" 
print_theorems
thm ls.induct
thm ls.simps

text \<open>This datatype definition proves a lot of theorems for you, as
  can be seen by print_theorems that is used directly after some definition.\<close>

thm list.exhaust (* case analysis on lists *)
thm list.induct  (* induction on lists *)
thm list.simps   (* simplification rules for list-constructors *)

fun app :: "'a list \<Rightarrow> 'a list \<Rightarrow> 'a list" where 
  "app [] ys = ys" 
| "app (x # xs) ys = x # (app xs ys)" 

print_theorems

fun reverse :: "'a list \<Rightarrow> 'a list" where
  "reverse [] = []"  
| "reverse (x # xs) = app (reverse xs) ([x])"

text \<open>Auxiliary lemmas obtained while trying to prove 
  @{prop "reverse (reverse xs) = xs"}\<close>

lemma app_Nil[simp]: "app xs [] = xs" 
  by (induction xs) auto

lemma app_assoc: "app (app xs ys) zs = app xs (app ys zs)" 
  by (induction xs) auto

lemma rev_app: "reverse (app xs ys) = app (reverse ys) (reverse xs)" 
proof (induction xs)
  case (Cons x xs)
  then show ?case by (auto simp: app_assoc)
qed auto

lemma rev_rev[simp]: "reverse (reverse xs) = xs"
proof (induction xs)
  case (Cons x xs)
  from Cons show "reverse (reverse (x # xs)) = x # xs" by (auto simp: rev_app)
qed auto



text \<open>What follows is the final proof of the fact that reversing a list twice
  results in the input list. The development process of this
  proof is much more interesting than the final proof and will be given via a live
  demo in the lecture.\<close>

text \<open>A proof by case-analysis\<close>

fun tail :: "'a list \<Rightarrow> 'a list" where
  "tail [] = []"
| "tail (_ # xs) = xs" 

lemma len_tail: "length (tail xs) = length xs - 1" 
proof (cases xs)
  case Nil
  then show ?thesis by auto
next
  case (Cons x ys)
  then show ?thesis by auto
qed

text \<open>An induction proof involving case-analysis\<close>

fun drop_last :: "'a list \<Rightarrow> 'a list" where
  "drop_last (x # y # ys) = x # drop_last (y # ys)" 
| "drop_last xs = []"

thm drop_last.simps (* observe how the second defining equation is handled *)

lemma len_drop: "length (drop_last xs) = length xs - 1" 
proof (induction xs)
  case (Cons x xs)
  text \<open>case analysis on xs required to trigger simp-rules of @{const drop_last}.\<close>
  show ?case 
  proof (cases xs)
    case xs: (Cons y ys)
    text \<open>use different label so that inner Cons (xs = y # ys) does not hide outer Cons (IH)\<close>    
    with Cons show ?thesis by auto
  qed auto
qed auto

end 
