theory week12B_demo imports "~~/src/HOL/Hoare/Hoare_Logic" begin -- ------------------------------------------------- section "recursive functions" -- ------------------------------------------------- text {* see http://isabelle.in.tum.de/dist/Isabelle2012/doc/functions.pdf *} text {* Question: What is needed for a recursive function to be defined in Isabelle? Why?) Question: What are the 3 ways of defining recursive functions? Question: When should each one be used? Give examples (eg app, fib, sum)*} text {* Answer: We need to prove termination for all recursive functions we define. This is because all Isabelle function need to be total. (and this is to avoid inconsistencies, eg if f(n) = f(n) + 1 we could prove 0 = 1 by subtracting f(n) on both sides) Answer: primrec, fun and function Answer: = WAY 1: primrec = > to be used when one parameter is a datatype and the recursion is "primitive" (each recursive call peels off a datatype constructor from one of the arguments; thus recursion always terminates, i.e. the function is total) Namely, each equation should be of the form: f x1 ... xm (C y1 ...yk) z1 ...zn = rhs where - C is one of the datatype constructor - all recursive occurrences of f in rhs are of the form f ... yi ... for some i - at most one reduction rule for each constructor can be given Example: *} primrec app where "app [] ys = ys " | "app (x#xs) ys = x#(app xs ys) " text {* = WAY 2: fun = > to be used for more complicated pattern matching, but "obvious" termination Example: *} fun fib :: "nat \ nat" where "fib 0 = 1" | "fib (Suc 0) = 1" | "fib (Suc (Suc n)) = fib n + fib (Suc n)" fun sep:: "'a \ 'a list \ 'a list" where "sep a (x # y # zs) = x # a # sep a (y # zs)" | "sep a xs = xs" text {* Note that equations are read sequentially: if patterns overlap, the order of the equations is taken into account. In sep, the second equation is only meant for the cases where the first one does not match. *} text {* = WAY 3: function = > any set of equations BUT need to prove that (a) equations are disjoint and complete > use "pat_completeness" method > may want to use (sequential) attribute if overlapping patterns (b) the function terminates (separate proof. If not there, partial rules only) > use "relation r" method > may want to use the "measure f" function that turns a function in natural numbers in a relation apply (relation "measure ..") relation takes a relation of type ('a × 'a) set, where 'a is the argument type of the function (if the function has 2 arguments of type say 'b and 'c resp. then we use tuples: 'a would be ('b x 'c)). measure :: ('a \ nat) \ ('a × 'a) set constructs a wellfounded relation from a mapping into the natural numbers > we must prove that (a) the relation we supplied is wellfounded, and (b) that the arguments of recursive calls indeed decrease with respect to the relation Note: fun f:: \ where is equivalent to function (sequential) f :: \ where by pat_completeness auto termination by lexicographic_order Example: Write a function quicksort:: "nat list \ nat list" sorting a list using the standard quick sort algorithm. Hint: use the function filter:: ('a \ bool) \ 'a list \ 'a list and less_Suc_eq_le*} fun quicksort where "quicksort [] = []" |"quicksort (x#xs) = (quicksort (filter (\y. y\x) xs)) @[x]@ (quicksort (filter (\y. y>x) xs))" text {* actually fun worked here! Let's try with function anyway *} function (sequential) quicksort2 :: "nat list \ nat list" where "quicksort2 [] = []" | "quicksort2 (x#xs) = quicksort2 [y \ xs. y \ x] @ [x] @ quicksort2 [y \ xs. x < y]" -- "need to prove distinctness and completeness of patterns" apply pat_completeness apply simp apply simp apply simp done -- "get only partial simp and induction rules" thm quicksort.psimps thm quicksort.pinduct termination quicksort2 apply (relation "measure length") thm less_Suc_eq_le apply (auto simp: less_Suc_eq_le) done -- "now have full induction and simp available" print_theorems text {* Other example: write a function "sum i N" which sums up natural numbers up to N, using a counter i: sum 0 N = 0+1+2+...+N *} function sum where "sum i N = (if i(i,N). N-i)") apply simp apply simp done -- ------------------------------------------------- section "functional correctness" -- ------------------------------------------------- text {* Goal: prove correctness of following program VARS k m d t {(*TODO*)} k := K; m := M; WHILE m \ 0 INV { (*TODO*)} DO t := m; m := k mod m; k := t OD; d := k { d= gcd(K,M) }" Question: what is this program computing? Question: write the standard definition in Isabelle of a postcondition for this program (i.e. functional version of this program) Question: write a precondition and an invariant Question: find another definition of the postcondition that would make the reasoning of the invariant easier, and prove its equivalent to the standard one. Question: prove the correctness of the program *} text {* standard definition of gcd *} definition gcd :: "nat \ nat \ nat" where "gcd k m \ THE d. d dvd k \ d dvd m \ (\ d'. d' dvd k \ d' dvd m \ d' \ d)" text {* other definition of gcd, usingsame algorithm than our program *} fun gcd2 :: "nat \ nat \ nat" where "gcd2 k 0 = k" | "gcd2 k m = gcd2 m (k mod m)" text {* gcd2 is a common divisor *} lemma gcd2_common_divisor: "gcd2 k m dvd k \ gcd2 k m dvd m" apply(induct k m rule: gcd2.induct) apply simp apply clarsimp apply (erule (1) dvd_mod_imp_dvd) done text {*gcd2 is the greatest *} lemma gcd2_bounds_common_divisors: "k \ 0 \ m \ 0 \ x dvd k \ x dvd m \ x \ gcd2 k m" apply(induct k m arbitrary: x rule: gcd2.induct) apply (fastforce elim: dvd_imp_le) apply (fastforce intro: dvd_mod) done text {*gcd2 is the greatest common divisor *} lemma greatest_common_divisor_is_gcd2: assumes a: "k \ 0 \ m \ 0" assumes b: "d dvd k \ d dvd m \ (\d'. d' dvd k \ d' dvd m \ d' \ d)" shows "d = gcd2 k m" proof(rule le_antisym) from a b show "d \ gcd2 k m" by (fastforce intro: gcd2_bounds_common_divisors) from a b gcd2_common_divisor show "gcd2 k m \ d" by auto qed text {* gcd2 is equivalent to gcd*} lemma gcd_gcd2: "\k \ 0 \ m \ 0\ \ gcd k m = gcd2 k m" apply(simp add: gcd_def) apply(rule the_equality) using gcd2_common_divisor gcd2_bounds_common_divisors greatest_common_divisor_is_gcd2 by auto text {* correctness of the program *} lemma " VARS k m d t {K \ 0 \ M \ 0} k := K; m := M; WHILE m \ 0 INV { gcd k m = gcd K M \ (k \ 0 \ m \ 0)} DO t := m; m := k mod m; k := t OD; d := k { d = gcd K M }" apply (vcg_simp) apply clarsimp apply (simp add: gcd_gcd2) apply (case_tac m, clarsimp) apply clarsimp apply clarsimp apply (clarsimp simp: gcd_gcd2) done -- ------------------------------------------------- section "playing with numbers" -- ------------------------------------------------- text {* Question: define a tree type containing elements of type "int" in the nodes (including leaves) Question: define a function sum_nodes:: "tree \ nat" computing the sum of all elements. Try on some examples *} datatype tree = Leaf int | Node tree int tree primrec sum_node:: "tree \ nat" where "sum_node (Leaf i) = (nat i)" |"sum_node (Node t1 i t2) = (nat i) + sum_node t1 + sum_node t2" value "nat (-1)" term nat value "sum_node (Node (Leaf 3) 6 (Leaf 1))" value "sum_node (Node (Node (Leaf 3) 6 (Leaf 1)) 14 (Leaf 0)) " end