diff --git a/isabelle/2020/HSV_tasks_2020_solutions.thy b/isabelle/2020/HSV_tasks_2020_solutions.thy new file mode 100644 index 0000000..49de1c7 --- /dev/null +++ b/isabelle/2020/HSV_tasks_2020_solutions.thy @@ -0,0 +1,381 @@ +theory HSV_tasks_2020_solutions imports Complex_Main begin + +section \Task 1: proving that "3 / sqrt 2" is irrational.\ + +(* The following theorem is copied from Chapter 3 of the worksheet *) +theorem sqrt2_irrational: "sqrt 2 \ \" +proof auto + assume "sqrt 2 \ \" + then obtain m n where + "n \ 0" and "\sqrt 2\ = real m / real n" and "coprime m n" + by (rule Rats_abs_nat_div_natE) + hence "\sqrt 2\^2 = (real m / real n)^2" by auto + hence "2 = (real m / real n)^2" by simp + hence "2 = (real m)^2 / (real n)^2" unfolding power_divide by auto + hence "2 * (real n)^2 = (real m)^2" + by (simp add: nonzero_eq_divide_eq `n \ 0`) + hence "real (2 * n^2) = (real m)^2" by auto + hence *: "2 * n^2 = m^2" + using of_nat_power_eq_of_nat_cancel_iff by blast + hence "even (m^2)" by presburger + hence "even m" by simp + then obtain m' where "m = 2 * m'" by auto + with * have "2 * n^2 = (2 * m')^2" by auto + hence "2 * n^2 = 4 * m'^2" by simp + hence "n^2 = 2 * m'^2" by simp + hence "even (n^2)" by presburger + hence "even n" by simp + with `even m` and `coprime m n` show False by auto +qed + +theorem "3 / sqrt 2 \ \" +proof auto + assume *: "3 / sqrt 2 \ \" + + (* establish that 3 / sqrt 2 = 3 * sqrt 2 / 2 *) + have "3 / sqrt 2 = (3 / sqrt 2) * (sqrt 2 / sqrt 2)" by auto + also have "... = 3 * sqrt 2 / (sqrt 2 * sqrt 2)" by argo + also have "... = 3 * sqrt 2 / 2" by simp + finally have "3 / sqrt 2 = 3 * sqrt 2 / 2" by assumption + + (* so 3 * sqrt 2 / 2 is rational ... *) + with * have "3 * sqrt 2 / 2 \ \" by auto + + (* ... and 2/3 is also rational ... *) + moreover have "2 / 3 \ \" by simp + + (* ... so their product is also rational ... *) + ultimately have "(3 * sqrt 2 / 2) * (2 / 3) \ \" + using Rats_mult by blast + + (* ... which means sqrt 2 is rational ... *) + hence "sqrt 2 \ \" by simp + + (* ... which contradicts the previous theorem! *) + with sqrt2_irrational show False by simp +qed + +section \Task 2: Centred pentagonal numbers.\ + +fun pent :: "nat \ nat" where + "pent n = (if n = 0 then 1 else 5 * n + pent (n - 1))" + +value "pent 0" (* should be 1 *) +value "pent 1" (* should be 6 *) +value "pent 2" (* should be 16 *) +value "pent 3" (* should be 31 *) + +theorem "pent n = (5 * n^2 + 5 * n + 2) div 2" +proof (induct n) + case 0 + thus ?case by auto +next + case (Suc n) + have "pent (Suc n) = 5 * (n + 1) + pent n" by simp + also have "... = 5 * n + 5 + (5 * n^2 + 5 * n + 2) div 2" using Suc by simp + also have "... = (5 * (n^2 + 2 * n + 1) + 5 * (n + 1) + 2) div 2" by simp + also have "... = (5 * (n + 1)^2 + 5 * (n + 1) + 2) div 2" + by (smt add.commute add_Suc mult_2 one_add_one plus_1_eq_Suc power2_sum power_one) + also have "... = (5 * (Suc n)^2 + 5 * Suc n + 2) div 2" by simp + finally show ?case by assumption +qed + + +section \Task 3: Lucas numbers.\ + +fun luc :: "nat \ nat" where + "luc n = (if n = 0 then 2 else if n = 1 then 1 else luc (n - 1) + luc (n - 2))" + +value "luc 0" (* should be 2 *) +value "luc 1" (* should be 1 *) +value "luc 2" (* should be 3 *) +value "luc 3" (* should be 4 *) + +fun fib :: "nat \ nat" where + "fib n = (if n = 0 then 0 else if n = 1 then 1 else fib (n - 1) + fib (n - 2))" + +value "fib 0" (* should be 0 *) +value "fib 1" (* should be 1 *) +value "fib 2" (* should be 1 *) +value "fib 3" (* should be 2 *) + +thm fib.induct (* rule induction theorem for fib *) + +theorem "luc n \ fib n" + apply (rule fib.induct[of "\n. luc n \ fib n"]) + apply (case_tac "n < 2") + apply auto + done + +theorem "luc (n + 1) = fib n + fib (n + 2)" +proof (rule fib.induct[of "\n. luc (n + 1) = fib n + fib (n + 2)"]) + fix n + assume IH1: "n \ 0 \ n \ 1 \ luc (n - 1 + 1) = fib (n - 1) + fib (n - 1 + 2)" + assume IH2: "n \ 0 \ n \ 1 \ luc (n - 2 + 1) = fib (n - 2) + fib (n - 2 + 2)" + + (* First deal with the easy case where n \ 1 *) + { + assume "n \ 1" (* local assumption *) + hence "luc (n + 1) = fib n + fib (n + 2)" by simp + } + moreover (* Now deal with the trickier case where n > 1 *) + { + assume "n > 1" + + (* Simplify the induction hypotheses *) + from IH1 have *: "luc n = fib (n - 1) + fib (n + 1)" using `n > 1` by auto + from IH2 have + "luc (n - 2 + 1) = fib (n - 2) + fib (n - 2 + 2)" using `n > 1` by fastforce + hence **: "luc (n - 1) = fib (n - 2) + fib n" + by (metis Nat.add_diff_assoc2 Suc_leI add.commute add_diff_cancel_right' + diff_Suc_Suc one_add_one plus_1_eq_Suc `n > 1`) + + (* Some equational reasoning using the definitions of luc and fib to finish the job *) + have "luc (n + 1) = luc n + luc (n - 1)" using `n > 1` by simp + also have "... = fib (n - 1) + fib (n + 1) + fib (n - 2) + fib n" using * and ** by simp + also have "... = fib n + fib (n + 2)" using `n > 1` by simp + finally have "luc (n + 1) = fib n + fib (n + 2)" by simp + } + ultimately show "luc (n + 1) = fib n + fib (n + 2)" by simp +qed + +section \Task 4: Balancing circuits.\ + +datatype "circuit" = + NOT "circuit" +| AND "circuit" "circuit" +| OR "circuit" "circuit" +| TRUE +| FALSE +| INPUT "int" + +text \Simulates a circuit given a valuation for each input wire\ + +fun simulate where + "simulate (AND c1 c2) \ = ((simulate c1 \) \ (simulate c2 \))" +| "simulate (OR c1 c2) \ = ((simulate c1 \) \ (simulate c2 \))" +| "simulate (NOT c) \ = (\ (simulate c \))" +| "simulate TRUE \ = True" +| "simulate FALSE \ = False" +| "simulate (INPUT i) \ = \ i" + +fun circuits_equiv (infix "\" 50) (* the "50" indicates the operator precedence *) where + "c1 \ c2 = (\\. simulate c1 \ = simulate c2 \)" + +text \Delay (assuming all gates have a delay of 1)\ + +fun delay :: "circuit \ nat" where + "delay (NOT c) = 1 + delay c" +| "delay (AND c1 c2) = 1 + max (delay c1) (delay c2)" +| "delay (OR c1 c2) = 1 + max (delay c1) (delay c2)" +| "delay _ = 0" + +fun is_balanced where + "is_balanced (NOT c) = is_balanced c" +| "is_balanced (AND c1 c2) = (is_balanced c1 \ is_balanced c2 \ delay c1 = delay c2)" +| "is_balanced (OR c1 c2) = (is_balanced c1 \ is_balanced c2 \ delay c1 = delay c2)" +| "is_balanced _ = True" + +value "is_balanced (AND TRUE TRUE)" (* should be True *) +value "is_balanced (AND (NOT TRUE) TRUE)" (* should be False *) +value "is_balanced (AND (NOT TRUE) (OR TRUE (INPUT 1)))" (* should be True *) + +(* produce a balanced tree of OR gates whose leaves are all TRUE *) +fun tree_TRUE where + "tree_TRUE 0 = TRUE" +| "tree_TRUE (Suc n) = OR (tree_TRUE n) (tree_TRUE n)" + +value "tree_TRUE 2" (* should be "OR (OR TRUE TRUE) (OR TRUE TRUE)" *) + +lemma tree_TRUE_equiv_TRUE: + "tree_TRUE n \ TRUE" + by (induct n, auto) + +lemma is_balanced_tree_TRUE: "is_balanced (tree_TRUE n)" + by (induct n, auto) + +lemma delay_tree_TRUE: "delay (tree_TRUE n) = n" + by (induct n, auto) + +(* "pad n c" adds a delay of n to circuit c. It does so by AND-ing with TRUE n times. + The TRUE is actually produced by a tree of OR-gates of whatever depth is necessary + to preserve balance. *) +fun pad where + "pad 0 c = c" +| "pad (Suc n) c = pad n (AND c (tree_TRUE (delay c)))" + +value "pad 2 (INPUT 1)" (* should be "AND (AND (INPUT 1) TRUE) (OR TRUE TRUE)" *) + +(* Padding does not change a circuit's behaviour *) +lemma padding_is_sound: + "pad n c \ c" + apply (induct n arbitrary: c) + using tree_TRUE_equiv_TRUE by auto + +(* Padding does not unbalance a circuit *) +lemma padding_preserves_balance: "is_balanced c \ is_balanced (pad n c)" +proof (induct n arbitrary: c) + case 0 + thus "is_balanced (pad 0 c)" by simp +next + case (Suc n) + thus "is_balanced (pad (Suc n) c)" + using is_balanced_tree_TRUE and delay_tree_TRUE by simp +qed + +(* Padding by n adds a delay of n *) +lemma padding_adds_delay: "delay (pad n c) = n + delay c" + apply (induct n arbitrary: c) + apply (auto simp add: delay_tree_TRUE) + done + +(* add padding to whichever of c1 and c2 is shorter, then call continuation k *) +fun pad_operands where + "pad_operands k c1 c2 = (let d1 = delay c1; d2 = delay c2 in + if d1 > d2 then k c1 (pad (d1 - d2) c2) else k (pad (d2 - d1) c1) c2)" + +lemma padding_AND_is_sound: + "pad_operands AND c1 c2 \ AND c1 c2" + apply (cases "delay c1 > delay c2") + using padding_is_sound by auto + +lemma padding_OR_is_sound: + "pad_operands OR c1 c2 \ OR c1 c2" + apply (cases "delay c1 > delay c2") + using padding_is_sound by auto + +theorem padding_AND_preserves_balance: + "is_balanced c1 \ is_balanced c2 \ is_balanced (pad_operands AND c1 c2)" + apply (cases "delay c1 > delay c2") + apply (auto simp add: padding_adds_delay padding_preserves_balance) + done + +theorem padding_OR_preserves_balance: + "is_balanced c1 \ is_balanced c2 \ is_balanced (pad_operands OR c1 c2)" + apply (cases "delay c1 > delay c2") + apply (auto simp add: padding_adds_delay padding_preserves_balance) + done + +fun balance where + "balance (NOT c) = NOT (balance c)" +| "balance (AND c1 c2) = pad_operands AND (balance c1) (balance c2)" +| "balance (OR c1 c2) = pad_operands OR (balance c1) (balance c2)" +| "balance TRUE = TRUE" +| "balance FALSE = FALSE" +| "balance (INPUT i) = INPUT i" + +value "balance (AND TRUE TRUE)" (* unchanged *) +value "balance (AND (NOT TRUE) TRUE)" (* should be AND (NOT TRUE) (OR TRUE TRUE) *) +value "balance (AND TRUE (NOT TRUE))" (* should be AND (OR TRUE TRUE) (NOT TRUE) *) +value "balance (AND (NOT TRUE) (OR TRUE FALSE))" (* unchanged *) + +(* balancing a circuit doesn't change its behaviour *) +theorem balance_is_sound: "balance c \ c" +proof (induct c) + case (AND c1 c2) + then show ?case using padding_AND_is_sound by auto +next + case (OR c1 c2) + then show ?case using padding_OR_is_sound by auto +qed (simp+) + +(* balancing a circuit does indeed result in a balanced circuit *) +theorem balance_is_complete: "is_balanced (balance c)" +proof (induct c) + case (AND c1 c2) + hence "is_balanced (balance c1)" and "is_balanced (balance c2)" by auto + hence "is_balanced (pad_operands AND (balance c1) (balance c2))" + by (rule padding_AND_preserves_balance) + thus "is_balanced (balance (AND c1 c2))" by simp +next + case (OR c1 c2) + hence "is_balanced (balance c1)" and "is_balanced (balance c2)" by auto + hence "is_balanced (pad_operands OR (balance c1) (balance c2))" + by (rule padding_OR_preserves_balance) + thus "is_balanced (balance (OR c1 c2))" by simp +qed (simp+) + +section \Task 5: Extending with NAND gates.\ + +datatype "circuit'" = + NOT "circuit'" +| AND "circuit'" "circuit'" +| OR "circuit'" "circuit'" +| TRUE +| FALSE +| INPUT "int" +| NAND "circuit'" "circuit'" + +fun fake_NOT where + "fake_NOT c = NAND c TRUE" + +fun transform_to_NAND where + "transform_to_NAND (NOT c) = fake_NOT (transform_to_NAND c)" +| "transform_to_NAND (AND c1 c2) = fake_NOT (NAND (transform_to_NAND c1) (transform_to_NAND c2))" +| "transform_to_NAND (OR c1 c2) = NAND (fake_NOT (transform_to_NAND c1)) (fake_NOT (transform_to_NAND c2))" +| "transform_to_NAND TRUE = TRUE" +| "transform_to_NAND FALSE = fake_NOT TRUE" +| "transform_to_NAND (INPUT i) = INPUT i" +| "transform_to_NAND (NAND c1 c2) = NAND (transform_to_NAND c1) (transform_to_NAND c2)" + + +text \Simulates a circuit given a valuation for each input wire\ + +fun simulate' where + "simulate' (AND c1 c2) \ = ((simulate' c1 \) \ (simulate' c2 \))" +| "simulate' (OR c1 c2) \ = ((simulate' c1 \) \ (simulate' c2 \))" +| "simulate' (NOT c) \ = (\ (simulate' c \))" +| "simulate' TRUE \ = True" +| "simulate' FALSE \ = False" +| "simulate' (INPUT i) \ = \ i" +| "simulate' (NAND c1 c2) \ = (\ ((simulate' c1 \) \ (simulate' c2 \)))" + +definition circuits_equiv' where + "circuits_equiv' c1 c2 \ \\. simulate' c1 \ = simulate' c2 \" + +fun only_NANDs :: "circuit' \ bool" where + "only_NANDs (AND _ _) = False" +| "only_NANDs (OR _ _) = False" +| "only_NANDs (NOT _) = False" +| "only_NANDs TRUE = True" +| "only_NANDs FALSE = False" +| "only_NANDs (INPUT _) = True" +| "only_NANDs (NAND c1 c2) = (only_NANDs c1 \ only_NANDs c2)" + +lemma transform_to_NAND_sound: "circuits_equiv' c (transform_to_NAND c)" + apply (simp add: circuits_equiv'_def) + apply (induct c) + apply auto + done + +lemma transform_to_NAND_complete: "only_NANDs (transform_to_NAND c)" + apply (induct c) + apply auto + done + +theorem "\c. \c'. circuits_equiv' c c' \ only_NANDs c'" + apply clarsimp + apply (rule_tac x="transform_to_NAND c" in exI) + apply (intro conjI) + apply (simp add: transform_to_NAND_sound) + apply (simp add: transform_to_NAND_complete) + done + +section \Task 6: Showing that the transformation to NAND gates can increase delay.\ + +text \Delay (assuming all gates have a delay of 1)\ + +fun delay' :: "circuit' \ nat" where + "delay' (NOT c) = 1 + delay' c" +| "delay' (AND c1 c2) = 1 + max (delay' c1) (delay' c2)" +| "delay' (OR c1 c2) = 1 + max (delay' c1) (delay' c2)" +| "delay' (NAND c1 c2) = 1 + max (delay' c1) (delay' c2)" +| "delay' _ = 0" + +theorem transform_to_NAND_increases_delay: "delay' (transform_to_NAND c) \ 2 * delay' c + 1" + apply (induct c) + apply auto + done + + +end \ No newline at end of file