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

module lecture2 where

-- lecture 2
-- Plan: basic MLTT types, including their elimination principles.
--

open import lecture1 hiding (𝟘 ; πŸ™ ; D ; β„• ; _+_)
open import introduction using (β„• ; zero ; suc ; _+_)

-- empty type
data 𝟘 : Type where

-- Ξ  x κž‰ X , A x
-- (X β†’ B) =. Ξ  x κž‰ X , B

𝟘-elim : {A : 𝟘 β†’ Type} (x : 𝟘) β†’ A x
𝟘-elim ()

-- 𝟘 interpreted as "false"

Β¬_ : Type β†’ Type
Β¬ A = A β†’ 𝟘

infix 1000 Β¬_

𝟘-nondep-elim : {B : Type} β†’ 𝟘 β†’ B
𝟘-nondep-elim {B} = 𝟘-elim {Ξ» _ β†’ B}

-- A := Ξ» _ β†’ B


is-empty : Type β†’ Type
is-empty A = A β†’ 𝟘

-- A ≑ 𝟘 or A ≃ 𝟘.

𝟘-is-empty'' : is-empty 𝟘
𝟘-is-empty'' = λ ()

𝟘-is-empty : is-empty 𝟘
𝟘-is-empty = 𝟘-nondep-elim

𝟘-is-empty' : is-empty 𝟘
𝟘-is-empty' = id

-- Unit type:

-- Record definitions satisfy a certain "Ξ·" rule.

record πŸ™ : Type where
 constructor
  ⋆

open πŸ™ public

πŸ™-is-nonempty' : Β¬ is-empty πŸ™
πŸ™-is-nonempty' = Ξ» (f : πŸ™ β†’ 𝟘) β†’ f ⋆

πŸ™-is-nonempty : Β¬ is-empty πŸ™
πŸ™-is-nonempty f = f ⋆

πŸ™-elim : {A : πŸ™ β†’ Type}
       β†’ A ⋆
       β†’ (x : πŸ™) β†’ A x
πŸ™-elim a x = a

πŸ™-nondep-elim : {A : Type}
              β†’ A
              β†’ πŸ™ β†’ A
πŸ™-nondep-elim {A} = πŸ™-elim {Ξ» _ β†’ A}

-- Type of binary digits:

data 𝟚 : Type where
 𝟎 𝟏 : 𝟚

𝟚-elim : {A : 𝟚 β†’ Type}
       β†’ A 𝟎
       β†’ A 𝟏
       β†’ (x : 𝟚) β†’ A x
𝟚-elim aβ‚€ a₁ 𝟎 = aβ‚€
𝟚-elim aβ‚€ a₁ 𝟏 = a₁

𝟚-nondep-elim : {A : Type}
              β†’ A
              β†’ A
              β†’ 𝟚 β†’ A
𝟚-nondep-elim {A} = 𝟚-elim {Ξ» _ β†’ A}

-- Ξ  types in Agda are primitive.
--
-- We have that Ξ  x κž‰ X , A x is written
--
--              (x : X) β†’ A x,   or
--              βˆ€ (x : X) β†’ A x, or
--              βˆ€ x β†’ A x        (if Agda can infer X).
--
-- We can introduce Ξ -syntax if we wish:

Pi : (A : Type) (B : A β†’ Type) β†’ Type
Pi A B = (x : A) β†’ B x

