theory Demo05 imports Main begin section \Function Definitions Revisited\ subsection \Overlapping Patterns\ fun drop_last :: "'a list \ 'a list" where "drop_last (x # y # ys) = x # drop_last (y # ys)" | "drop_last xs = []" text \Simplification rules use disjoint patterns\ thm drop_last.simps subsection \Underspecification\ fun head1 where "head1 (x # xs) = x" (* observe missing pattern warning and "undefined" *) thm head1.simps (* does not cover undefined-case *) fun head2 where "head2 xs = (case xs of x # _ \ x)" thm head2.simps (* undefined case is not mentioned *) ML \ (* observe the internal usage of @{const undefined} in the case-expression *) Thm.prop_of @{thm head2.simps} \ (* even if we specify undefined cases explicitly, these are not shown *) term "case xs of [] \ undefined | x # _ \ x" subsection \Computation Induction\ text \Computation induction rule uses exactly the three cases\ thm drop_last.induct lemma "length (drop_last xs) = length xs - 1" proof (induction xs rule: drop_last.induct) text \Get three subgoals, for each of the cases. All are automatic, the cases are numbered.\ case (1 x y ys) (* suffixes .IH and .prems do not exist when using computation induction, e.g., fact 1.IH is not available *) thus ?case by auto qed auto text \No way to cheat! All cases have to be covered, even if these have not been specified: induction rule covers empty list.\ thm head1.simps thm head1.induct section \Manual Termination Proofs\ function gen_list :: "nat \ nat \ nat list" where "gen_list n m = (if n \ m then n # gen_list (Suc n) m else [])" by pat_completeness auto text \Evaluation is not yet possible, since termination has not been proven! Similarly, there are no simp rules and there is no computation induction rule\ value "gen_list 3 5" thm gen_list.simps thm gen_list.induct termination proof oops termination apply (relation "measure (\ (n,m). Suc m - n)") apply simp apply (simp only: in_measure, unfold split) oops termination by (relation "measure (\ (n,m). Suc m - n)") auto print_theorems (* only after proving termination we get simp-rules and induction-rule *) value "gen_list 3 10" lemma "gen_list n m = gen_list n (Suc m)" (* \apply simp\ loops, since lhs of gen_list does always match *) oops declare gen_list.simps[simp del] lemma "length (gen_list n m) = Suc m - n" proof (induction n m rule: gen_list.induct) case (1 n m) text \locally enable simplification for specific arguments with @{attribute of}\ note [simp] = gen_list.simps[of n m] from 1 show ?case by auto qed section \Attributes\ text \Here it is perfectly fine to just state the witness\ lemma "\ x :: nat. x mod 7 = 0 \ x > 42 \ x * x < 2500" by (rule exI[of _ 49]) auto text \In the Isar proof one would even have to add type-constraints\ lemma "\ x :: nat. x mod 7 = 0 \ x > 42 \ x * x < 2500" proof show "(49 :: nat) mod 7 = 0 \ (49 :: nat) > 42 \ (49 :: nat) * 49 < 2500" by auto qed text \Less type-constraints via let\ lemma "\ x :: nat. x mod 7 = 0 \ x > 42 \ x * x < 2500" proof let ?x = "49 :: nat" show "?x mod 7 = 0 \ ?x > 42 \ ?x * ?x < 2500" by auto qed text \Preview: use pattern matching via \(is "term")\ after proposition\ lemma "\ x :: nat. x mod 7 = 0 \ x > 42 \ x * x < 2500" (is "\ x. ?P x") proof term ?P (* abbreviation ?P is created from pattern matching above *) show "?P 49" by auto qed text \In the following example the Isar style is much more readable than using complicated attributes\ lemma "\ x :: nat. (\ y. y * y = x) \ x > 42" apply (rule exI[of _ 49, OF conjI[OF exI[of _ 7]]]) by auto thm exI[of _ 49, OF conjI[OF exI[of _ 7]]] lemma "\ x :: nat. (\ y. y * y = x) \ x > 42" proof (intro exI conjI) (* intro applies introduction rules exhaustively *) show "7 * 7 = (49 :: nat)" by simp qed auto lemma "\ x :: nat. (\ y. y * y = x) \ x > 42" proof (intro exI[of _ 7] exI[of _ 49] conjI) (* this does not work out, since always the first instantiated exI-theorem is applied *) show "7 * 7 = (7 :: nat)" oops section \Quicksort\ fun split :: "'a :: linorder \ 'a list \ 'a list \ 'a list" where "split a [] = ([], [])" | "split a (x # xs) = (case split a xs of (low, high) \ if x \ a then (x # low, high) else (low, x # high))" text \Advantage: reuse existing library functions such as @{const filter} for which a lot of lemmas have been proven.\ lemma split[termination_simp]: "split a xs = (filter (\ x. x \ a) xs, filter (\ x. \ x \ a) xs)" by (induction xs) auto fun qsort :: "'a :: linorder list \ 'a list" where "qsort [] = []" | "qsort (x # xs) = (case split x xs of (low, high) \ qsort low @ [x] @ qsort high)" text \Auxiliary property that is required in next proof.\ lemma set_qsort[simp]: "set (qsort xs) = set xs" proof (induction xs rule: qsort.induct) case (2 x xs) then show ?case by (auto simp: split) qed auto lemma sorted_qsort: "sorted (qsort xs)" text \use induction rule coming from qsort-algorithm\ proof (induction xs rule: qsort.induct) case (2 x xs) text \avoid writing term @{term "split x xs"} twice by using pattern matching via \(is "term")\\ obtain low high where s: "split x xs = (low, high)" (is "?e = _") by (cases ?e) auto thm 2 text \pipe symmetric version of s into IH @{thm 2} via @{attribute OF} and store result as fact IH\ note IH = 2[OF s[symmetric]] have "?case = (sorted (qsort low @ [x] @ qsort high))" using s by auto find_theorems "sorted (_ @ _)" also have "\ = ((\ y \ set high. x \ y) \ (\y\set (qsort low). y \ x \ (\ z \ set high. y \ z)))" unfolding sorted_append using IH by auto also have \ using s by (auto simp: split) text \In the step above, we profit from several properties that have been proven for @{const filter}, so that @{method auto} succeeds. These become available since adding @{thm split} switches from @{const split} to @{const filter}.\ finally show ?case . qed auto end