import data.fin
open function

constant tuple.{l} : Type.{l} → nat → Type.{l}
constant nil {A : Type} : tuple A 0
constant cons {A : Type} {n : nat} : A → tuple A n → tuple A (nat.succ n)
constant append {A : Type} {n m : nat} : tuple A n → tuple A m → tuple A (n + m)
constant reverse {A : Type} {n : nat} : tuple A n → tuple A n
constant map {A B : Type} {n : nat} : (A → B) → tuple A n → tuple B n
constant ith {A : Type} {n : nat} : tuple A n → fin n → A
constant tail {A : Type} {n : nat} : tuple A (nat.succ n) → tuple A n

infix ` ++ ` := append
axiom append_nil {A : Type} {n : nat} (v : tuple A n) : v ++ nil = v
axiom nil_append {A : Type} {n : nat} (v : tuple A n) : nil ++ v == v
axiom append_assoc {A : Type} {n₁ n₂ n₃ : nat} (v₁ : tuple A n₁) (v₂ : tuple A n₂) (v₃ : tuple A n₃) : (v₁ ++ v₂) ++ v₃ == v₁ ++ (v₂ ++ v₃)

axiom reverse_reverse {A : Type} {n : nat} (v : tuple A n) : reverse (reverse v) = v
axiom map_cons {A B : Type} {n : nat} (f : A → B) (a : A) (v : tuple A n) : map f (cons a v) = cons (f a) (map f v)
axiom map_map {A B C : Type} {n : nat} (g : B → C) (f : A → B) (v : tuple A n) : map g (map f v) = map (g ∘ f) v
axiom ith_succ_eq_ith_tail {A : Type} {n : nat} (v : tuple A (nat.succ n)) (i : fin n) : ith v (fin.succ i) = ith (tail v) i

attribute append_nil nil_append append_assoc reverse_reverse map_cons map_map ith_succ_eq_ith_tail [simp]

set_option blast.strategy "cc"
set_option blast.cc.heq true
set_option blast.cc.subsingleton true

universes l1 l2 l3
constants {A : Type.{l1}} (a b c : A) (f g : A → A)
          (m n p q : ℕ)
          (xs : tuple A m) (ys : tuple A n) (zs : tuple A p) (ws : tuple A q)
          {B : Type.{l2}} (h1 h2 : A → B)
          {C : Type.{l3}} (k1 k2 : B → C)

example : a = b → m = n → xs == ys → cons a xs == cons b ys := by blast
example : a = b → b = c → m = n → n = p → xs == ys → ys == zs → cons a (cons b xs) == cons c (cons a zs) := by blast
example : a = b → b = c → m = n → n = p → xs == ys → ys == zs → cons (f a) (cons (g b) xs) == cons (f c) (cons (g a) zs) := by blast

example : m = n → xs == ys → xs ++ zs == ys ++ zs := by blast
example : m = n → p = q → xs == ys → zs == ws → xs ++ zs == ys ++ ws := by blast

example : m = n → xs == ys → nil ++ xs == ys := by inst_simp
example : (xs ++ ys) ++ zs == xs ++ (ys ++ zs) := by inst_simp
example : p = m + n → zs == xs ++ ys → zs ++ ws == (xs ++ ys) ++ ws := by inst_simp
example : m + n = p → xs ++ ys == zs → zs ++ ws == xs ++ (ys ++ ws) := by inst_simp

example : m = n → p = q → m + n = p → xs == ys → zs == ws → xs ++ ys == zs → ws ++ ws == (ys ++ xs) ++ (xs ++ ys) := by inst_simp

example : m = n → n = p → xs == reverse ys → ys == reverse zs → xs == zs := by inst_simp

example : m = n → xs == ys → f = g → a = b → map f (cons a xs) == cons (g b) (map g ys) := by
inst_simp

attribute function.comp [semireducible]
example : m = n → xs == ys → h1 = h2 → k1 = k2 → map k1 (map h1 xs) == map (k2 ∘ h2) ys := by inst_simp

example : ∀ (xs : tuple A (nat.succ m)) (ys : tuple A (nat.succ n))
            (i : fin m) (j : fin n),
            m = n → i == j → xs == ys → ith xs (fin.succ i) == ith (tail ys) j := by inst_simp