syntax Pi A (Ξ» x β†’ b) = Ξ  x κž‰ A , b
--                          ↑
--                         this is typed "\:4" in emacs mode and is not the same as ":".
--                         (we can't use the normal one unfortunately.)


-- Function composition.

-- The usual one found in mathematics:

module _ where
 private
  _∘_ : {A B C : Type} β†’ (B β†’ C) β†’ (A β†’ B) β†’ (A β†’ C)
  (g ∘ f) x = g (f x)

-- A more general version:

_∘_ : {A B : Type} {C : B β†’ Type}
    β†’ ((y : B) β†’ C y)
    β†’ (f : A β†’ B)
    β†’ (x : A) β†’ C (f x)
(g ∘ f) x = g (f x)

-- The types-as-mathematical-statements reading of dependent function composition is:
--
-- If (for all y : B we have that C y) and f : A β†’ B is any function, then
-- for all x : A we have that C (f x).
--
-- The proof is function composition.


-- Ξ£-types:

module _ where
 private

  data Ξ£ {A : Type } (B : A β†’ Type) : Type  where
   _,_ : (x : A) (y : B x) β†’ Ξ£ {A} B

  pr₁ : {A : Type} {B : A β†’ Type} β†’ Ξ£ B β†’ A
  pr₁ (x , y) = x

  prβ‚‚ : {A : Type} {B : A β†’ Type} β†’ (z : Ξ£ B) β†’ B (pr₁ z)
  prβ‚‚ (x , y) = y

-- Our preferred definition:

record Ξ£ {A : Type } (B : A β†’ Type) : Type  where
 constructor
  _,_
 field
  pr₁ : A
  prβ‚‚ : B pr₁

open Ξ£ public
infixr 0 _,_

pr₁-again : {A : Type} {B : A β†’ Type} β†’ Ξ£ B β†’ A
pr₁-again = pr₁

prβ‚‚-again : {A : Type} {B : A β†’ Type} ((x , y) : Ξ£ B) β†’ B x
prβ‚‚-again = prβ‚‚


-- This satisfies the Ξ·-rule z = (pr₁ z , prβ‚‚ z), which the definition using `data` doesn't.


Sigma : (A : Type) (B : A β†’ Type) β†’ Type
Sigma A B = Ξ£ {A} B

syntax Sigma A (Ξ» x β†’ b) = Ξ£ x κž‰ A , b

infix -1 Sigma

-- Recall that we defined D as follows in the first lecture:

D : Bool β†’ Type
D true  = β„•
D false = Bool

-- Example

Ξ£-example₁ Ξ£-exampleβ‚‚ : Ξ£ b κž‰ Bool , D b
Ξ£-example₁ = (true  , 17)
Ξ£-exampleβ‚‚ = (false , true)

-- Ξ£-elim is "curry":

Ξ£-elim : {A : Type } {B : A β†’ Type} {C : (Ξ£ x κž‰ A , B x) β†’ Type}
       β†’ ((x : A) (y : B x) β†’ C (x , y))
       β†’ (z : Ξ£ x κž‰ A , B x) β†’ C z
Ξ£-elim f (x , y) = f x y

Ξ£-uncurry : {A : Type } {B : A β†’ Type} {C : (Ξ£ x κž‰ A , B x) β†’ Type}
          β†’ ((z : Ξ£ x κž‰ A , B x) β†’ C z)
          β†’ (x : A) (y : B x) β†’ C (x , y)
Ξ£-uncurry g x y = g (x , y)

_Γ—_ : Type β†’ Type β†’ Type
A Γ— B = Ξ£ x κž‰ A , B

-- (x : X) β†’ A x
-- (x : X) Γ— A x

infixr 2 _Γ—_

-- We will have that Aβ‚€ Γ— A₁ β‰… Ξ  (n : 𝟚) , A n β‰… ((n : 𝟚) β†’ A n)
-- where A 𝟎 = Aβ‚€
--       A 𝟏 = A₁
--       A : 𝟚 β†’ Type
-- f ↦ (f 𝟎 , f 𝟏)
-- (aβ‚€ , a₁) ↦ 𝟚-elim aβ‚€ a₁
-- But we need function extensionality to prove that this works.
-- Binary products are special cases of products.

Various uses of Ξ£:

-- Binary sums _+_ βˆ”

data _βˆ”_ (A B : Type) : Type where
 inl : A β†’ A βˆ” B
 inr : B β†’ A βˆ” B

-- Mathematically A βˆ” B is (disjoint) union.
-- Logically, it is "or" (disjunction).
-- βˆ₯ A βˆ” B βˆ₯.

infixr 20 _βˆ”_

βˆ”-elim : {A B : Type} (C : A βˆ” B β†’ Type)
       β†’ ((x : A) β†’ C (inl x)) -- f
       β†’ ((y : B) β†’ C (inr y)) -- g
       β†’ (z : A βˆ” B) β†’ C z
βˆ”-elim C f g (inl x) = f x
βˆ”-elim C f g (inr y) = g y

βˆ”-nondep-elim : {A B C : Type}
              β†’ (A β†’ C)
              β†’ (B β†’ C)
              β†’ (A βˆ” B β†’ C)
βˆ”-nondep-elim {A} {B} {C} = βˆ”-elim (Ξ» z β†’ C)

-- We will have that Aβ‚€ βˆ” A₁ β‰… Ξ£ (n : 𝟚) , A n
-- where A 𝟎 = Aβ‚€
--       A 𝟏 = A₁
-- inl aβ‚€ ↦ (𝟎 , aβ‚€)
-- inr a₁ ↦ (𝟏 , a₁)
-- Binary sums are special cases of sums.


-- We call an element of the identity type x ≑ y an
-- "identification". The terminology "path" is also used.
-- I prefer the former.

data _≑_ {A : Type} : A β†’ A β†’ Type where
 refl : (x : A) β†’ x ≑ x

-- refl x : proof that x is equal to itself.

infix 0 _≑_

-- The following is also called "J":

≑-elim : {X : Type} (A : (x y : X) β†’ x ≑ y β†’ Type)
       β†’ ((x : X) β†’ A x x (refl x))
       β†’ (x y : X) (p : x ≑ y) β†’ A x y p
≑-elim A f x x (refl x) = f x

-- To conclude that a property A x y p of identifications p of
-- elements x and y holds for all x, y and p, it is enough to show
-- that A x x (refl x) holds for all x.

≑-nondep-elim : {X : Type} (A : X β†’ X β†’ Type)
              β†’ ((x : X) β†’ A x x)
              β†’ (x y : X) β†’ x ≑ y β†’ A x y
≑-nondep-elim A = ≑-elim (Ξ» x y _ β†’ A x y)

-- We finished lecture 2 here. So we'll start lecture 3 here.

trans : {A : Type} {x y z : A} β†’ x ≑ y β†’ y ≑ z β†’ x ≑ z
trans p (refl y) = p

sym : {A : Type} {x y : A} β†’ x ≑ y β†’ y ≑ x
sym (refl x) = refl x

ap : {A B : Type} (f : A β†’ B) {x y : A} β†’ x ≑ y β†’ f x ≑ f y
ap f (refl x) = refl (f x)

apβ‚‚ : {A B C : Type} (f : A β†’ B β†’ C) {x x' : A} {y y' : B}
    β†’ x ≑ x' β†’ y ≑ y' β†’ f x y ≑ f x' y'
apβ‚‚ f (refl x) (refl y) = refl (f x y)

transport : {X : Type} (A : X β†’ Type)
          β†’ {x y : X} β†’ x ≑ y β†’ A x β†’ A y
transport A (refl x) a = a

_βˆ™_ : {A : Type} {x y z : A} β†’ x ≑ y β†’ y ≑ z β†’ x ≑ z
_βˆ™_ = trans

infixl 7 _βˆ™_

_⁻¹ : {A : Type} {x y : A} β†’ x ≑ y β†’ y ≑ x
_⁻¹ = sym

infix  40 _⁻¹

_≀_ : β„• β†’ β„• β†’ Type
0     ≀ y     = πŸ™
suc x ≀ 0     = 𝟘
suc x ≀ suc y = x ≀ y

_β‰₯_ : β„• β†’ β„• β†’ Type
x β‰₯ y = y ≀ x

_*_ : β„• β†’ β„• β†’ β„•
0     * y = 0
suc x * y = x * y + y

infixr 30 _*_

_divides_ : β„• β†’ β„• β†’ Type
x divides y = Ξ£ z κž‰ β„• , x * z ≑ y

is-prime : β„• β†’ Type
is-prime p = (p β‰₯ 2) Γ— ((n : β„•) β†’ n divides p β†’ (n ≑ 1) βˆ” (n ≑ p))


twin-prime-conjecture : Type
twin-prime-conjecture = (n : β„•) β†’ Ξ£ p κž‰ β„• , (p β‰₯ n)
                                          Γ— is-prime p
                                          Γ— is-prime (p + 2)

there-are-infinitely-many-primes : Type
there-are-infinitely-many-primes = (n : β„•) β†’ Ξ£ p κž‰ β„• , (p β‰₯ n) Γ— is-prime p