// Adapted from https://www.aya-prover.org/guide/prover-tutorial.html

// For syntax, see
// - https://github.com/aya-prover/aya-dev/blob/main/parser/src/main/grammar/AyaPsiLexer.flex
// - https://github.com/aya-prover/aya-dev/blob/main/parser/src/main/grammar/AyaPsiParser.bnf

prim I
prim coe (r s : I) (A : IType) : A rA s
prim Path (A : IType) (a : A 0) (b : A 1) : Type
variable A B : Type // A : Type 0

// I: 0 = 1
// Path :
// 0 ↦ a : A
// 1 ↦ b : A
//
def infix = (a b : A) ⇒ Path (fn iA) /* I → Type 0*/ a b

open inductive Nat
| zero
| suc Nat

open inductive Bool | true | false
def not Bool : Bool
| truefalse
| falsetrue

def id (x : Bool) ⇒ x

def Goalid = (fn xnot (not x))

// def question : Goal => {??}

def Goal' (x : Bool) ⇒ id x = not (not x)

def funExt (f g : AB) (p : af a = g a /* Fn (y : ?_2 A B f g) → f y = g y */) : f = g /* I -> (A -> B) */
fn i ap a i

def refl {a : A} : a = afn ia

def pmap (f : AB) {a b : A} (p : a = b) : f a = f b
fn if (p i)

/* coe (r s : I) (A : I -> Type) : A r -> A s r: 0 s: 1 A r ↦ A 0 ↦ p 0 ↦ A A s ↦ A 1 ↦ p 1 ↦ B cast A to B using A ↑= B */
def cast (p : A = B) : AB coe 0 1 (fn ip i)

// ↑ is ulift
def cast' (p : A = B) : AB ⇒ ( coe) 0 1 (fn ip i)

/* cast ?A A a b p ?A A a b p = (b = a) */
// def pinv {a b : A} (p : a = b) : b = a ⇒ cast {??} {??}

/* Error: Cannot check the expression cast (\ i ⇒ p i = b) refl of type b = b against the type b = a In particular, we failed to unify b with a */
// def pinv {a b : A} (p : a = b) : b = a ⇒ cast (fn i ⇒ p i = b) refl

/* Error: Cannot check the expression p of type a = b against the type a = a In particular, we failed to unify b with a */
// def pinv {a b : A} (p : a = b) : b = a ⇒ cast (fn i ⇒ p i = a) p

/* Error: Cannot check the expression cast (\ i ⇒ p i = b) of type (a = b) → b = b against the type (a = a) = (b = a) */
// def pinv_pre {a b : A} (p : a = b) : (a = a) = (b = a) => cast (fn i => p i = b)

def pinv {a b : A} (p : a = b) : b = acast (fn ip i = a) refl

def concat {a b c : A} (p : a = b) (q : b = c) : a = ccast (fn ia = q i) p

example def infix + Nat Nat : Nat
| 0, nn
| suc m, nsuc (m + n)

overlap def infix + Nat Nat : Nat
| 0, nn
| n, 0 ⇒ n
| suc m, nsuc (m + n)
| m, suc nsuc (m + n)
tighter =

