-- Everything I want, as I want it.

module NeilPrelude1 where


-- Precedence -------------------------------------

-- Higher precedence numbers bind tighter

infix  0  if_then_else_

infixr 2  _&_ _×_ _1×_ _×1_ _1×1_

infix  3  _≡_ _≢_ -- _≤_ _∈_ 

-- infixl 4 _>>=_ _>>_

infixr 5  _∨_ _xor_
infixr 6  _∧_

infix  8  _==_ _!=_ -- _<=_
infixl 10 _+_ _-_
infixl 11 _*_
-- infixr 12 _^_

-- infixr 13 _+m+_
-- infixr 15 _++_ _+v+_
infixr 16 _∷_

infixr 90 _∘_ _∘'_ _∘≡_ _≡∘_
-- infixr 90 _⋙_
infixr 91 _∥_ _∥∥_


-- Dependent Functions (Π types) ---------------------

Π : (A : Set)  (A  Set)  Set
Π A B = (a : A)  B a


-- Combinators --

_∘_ : {A B C : Set}  (B  C)  (A  B)  (A  C)
f  g = λ a  f (g a)

_∘'_ : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  ({a : A}  Π (B a) (C a))  (g : Π A B)  (a : A)  C a (g a)
f ∘' g = λ a  f (g a)

id : {A : Set}  A  A
id a = a

apply : {A : Set}  {B : A  Set}  Π A B  Π A B
apply = id

applyTo : {A : Set}  {B : A  Set}  (a : A)  Π A B  B a 
applyTo a f = f a

flip : {A B C : Set}  (A  B  C)  B  A  C
flip f b a = f a b

const : {A B : Set}  A  B  A
const a b = a

compose : {A B C : Set}  (A  B)  (B  C)  (A  C)
compose = flip _∘_ 


-- Propositional Equality --

data Unit : Set where
  unit : Unit

data False : Set where

record True : Set where

absurd : {A : Set}  False  A
absurd ()

Not : Set  Set
Not A = A  False

data _≡_ {A : Set} : A  A  Set where
  refl : {a : A}  a  a 

Id : (A : Set)  A  A  Set
Id _ = _≡_ 

_≢_   : {A : Set}  A  A  Set
a  b = Not (a  b)

trans : {A : Set}  {a b : A}  (x : A)  a  x  x  b  a  b
trans _ refl refl = refl

_∘≡_ : {A : Set}  {x a b : A}  x  b  a  x  a  b
refl ∘≡ refl = refl

_≡∘_ : {A : Set}  {x a b : A}  x  a  b  x  a  b
refl ≡∘ refl = refl 

_∘≡∘_ : {A : Set}  {a b c d : A}  a  c  b  d  (a  b  c  d)
refl ∘≡∘ refl = id 

comm : {A : Set}  {a b : A}  a  b  b  a
comm refl = refl

resp : {A B : Set}  {a a' : A}  (f : A  B)  a  a'  f a  f a' 
resp _ refl = refl

resp2 : {A B C : Set}  {a a' : A}  {b b' : B}
      (f : A  B  C)  a  a'  b  b'  f a b  f a' b'
resp2 {A} {B} {C} {.a} {a} f refl = resp (f a)

resp3 : {A B C D : Set}  {a a' : A}  {b b' : B}  {c c' : C}
      (f : A  B  C  D)  a  a'  b  b'  c  c'  f a b c  f a' b' c'
resp3 {A} {B} {C} {D} {.a} {a} f refl = resp2 (f a)

resp4 : {A B C D E : Set}  {a a' : A}  {b b' : B}  {c c' : C}  {d d' : D}
      (f : A  B  C  D  E)  a  a'  b  b'  c  c'  d  d'  f a b c d  f a' b' c' d'
resp4 {A} {B} {C} {D} {E} {.a} {a} f refl = resp3 (f a)

rewriteLHS : {A : Set}  {a b c : A}  a  c  a  b  c  b
rewriteLHS refl refl = refl

rewriteRHS : {A : Set}  {a b c : A}  b  c  a  b  a  c
rewriteRHS refl refl = refl 

data Inspect {A : Set} (a : A) : Set where
  it : (b : A)  a  b  Inspect a

inspect : {A : Set}  (a : A)  Inspect a
inspect a = it a refl


-- Comparisons ---------------------------------------

data Compare {A : Set} : A  A  Set where
  refl : {a : A}    Compare a a
  neq  : {a b : A}  a  b  Compare a b

data SimpleCompare {A : Set} : A  A  Set where
  refl : {a : A}    SimpleCompare a a
  neq  : {a b : A}  SimpleCompare a b

data OrdCompare {A : Set} (_<_ : A  A  Set) : A  A  Set where
  refl : {a : A}            OrdCompare _<_ a a
  less : {a b : A}  a < b  OrdCompare _<_ a b
  more : {a b : A}  b < a  OrdCompare _<_ a b


-- Dependent Products (Σ types) ----------------------

data Σ (A : Set) (B : A  Set) : Set where
  _&_ : (a : A)  B a  Σ A B

data Σ1 (A : Set) (B : A  Set1) : Set1 where
  _&_ : (a : A)  B a  Σ1 A B

data  (A : Set1) (B : A  Set) : Set1 where
  _&_ : (a : A)  B a   A B

data 1Σ1 (A : Set1) (B : A  Set1) : Set1 where
  _&_ : (a : A)  B a  1Σ1 A B


_×_ : (A B : Set)  Set
A × B = Σ A  _  B)

_×1_ : (A : Set)  (B : Set1)  Set1
A ×1 B = Σ1 A  _  B)

