{-# OPTIONS --without-K --safe #-}

module lecture1 where
import introduction

Type = Set
Type₁ = Set₁

data Bool : Type where
 true false : Bool

-- Type "→" as "\to" or "\->"
not : Bool → Bool
not true  = false
not false = true

idBool : Bool → Bool
idBool x = x

idBool' : Bool → Bool
idBool' = λ (x : Bool) → x

-- The following is a Π type
id' : (X : Type) → X → X
id' X x = x

-- Implicit
id : {X : Type} → X → X
id x = x

idBool'' : Bool → Bool
idBool'' = id' _

-- "propositions as types" "mathematical statements as types"
example : {P Q : Type} → P → (Q → P)
example {P} {Q} p = f
 where
  f : Q → P
  f _ = p

example' : {P Q : Type} → P → (Q → P)
example' p = λ q → p

open import binary-products

-- "×" is "and" in propositions as types
ex : {P Q : Type} → P × Q → Q × P
ex (p , q) = (q , p)

-- \bN
data ℕ : Type where
 zero : ℕ
 suc  : ℕ → ℕ

three : ℕ
three = suc (suc (suc zero))

-- {-# BUILTIN NATURAL ℕ #-}
-- for technical reasons we can not have this binding here. however
-- there is already a type ℕ in the introduction module which /does/
-- support numeral notation. we are careful not to use the ℕ type
-- defined in this module again.

three' : introduction.ℕ
three' = 3 -- synonym for the above

D : Bool → Type
D true  = introduction.ℕ
D false = Bool

-- "mix-fix" operator (3rd sense of "_" in Agda)
--                           b      x   y
if_then_else_ : {X : Type} → Bool → X → X → X
if true  then x else y = x
if false then x else y = y

if[_]_then_else_ : (X : Bool → Type)
                 → (b : Bool)
                 → X true
                 → X false
                 → X b
if[ X ] true then  x else y = x
if[ X ] false then x else y = y

-- Π (b : Bool), D b
unfamiliar : (b : Bool) → D b
unfamiliar b = if[ D ] b then 3 else false

data List (A : Type) : Type where
 []   : List A  -- empty list
 _::_ : A → List A → List A -- if xs is a list then x :: xs is list

infixr 10 _::_

ff : Type → Type
ff = List

sample-list₀ : List introduction.ℕ
sample-list₀ = 0 :: 1 :: 2 :: []

length : {X : Type} → List X → ℕ
length []        = zero
length (x :: xs) = suc (length xs)

-- Principle of induction on ℕ
ℕ-elim : {A : ℕ → Type}
       → A zero -- base case
       → ((k : ℕ) → A k → A (suc k)) -- induction step
       → (n : ℕ) → A n
ℕ-elim {A} a₀ f = h
 where
  h : (n : ℕ) → A n
  h zero    = a₀
  h (suc n) = f n IH
   where
    IH : A n
    IH = h n

ℕ-elim' : {A : ℕ → Type}
       → A zero -- base case
       → ((k : ℕ) → A k → A (suc k)) -- induction step
       → (n : ℕ) → A n
ℕ-elim' {A} a₀ f zero    = a₀
ℕ-elim' {A} a₀ f (suc n) = f n (ℕ-elim' a₀ f n)

List-elim : {X : Type} (A : List X → Type)
          → A [] -- base
          → ((x : X) (xs : List X) → A xs → A (x :: xs)) -- step
          → (xs : List X) → A xs
List-elim {X} A a f = h
 where
  h : (xs : List X) → A xs
  h []        = a -- base
  h (x :: xs) = f x xs (h xs) -- step

-- \b0
data 𝟘 : Type where

-- \b1
data 𝟙 : Type where
 ⋆ : 𝟙  -- \star

_≣_ : ℕ → ℕ → Type
zero  ≣ zero  = 𝟙
zero  ≣ suc y = 𝟘
suc x ≣ zero  = 𝟘
suc x ≣ suc y = x ≣ y

infix 0 _≣_

ℕ-refl : (x : ℕ) → x ≣ x
ℕ-refl zero    = ⋆
ℕ-refl (suc x) = ℕ-refl x

_+_ : ℕ → ℕ → ℕ
zero  + y = y
suc x + y = suc (x + y)

infixr 20 _+_

_++_ : {A : Type} → List A → List A → List A
[]        ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)

lh : {X : Type} (xs ys : List X)
   → length (xs ++ ys) ≣ length xs + length ys
lh [] ys        = ℕ-refl (length ys)
lh (x :: xs) ys = IH
 where
  IH : length (xs ++ ys) ≣ (length xs + length ys)
  IH = lh xs ys