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