_1×_ : (A : Set1)  (B : Set)  Set1
A  B =  A  _  B)

_1×1_ : (A B : Set1)  Set1
A 1×1 B = 1Σ1 A  _  B)


fst : {A : Set}  {B : A  Set}  Σ A B  A
fst (a & b) = a

snd : {A : Set}  {B : A  Set}  (ab : Σ A B)  B (fst ab)
snd (a & b) = b

fstsnd : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  (abc : Σ A  a  Σ (B a) (C a)))  B (fst abc)
fstsnd (_ & b & _) = b

thd : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  (abc : Σ A  a  Σ (B a) (C a)))  C (fst abc) (fstsnd abc)
thd (_ & _ & c) = c

curry : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  ((ab : Σ A B)  C (fst ab) (snd ab))  (a : A)  (b : B a)  C a b
curry f a b = f (a & b)

uncurry : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  ((a : A)  (b : B a)  C a b)  (ab : Σ A B)  C (fst ab) (snd ab)
uncurry f (a & b) = f a b

uncurry2 : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  {D : (a : A)  (b : B a)  C a b  Set}
            ((a : A)  (b : B a)  (c : C a b)  D a b c)  (abc : Σ A  a  Σ (B a) (C a)))  D (fst abc) (fst (snd abc)) (snd (snd abc)) 
uncurry2 f (a & b & c) = f a b c

uncurry3 : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  {D : (a : A)  (b : B a)  C a b  Set}  {E : (a : A)  (b : B a)  (c : C a b)  D a b c  Set}
            ((a : A)  (b : B a)  (c : C a b)  (d : D a b c)  E a b c d)  (abcd : Σ A  a  Σ (B a)  b  Σ (C a b) (D a b))))  E (fst abcd) (fstsnd abcd) (fst (thd abcd)) (snd (thd abcd))
uncurry3 f (a & b & c & d) = f a b c d

first : {A B C : Set}  (A  C)  A × B  C × B
first f (a & b) = f a & b

second : {A : Set}  {B C : A  Set}  ({a : A}  B a  C a)  Σ A B  Σ A C
second f (a & b) = a & f b

third : {A : Set}  {B : A  Set}  {C D : (a : A)  B a  Set}  ({a : A}  {b : B a}  C a b  D a b)  Σ A  a  Σ (B a) (C a))  Σ A  a  Σ (B a) (D a))
third f (a & b & c) = a & b & f c

_∥_ : {A C : Set}  {B : A  Set}  {D : C  Set}  (f : A  C)  ({a : A}  B a  D (f a))  Σ A B  Σ C D
_∥_ f g (a & b) = f a & g b 

-- cross : (A → C) × (B → D) → A × B → C × D

cross : {A C : Set}  {B : A  Set}  {D : C  Set}  Σ (A  C)  f  {a : A}  B a  D (f a))  Σ A B  Σ C D
cross = uncurry _∥_

_∥∥_ : {A B C : Set}  {J : A  Set}  {K : B  Set}  {L : C  Set} 
         (f : A  B  C)  (g : {a : A}  {b : B}  J a × K b  L (f a b))  Σ A J × Σ B K  Σ C L
_∥∥_ f g ((a & j) & (b & k)) = f a b & g (j & k)