def +-comm' (a b : Nat) : a + b = b + a
| _, 0 ⇒ refl
| _, suc _ ⇒ pmap suc (+-comm' _ _)

def +-comm (a b : Nat) : a + b = b + a elim a
| 0 ⇒ refl
| suc _ ⇒ pmap suc (+-comm _ _)

def +-assoc' (a b c : Nat) : (a + b) + c = a + (b + c)
| 0, _, _ ⇒ refl
| suc a', b', c'pmap suc (+-assoc' a' b' c')

def +-assoc {a b c : Nat} : (a + b) + c = a + (b + c) elim a
| 0 ⇒ refl
| suc _ ⇒ pmap suc +-assoc

variable n m o : Nat
// Definitions
open inductive Vec (Nat) (Type)
| 0, Anil
| suc n, Ainfixr :< A (Vec n A)

overlap def infixr ++ (Vec n A) (Vec m A) : Vec (n + m) A
| nil, ysys
| ys, nilys
| x :< xs, ysx :< xs ++ ys
tighter :< =

/* Error: Cannot check the expression xs ++ (ys ++ zs) of type Vec (n + (m + o)) A against the type Vec ((n + m) + o) A In particular, we failed to unify n + (m + o) with (n + m) + o 1 error(s), 0 warning(s). */
// example def ++-assoc-ty' (xs : Vec n A) (ys : Vec m A) (zs : Vec o A)
// ⇒ ((xs ++ ys) ++ zs) = xs ++ (ys ++ zs)

// This is the original example
example def ++-assoc-ty (xs : Vec n A) (ys : Vec m A) (zs : Vec o A)
cast ( pmap (fn nVec n A) +-assoc) ((xs ++ ys) ++ zs) = xs ++ (ys ++ zs)

// This is after properly adding parenthesis, and one ulift is actually unnecessary
example def ++-assoc-ty'' (xs : Vec n A) (ys : Vec m A) (zs : Vec o A)
⇒ (cast // def cast (p : A ↑ = B) : A → B
(/* ↑ */ pmap (fn nVec n A) +-assoc) // (n + m) + o = m + (n + o) --pmap-> Vec ((n + m) + o) A = Vec (m + (n + o)) A
((xs ++ ys) ++ zs)
)
= xs ++ (ys ++ zs)

def coeRefl (a : A) : ((coe 1 1 (fn jA)) a) = arefl

def coeRefl' (a : A) : ((coe 0 0 (fn jA)) a) = arefl

// def coeRefl'' (a : A) : ((coe 0 1 (fn j ⇒ A)) a) = a ⇒ refl

// original
def castRefl (a : A) : cast refl a = afn icoe i 1 (fn jA) a

// break down
def castRefl' (a : A) : (cast ( refl) a) = a
/* p: (cast (↑ refl) a) = a p 0: (cast (↑ refl) a) p 1: a */
fn i
/* coe (r s : I) (p : I -> Type) : p r -> p s r: i s: 1 p r ↦ p i p s ↦ p 1 ↦ a cast A to A using A ↑= A */
(coe i 1 (fn jA)) a

def castRefl'' (a : A) : a = (cast ( refl) a) ⇒
fn i
/* coe (r s : I) (A : I -> Type) : A r -> A s r: i s: 1 A r ↦ A i ↦ A A s ↦ A 1 ↦ A cast A to A using A ↑= A */
(coe 0 i (fn jA)) a

def Path' (A : IType) (a : A 0) (b : A 1) ⇒ Path A a b

def ++-assoc-type (xs : Vec n A) (ys : Vec m A) (zs : Vec o A)
Path (fn iVec (+-assoc i) A) ((xs ++ ys) ++ zs) (xs ++ (ys ++ zs))

/* overlap def infixr ++ (Vec n A) (Vec m A) : Vec (n + m) A | nil, ys ⇒ ys | ys, nil ⇒ ys | x :< xs, ys ⇒ x :< xs ++ ys tighter :< = */

def ++-assoc (xs : Vec n A) (ys : Vec m A) (zs : Vec o A)
: Path (fn iVec (+-assoc i) A) ((xs ++ ys) ++ zs) (xs ++ (ys ++ zs)) elim xs
| nilrefl
| x :< _ ⇒
fn ix :< ((++-assoc _ _ _) i)

// def ++-assoc' (xs : Vec n A) (ys : Vec m A) (zs : Vec o A)
// : Path (fn i ⇒ Vec (+-assoc i) A) ((xs ++ ys) ++ zs) (xs ++ (ys ++ zs)) elim xs
// | nil ⇒ refl
// | x :< _ => pmap (x :<) (++-assoc' _ _ _)

open inductive Interval
| left
| right
| line : left = right

example def Interval-elim {a b : A} {p : a = b} (i : Interval) : A elim i
| lefta
| rightb
| line jp j

private def lemma
(f g : AB) (p : xf x = g x)
(i : Interval) (a : A) : B elim i
| leftf a
| rightg a
| line jp a j

example def funExt' (f g : AB) (p : af a = g a) : f = g
pmap (lemma f g p) (fn iline i)

open inductive Int
| pos Nat | neg Nat
| zro : pos 0 = neg 0

def succ Int : Int
| pos npos (suc n)
| neg 0 ⇒ pos 1
| neg (suc n) ⇒ neg n
| zro ipos 1

def abs Int : Nat
| pos nn
| neg nn
| zro _ ⇒ 0