section \<open>Solutions for Session 1\<close>

theory Solutions01
  imports Main
begin

(*avoid theory name prefix for ambiguous constant names in output*)
declare [[names_short]]

\<comment> \<open>A data type for finite lists/stacks.\<close>
datatype 'a list = Nil | Cons 'a "'a list"

\<comment> \<open>Appending two lists.\<close>
fun append :: "'a list \<Rightarrow> 'a list \<Rightarrow> 'a list"
  where
    "append Nil ys = ys"
  | "append (Cons x xs) ys = Cons x (append xs ys)"

\<comment> \<open>Reversing a list.\<close>
fun rev :: "'a list \<Rightarrow> 'a list"
  where
    "rev Nil = Nil"
  | "rev (Cons x xs) = append (rev xs) (Cons x Nil)"

lemma append_Nil_right: "append xs Nil = xs"
  by (induction xs) auto

lemma append_assoc: "append (append xs ys) zs = append xs (append ys zs)"
  by (induction xs) auto

lemma rev_append: "rev (append xs ys) = append (rev ys) (rev xs)"
  by (induction xs) (auto simp: append_Nil_right append_assoc)

lemma rev_rev: "rev (rev xs) = xs"
  by (induction xs) (auto simp: rev_append)


subsection \<open>Exercise 1\<close>

fun map :: "('a \<Rightarrow> 'b) \<Rightarrow> 'a list \<Rightarrow> 'b list"
  where
    "map f Nil = Nil"
  | "map f (Cons x xs) = Cons (f x) (map f xs)"

lemma map_map: "map f (map g xs) = map (f \<circ> g) xs"
  by (induction xs) auto


subsection \<open>Exercise 2\<close>

lemma map_append: "map f (append xs ys) = append (map f xs) (map f ys)"
  by (induction xs) auto


subsection \<open>Exercise 3\<close>

fun concat :: "'a list list \<Rightarrow> 'a list"
  where
    "concat Nil = Nil"
  | "concat (Cons x xs) = append x (concat xs)"

lemma concat_append: "concat (append xs ys) = append (concat xs) (concat ys)"
  by (induction xs) (auto simp: append_assoc)


subsection \<open>Exercise 4\<close>

fun add :: "nat \<Rightarrow> nat \<Rightarrow> nat"
  where
    "add 0 y = y"
  | "add (Suc x) y = Suc (add x y)"

lemma add_0_right: "add x 0 = x"
  by (induction x) auto

lemma add_Suc_right: "add x (Suc y) = Suc (add x y)"
  by (induction x) auto

lemma add_commute: "add x y = add y x"
  by (induction x) (auto simp: add_0_right add_Suc_right)


subsection \<open>Exercise 5\<close>

fun length :: "'a list \<Rightarrow> nat"
  where
    "length Nil = 0"
  | "length (Cons x xs) = Suc (length xs)"

lemma length_map: "length (map f xs) = length xs"
  by (induction xs) auto


subsection \<open>Exercise 6\<close>

fun sum_list :: "nat list \<Rightarrow> nat"
  where
    "sum_list Nil = 0"
  | "sum_list (Cons x xs) = add x (sum_list xs)"

lemma length_append: "length (append xs ys) = add (length xs) (length ys)"
  by (induction xs) auto

lemma length_concat: "length (concat xs) = sum_list (map length xs)"
  by (induction xs) (auto simp: length_append)


subsection \<open>Exercise 7\<close>

fun snoc :: "'a list \<Rightarrow> 'a \<Rightarrow> 'a list"
  where
    "snoc Nil y = Cons y Nil"
  | "snoc (Cons x xs) y = Cons x (snoc xs y)"

lemma snoc_append: "snoc (append xs ys) z = append xs (snoc ys z)"
  by (induction xs) auto

lemma snoc_rev: "snoc (rev xs) y = rev (Cons y xs)"
  by (induction xs) (auto simp: snoc_append append_assoc)

end