-- ×map : (A → B) → A × A → B × B

×map : {A : Set}  {B : A  Set}  Π A B  (aa : A × A)  B (fst aa) × B (snd aa)
×map f (a₁ & a₂) = f a₁ & f a₂

-- ×zipWiths : (f : A → B → C) → (g : J → K → L) → (A × J) → (B × K) → C × L

×zipWiths : {A B C : Set}  {J : A  Set}  {K : B  Set}  {L : C  Set} 
           (f : A  B  C)  (g : {a : A}  {b : B}  J a  K b  L (f a b))  Σ A J  Σ B K  Σ C L
×zipWiths f g (a & j) (b & k) = f a b & g j k

-- across : (A → B → C) → (J → K → L) → (A × J) × (B × K) → C × L

across : {A B C : Set}  {J : A  Set}  {K : B  Set}  {L : C  Set} 
         (f : A  B  C)  (g : {a : A}  {b : B}  J a  K b  L (f a b))  Σ A J × Σ B K  Σ C L
across f g = uncurry (×zipWiths f g) 

-- pair : (A → B) → (A → C) → A → B × C

pair : {A : Set}  {B : A  Set}  {C : (a : A)  B a  Set}  (f : Π A B)  ((a : A)  C a (f a))  (a : A)  Σ (B a) (C a)
pair f g a = f a & g a

fork : {A : Set}  A  A × A
fork a = a & a

dup : {A : Set}  A  A × A
dup = fork

swap : {A B : Set}  A × B  B × A
swap (a & b) = b & a

×assoc : {A B C : Set}  (A × B) × C  A × B × C
×assoc ((a & b) & c) = a & b & c

×assocR : {A B C : Set}  A × B × C  (A × B) × C
×assocR (a & b & c) = (a & b) & c


-- Sum Types (a.k.a. co-products) --

data Either (A B : Set) : Set where
  left : A  Either A B
  right : B  Either A B

case : {A B C : Set}  (A  C)  (B  C)  Either A B  C
case f g (left a) = f a
case f g (right b) = g b


Trichotomy : (A B C : Set)  Set
Trichotomy A B C = Either (A × Not B × Not C)
                  (Either (Not A × B × Not C)
                          (Not A × Not B × C))

trichcases : {A B C D : Set}  (A  Not B  Not C  D)  (Not A  B  Not C  D)  (Not A  Not B  C  D)  Trichotomy A B C  D
trichcases f _ _ (left (a & b & c)) = f a b c
trichcases _ f _ (right (left (a & b & c))) = f a b c
trichcases _ _ f (right (right (a & b & c))) = f a b c


-- Maybe --

data Maybe (A : Set) : Set where
  nothing : Maybe A
  just    : A  Maybe A

maybe : {A B : Set}  B  (A  B)  Maybe A  B 
maybe b _ nothing = b
maybe _ f (just a) = f a

maybeMergeWith : {A : Set}  (A  A  A)  Maybe A  Maybe A  Maybe A
maybeMergeWith f (just a) (just b) = just (f a b)
maybeMergeWith f nothing mb = mb
maybeMergeWith f ma nothing = ma

maybeMap : {A B : Set}  (A  B)  Maybe A  Maybe B
maybeMap f nothing = nothing
maybeMap f (just a) = just (f a)


-- Booleans --

data Bool : Set where
  false : Bool
  true  : Bool

