-- ArkLib/Data/Fin/Tuple/Lemmas.lean

import ArkLib.Data.Fin.Tuple.Notation

namespace Fin

variable {m n : ℕ} {α : Sort u}

-- Repository definitions used:
content: @[elab_as_elim]
def dappend {m n : ℕ} {motive : Fin (m + n) → Sort u}
    (u : (i : Fin m) → motive (Fin.castAdd n i))
    (v : (i : Fin n) → motive (Fin.natAdd m i))
    (i : Fin (m + n)) : motive i :=
  match n with
  | 0 => u i
  | k + 1 => dconcat (dappend u (fun i => v (Fin.castSucc i))) (v (Fin.last k)) i

content: @[elab_as_elim]
def dconcat {n : ℕ} {motive : Fin (n + 1) → Sort u}
    (u : (i : Fin n) → motive i.castSucc) (a : motive (last n)) (i : Fin (n + 1)) : motive i :=
  match n with
  | 0 => match i with | 0 => a
  | _ + 1 => dcons (u 0) (dconcat (fun i => u (Fin.succ i)) a) i

content: @[elab_as_elim]
def dcons {n : ℕ} {motive : Fin (n + 1) → Sort u} (a : motive 0)
    (b : (i : Fin n) → motive i.succ) (i : Fin (n + 1)) : motive i :=
  match n with
  | 0 => match i with | 0 => a
  | _ + 1 => match i with
    | 0 => a
    | ⟨k + 1, hk⟩ => b ⟨k, Nat.succ_lt_succ_iff.mp hk⟩

content: def vcons {n : ℕ} (a : α) (v : Fin n → α) : Fin (n + 1) → α :=
  dcons a v

content: def hconcat {n : ℕ} {α : Fin n → Sort u} {β : Sort u} (u : (i : Fin n) → α i) (a : β) :
    (i : Fin (n + 1)) → Fin.vconcat α β i :=
  fconcat (F := id) u a

content: def fconcat {A : Sort u} {F : A → Sort v} {n : ℕ} {α : Fin n → A} {β : A}
    (u : (i : Fin n) → F (α i)) (a : F β) : (i : Fin (n + 1)) → F (Fin.vconcat α β i) :=
  match n with
  | 0 => fun i => match i with | 0 => a
  | _ + 1 => fcons (u 0) (fconcat (fun i => u (Fin.succ i)) a)

content: def fcons {A : Sort u} {F : A → Sort v} {n : ℕ} {α : A} {β : Fin n → A}
    (a : F α) (b : (i : Fin n) → F (β i)) : (i : Fin (n + 1)) → F (Fin.vcons α β i) :=
  match n with
  | 0 => fun i => match i with | 0 => a
  | _ + 1 => fun i => match i with
    | 0 => a
    | ⟨k + 1, hk⟩ => b ⟨k, Nat.succ_lt_succ_iff.mp hk⟩

content: def vconcat {n : ℕ} (v : Fin n → α) (a : α) : Fin (n + 1) → α :=
  dconcat v a

content: def happend {m n : ℕ} {α : Fin m → Sort u} {β : Fin n → Sort u}
    (u : (i : Fin m) → α i) (v : (i : Fin n) → β i) : (i : Fin (m + n)) → Fin.vappend α β i :=
  fappend (F := id) u v

content: def vappend {m n : ℕ} {α : Sort*} (u : Fin m → α) (v : Fin n → α) : Fin (m + n) → α :=
  dappend u v

content: def fappend {A : Sort u} {F : A → Sort v} {m n : ℕ} {α : Fin m → A} {β : Fin n → A}
    (u : (i : Fin m) → F (α i)) (v : (i : Fin n) → F (β i)) :
    (i : Fin (m + n)) → F (Fin.vappend α β i) :=
  match n with
  | 0 => u
  | k + 1 => fconcat (fappend u (fun i => v (Fin.castSucc i))) (v (Fin.last k))

content: abbrev vempty {α : Sort*} : Fin 0 → α := Fin.elim0

content: def dempty {α : Fin 0 → Sort u} : (i : Fin 0) → α i := fun i => Fin.elim0 i

content: def hcons {n : ℕ} {α : Sort u} {β : Fin n → Sort u} (a : α) (b : (i : Fin n) → β i) :
    (i : Fin (n + 1)) → Fin.vcons α β i :=
  fcons (F := id) a b

content: @[inherit_doc]
infixr:67 " ::ᵛ " => Fin.vcons

content: @[inherit_doc]
infixl:65 " :+ᵛ " => Fin.vconcat

content: @[inherit_doc]
infixr:67 " ::ʰ " => Fin.hcons

content: @[inherit_doc]
infixl:65 " :+ʰ " => Fin.hconcat

content: @[inherit_doc]
infixr:67 " ::ᵈ " => Fin.dcons

content: @[inherit_doc]
infixl:65 " :+ᵈ " => Fin.dconcat

content: infixl:65 " ++ᵛ " => Fin.vappend

content: infixl:65 " ++ᵈ " => Fin.dappend

content: infixl:65 " ++ʰ " => Fin.happend

-- 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:

-- Local lemmas used:
content: @[simp]
theorem dconcat_last {motive : Fin (n + 1) → Sort u} (v : (i : Fin n) → motive (castSucc i))
    (a : motive (last n)) : (v :+ᵈ⟨motive⟩ a) (last n) = a

content: @[simp]
theorem dconcat_castSucc {motive : Fin (n + 1) → Sort u} (v : (i : Fin n) → motive (castSucc i))
    (a : motive (last n)) (i : Fin n) : (v :+ᵈ⟨motive⟩ a) (castSucc i) = v i

content: @[csimp]
theorem dconcat_eq_snoc : @dconcat = @snoc

-- Target theorem and proof:
@[simp]
theorem zero_dappend {motive : Fin (0 + n) → Sort u} {u : (i : Fin 0) → motive (castAdd n i)}
    (v : (i : Fin n) → motive (natAdd 0 i)) :
    dappend (motive := motive) u v = fun i => cast (by simp) (v (i.cast (by omega)))
:= by
  induction n with
  | zero => ext i; exact Fin.elim0 i
  | succ n ih =>
    simp [dappend, ih, dconcat_eq_snoc, Fin.cast, last]
    ext i
    by_cases h : i.val < n
    · have : i = Fin.castSucc ⟨i.val, by simp [h]⟩ := by ext; simp
      rw [this, snoc_castSucc]
      simp
    · have : i.val = n := by omega
      have : i = Fin.last _ := by ext; simp [this]
      rw! [this]
      subst this
      simp_all only [forall_fin_zero_pi, Nat.add_eq, val_last, zero_add,
                    lt_self_iff_false, not_false_eq_true, snoc_last]
      grind only [cases Or]