-- Frap/Exercises/IndProp.lean

namespace Hidden

inductive List (α : Type u) where
  | nil  : List α
  | cons : α → List α → List α

namespace List

def reverse {α : Type u} (as : List α) : List α :=
  match as with
  | nil        => nil
  | cons a as' => reverse as' ++ cons a nil

inductive Palindrome {α : Type} : List α → Prop where
  | nil : Palindrome nil
  | single (x : α) : Palindrome (cons x nil)
  | sandwich (x : α) (xs : List α)
      : Palindrome xs → Palindrome (cons x xs ++ cons x nil)

namespace Palindrome

-- Repository definitions used:
content: ...

-- Repository lemmas used:
content: theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]

-- Local definitions used:
content: inductive List (α : Type u) where
  | nil  : List α
  | cons : α → List α → List α

content: def reverse {α : Type u} (as : List α) : List α :=
  match as with
  | nil        => nil
  | cons a as' => reverse as' ++ cons a nil

content: inductive Palindrome {α : Type} : List α → Prop where
  | nil : Palindrome nil
  | single (x : α) : Palindrome (cons x nil)
  | sandwich (x : α) (xs : List α)
      : Palindrome xs → Palindrome (cons x xs ++ cons x nil)

-- Local lemmas used:
content: theorem nil_append (as : List α) : nil ++ as = as

content: theorem cons_append (a : α) (as bs : List α)
        : (cons a as) ++ bs = cons a (as ++ bs)

content: theorem append_assoc (as bs cs : List α)
        : (as ++ bs) ++ cs = as ++ (bs ++ cs)

-- Target theorem and proof:
theorem Palindrome_app_rev {α : Type} (l : List α) : Palindrome (l ++ reverse l)
:= by
  induction l with
  | nil => rw [nil_append, reverse]; exact nil
  | cons x xs ih =>
    rw [cons_append, reverse, ←append_assoc, ←cons_append]
    apply sandwich x
    exact ih