{-# BUILTIN BOOL  Bool  #-}
{-# BUILTIN TRUE  true  #-}
{-# BUILTIN FALSE false #-}

not           : Bool  Bool
not false     = true
not true      = false

¬ : {A : Set}  (A  Bool)  (A  Bool)
¬ p = not  p

isTrue : Bool  Set
isTrue true = True
isTrue false = False

-- "Sat"isfies the predicate

Sat : {A : Set}  (A  Bool)  A  Set
Sat p a = isTrue (p a)

isFalse : Bool  Set
isFalse = Sat not

trueIsTrue : {b : Bool}  b  true  isTrue b
trueIsTrue refl = _

falseIsFalse : {b : Bool}  b  false  isFalse b
falseIsFalse refl = _

if_then_else_ : {A : Set}  Bool  A  A  A
if_then_else_ false t e = e
if_then_else_ true t e = t

ifte : {A : Set}  A  A  Bool  A
ifte t e false = e
ifte t e true  = t

_∧_ : Bool  Bool  Bool
true   b = b
false  b = false

_∨_ : Bool  Bool  Bool
true   b = true
false  b = b

_xor_ : Bool  Bool  Bool
true  xor b = not b
false xor b = b


-- Natural Numbers --

data  : Set where
  O : 
  S :   

{-# BUILTIN NATURAL  #-}
{-# BUILTIN ZERO    O #-}
{-# BUILTIN SUC     S #-}

_+_         :     
O + n       = n
S m + n     = S (m + n)

{-# BUILTIN NATPLUS _+_ #-}

_-_ :     
m   - O   = m
O   - n   = O
S m - S n = m - n

{-# BUILTIN NATMINUS _-_ #-}

_*_ :     
O   * n = O
S m * n = n + m * n

{-# BUILTIN NATTIMES _*_ #-}

natrec : {P :   Set}  P O  ({m : }  P m  P (S m))  Π  P
natrec     base step O = base
natrec {P} base step (S n) = step (natrec {P} base step n)

natcases : {A :   Set}  A O  ((n : )  A (S n))  Π  A
natcases a f = natrec a  {n} _  f n)



------------------- Lists ---------------------

-- Lists --

-- import List.agda for functions

data List (A : Set) : Set where
  []   : List A
  _∷_  : A  List A  List A

{-# BUILTIN LIST List #-}
{-# BUILTIN NIL  []  #-}
{-# BUILTIN CONS _∷_ #-}

data List' (S : Set1) : Set1 where
  []  : List' S
  _∷_ : S  List' S  List' S 


-- Vectors --

-- import Vector.agda for functions

data Vec (A : Set) :   Set where
  [] : Vec A O
  _∷_  : {n : }  A  Vec A n  Vec A (S n)

data Fin :   Set where
  fO : {n : }  Fin (S n)
  fS : {n : }  Fin n  Fin (S n)


-- import HetroVector.agda for functions

data HetVec {A : Set} (eType : A  Set) : (List A)  Set where
  ⟨⟩   : HetVec eType []
  _∷_  : {a : A}  {as : List A}  (eType a)  HetVec eType as  HetVec eType (a  as)

data HetVec2 {A B : Set} (eType : A  B  Set) : List A  List B  Set where
  ⟨⟩   : HetVec2 eType [] []
  _∷_  : {a : A}  {b : B}  {as : List A}  {bs : List B}  eType a b  HetVec2 eType as bs  HetVec2 eType (a  as) (b  bs)


-- Integers -------------------------------------

data  : Set where
  +S :     
  Z  : 
  -S :   

pred :   
pred (+S O)     = Z
pred (+S (S n)) = +S n
pred Z          = -S O
pred (-S n)     = -S (S n)



-- Equality Universe --

data EqUni : Set where
  bool    : EqUni
  nat     : EqUni
  int     : EqUni
  maybeEq : EqUni  EqUni
  list    : EqUni  EqUni
  vector  : EqUni    EqUni
  fin     :   EqUni

eqSet : EqUni  Set
eqSet bool = Bool
eqSet nat = 
eqSet int = 
eqSet (maybeEq t) = Maybe (eqSet t)
eqSet (list t) = List (eqSet t)
eqSet (vector t n) = Vec (eqSet t) n
eqSet (fin n) = Fin n

_==_ : {t : EqUni}  eqSet t  eqSet t  Bool

_==_ {bool} false b = not b
_==_ {bool} true b = b

_==_ {nat} O O = true
_==_ {nat} (S m) (S n) = m == n
_==_ {nat} _ _ = false

_==_ {int} Z Z = true
_==_ {int} (+S m) (+S n) = m == n
_==_ {int} (-S m) (-S n) = m == n
_==_ {int} _ _ = false

_==_ {maybeEq t} nothing nothing = true
_==_ {maybeEq t} (just a) (just b) = a == b
_==_ {maybeEq t} _ _ = false

_==_ {list t} [] [] = true
_==_ {list t} (a  as) (b  bs) = a == b  as == bs
_==_ {list t} _ _ = false

_==_ {vector t O} [] [] = true
_==_ {vector t (S n)} (a  as) (b  bs) = a == b  as == bs

_==_ {fin O} () () 
_==_ {fin (S n)} fO fO = true
_==_ {fin (S n)} (fS fm) (fS fn) = fm == fn
_==_ {fin (S n)} _ _ = false


_!=_ : {t : EqUni}  eqSet t  eqSet t  Bool
a != b = not (a == b)