/- Lecture 02 Exercises -/

-- Recall the definition of `list`.
#print inductive list

namespace exercises2

def length {a : Type} : list a → nat
| [] := 0
| (x :: xs) := nat.succ (length xs) .

/-- Add one list to the end of another. -/
def app {a : Type} : list a → list a → list a
| [] l2 := l2
| (x :: xs) l2 := x :: (app xs l2)

theorem app_nil:
  forall {a : Type} (l : list a),
  app l [] = l :=
begin
  admit -- TODO
end

theorem app_assoc:
  forall {a : Type} (l1 l2 l3: list a),
  app (app l1 l2) l3 = app l1 (app l2 l3) :=
begin
  intros; induction l1 ; { simp [app, *] at * }
end

/-- Simple but inefficient way to reverse a list. -/
def rev {α : Type} : list α → list α
| [] := []
| (x :: xs) := app (rev xs) [x]

/-- Tail recursion is faster, but more complicated. -/
def fast_rev_aux {a : Type} : list a → list a → list a
| [] acc := acc
| (x :: xs) acc := fast_rev_aux xs (x :: acc)

def fast_rev {a : Type} (l : list a) : list a :=
  fast_rev_aux l []

/-- Add an element to the end of a list. -/
def snoc {a : Type} : list a → a → list a
| [] x := x :: []
| (y :: ys) x := y :: (snoc ys x)

theorem snoc_app_singleton:
  forall A (l: list A) (x: A),
  snoc l x = app l [x] :=
begin
--  (** TODO *)
admit
end

theorem app_snoc_l:
  forall {a : Type} (l1 l2 : list a) (x : a),
  app (snoc l1 x) l2 = app l1 (x :: l2) :=
begin
  -- (** TODO *)
  admit
end

theorem app_snoc_r:
  forall A (l1: list A) (l2: list A) (x: A),
  app l1 (snoc l2 x) = snoc (app l1 l2) x :=
begin
  -- (** TODO *)
  admit
end

/-- Simple but inefficient way to reverse a list. -/
def rev_snoc {a : Type} : list a → list a
| [] := []
| (x :: xs) := snoc (rev_snoc xs) x

lemma fast_rev_ok_snoc:
  forall a (l: list a),
  fast_rev l = rev_snoc l :=
begin
  -- (** TODO -- you will need to define a helper
  --            very similar to how we proved fast_ref_ok *)
  admit
end

/-- Useful in proving rev_length below. -/
lemma
 plus_1_S:
  forall (n : nat),
  n + 1 = nat.succ n :=
begin
  intros,
  induction n,
  { simp },
  { simp * at * }
end

lemma rev_length:
  forall (a : Type) (l : list a),
  length (rev l) = length l :=
begin
  admit
  -- (** TODO -- you will need to define a helper
  --            that relates length and app *)
end

lemma
 rev_involutive:
  forall A (l: list A),
  rev (rev l) = l :=
begin
  -- (** TODO -- you will need to define a helper
  --            that relates rev and app, its proof should
  --            use app_assoc *)
  admit
end

/-







  Solutions Below







-/

namespace solutions

theorem app_nil:
  forall {a : Type} (l : list a),
  app l [] = l :=
begin
  intros, induction l,
  { simp [app] },
  { simp [app, *] }
end

theorem snoc_app_singleton:
  forall {a : Type} (l: list a) (x: a),
  snoc l x = app l [x] :=
begin
  intros, induction l,
  { simp [snoc, app] },
  { simp [snoc, app, *] },
end

theorem app_snoc_l:
  forall {a : Type} (l1 l2 : list a) (x : a),
  app (snoc l1 x) l2 = app l1 (x :: l2) :=
begin
  intros, induction l1,
  { simp [snoc, app] },
  { simp [snoc, app, *] },
end

theorem app_snoc_r:
  forall A (l1: list A) (l2: list A) (x: A),
  app l1 (snoc l2 x) = snoc (app l1 l2) x :=
begin
  intros, induction l1,
  { simp [app, snoc] },
  { simp [app, snoc, *] },
end

lemma fast_rev_ok_snoc:
  forall a (l: list a),
  fast_rev l = rev_snoc l :=
begin
  intros, induction l,
  { simp [fast_rev, fast_rev_aux, rev_snoc], },
  { simp [fast_rev, fast_rev_aux, rev_snoc],
    admit
  }
end

lemma length_app :
  forall (a : Type) (l1 l2 : list a),
  length (app l1 l2) = length l1 + length l2 :=
begin
  intros, induction l1; { simp [app, length, *] }
end

lemma rev_length:
  forall (a : Type) (l : list a),
  length (rev l) = length l :=
begin
  intros, induction l,
  { simp [length, rev] },
  { simp [rev, length_app, length, *], }
end

lemma rev_app_singleton :
  forall (t : Type) (a : t) (l1 l2 : list t),
  rev (app l1 l2) = app (rev l2) (rev l1) :=
begin
  intros, induction l1,
  { simp [rev, app, app_nil] },
  { simp [app, rev, ih_1, app_assoc] }
end

lemma
 rev_involutive:
  forall a (l: list a),
  rev (rev l) = l :=
begin
  intros, induction l,
  { simp [rev] },
  { simp [rev],
    rw rev_app_singleton,
    rw ih_1,
    simp [rev, app],
    exact a_1 }
end

end solutions

end exercises2
