{-# OPTIONS --allow-unsolved-metas #-}
{-
  AGDA IN PADOVA 2024
  Exercise sheet 1

  ┌─ SHORTCUTS ───────────────────────┐    ┌─ BACKSLASH CHARACTERS ─┐
  │ C-c C-l   = load file             │    │ \bN    = ℕ             │
  │ C-c C-c   = case split            │    │ \alpha = α             │
  │ C-c C-SPC = check hole            │    │ \to    = →             │
  │ C-c C-,   = see context           │    │ \cdot  = ·             │
  │ C-c C-.   = see context and goal  │    │ \::    = ∷             │
  │ C-c C-d   = display type          │    │ \==    = ≡             │
  │ C-c C-v   = evaluate expression   │    └────────────────────────┘
  │ C-z       = enable Vi keybindings │    Use M-x describe-char to
  │ C-x C-+   = increase font size    │    lookup input method for
  └───────────────────────────────────┘    symbol under cursor.

  You can open this file in an Agdapad session by pressing C-x C-f ("find file")
  and then entering the path to this file: ~/Padova2024/exercise01.agda.
  As this file is read-only, you can then save a copy of this file to your personal
  directory and edit that one: File > Save As... For instance, you can save this file
  as ~/solutions01.agda.
-}


-- ────────────────────
-- ────[ BOOLEANS ]────
-- ────────────────────

data Bool : Set where
  false : Bool
  true  : Bool

_&&_ : Bool  Bool  Bool
false && b     = false
true  && false = false
true  && true  = true

-- EXERCISE: Implement boolean "or".
-- "false || true" should evaluate to "true".
-- "true  || true" should evaluate to "true".
_||_ : Bool  Bool  Bool
a || b = {!!}

-- EXERCISE: Implement a function "is-tautology₁?" which checks whether
-- a given input function is constantly true. For instance, if f x = x,
-- then "is-tautology₁? f" should evaluate to "false".
is-tautology₁? : (Bool  Bool)  Bool
is-tautology₁? f = f false && f true

-- EXERCISE: Implement a function "is-tautology₂?" which checks whether
-- a given input function of two arguments is constantly true. For
-- instance, if f x y = true, then "is-tautology₂ f" should evaluate to "true".
is-tautology₂ : (Bool  Bool  Bool)  Bool
is-tautology₂ f = {!!}


-- ───────────────────────────────────────
-- ────[ NATURAL NUMBERS PROGRAMMING ]────
-- ───────────────────────────────────────

data  : Set where
  zero : 
  succ :   

_+_ :     
zero   + b = b
succ a + b = succ (a + b)

-- EXERCISE: Define the function "half" which divides its input by two.
-- For instance "half (succ (succ (succ (succ zero))))" should be "succ (succ zero)"
-- and "half (succ (succ (succ zero)))" should be "succ zero" (so we round down).
half :   
half zero            = zero
half (succ zero)     = zero
half (succ (succ n)) = succ (half n)

-- EXERCISE: Define (cut-off) subtraction. For instance "succ zero - succ zero"
-- and "succ zero - succ (succ zero)" should both be "zero".
_-_ :     
a - b = {!!}

-- EXERCISE: Define multiplication and exponentiation.

-- EXERCISE: Define a function "max" which returns the maximum of two inputs.
-- For instance "max (succ zero) zero" should be "succ zero".

-- EXERCISE: Define a function "eq?" which determines whether its two
-- input numbers are equal. For instance, "eq? zero zero" should evaluate
-- to "true" while "eq? zero (succ zero)" should evaluate to "false".
eq? :     Bool
eq? a b = {!!}

-- EXERCISE: Define a function "≤?" which determines whether its first
-- argument is less than or equal to its second. For instance, "≤?
-- zero zero" should evaluate to "true" while "≤? (succ zero) zero"
-- should evaluate to "false".
≤? :     Bool
≤? a b = {!!}

{-
-- EXERCISE: Define a function "even?" which determines whether its input is even.
-- For instance, "even? zero" and "even? (succ (succ zero))" should both evaluate to "true",
-- while "even? (succ zero)" should evaluate to "false".
even? : ℕ → Bool
even? n = {!!}
-}

-- EXERCISE: Define a function "odd?" which determines whether its input is odd.
-- You may use the "even?" function from before.
odd? :   Bool
odd? n = {!!}


-- ─────────────────
-- ────[ TYPES ]────
-- ─────────────────

-- EXERCISE: Describe the following type in simple terms. What are its values?
data Weird : Set where
  foo : Weird  Weird
-- This type doesn't have any inhabitants at all, because the only
-- constructor, "foo", is not useful.

ex : Weird
ex = foo (foo (foo (foo (foo {!!}))))

{-
loop : Weird
loop = foo loop
-}

id : Weird  Weird
id x = x

-- EXERCISE: Define a manifestly empty type called "Empty".
data Empty : Set where
-- No constructors, so no ways to construct inhabitants of type "Empty",
-- so the type "Empty" does not contain any inhabitants.

-- Can you define a function Empty → ℕ?

from-empty : Empty  
from-empty x = zero

from-empty' : Empty  
from-empty' x = succ (succ zero)

from-empty'' : Empty  
from-empty'' ()

-- Can you define a function in the other direction, so ℕ → Empty?

to-empty :   Empty
to-empty x = {!!}  -- this hole cannot be filled

to-empty' :   Empty
to-empty' zero     = {!()!}
to-empty' (succ x) = {!()!}

unicorn : Empty  Empty
unicorn x = x

{-
  To summarize: There is a function of type X → Empty only in the case that X doesn't
  contain any inhabitants.
-}

riddle : Weird  Empty
riddle (foo x) = riddle x

-- EXERCISE: Write a function "Endo" which takes as input a type X and returns
-- as output the type of functions X → X.
Endo : Set  Set
Endo X = X  X

Bar : Set  Set
Bar X = 

Baz :   Set
Baz zero            = Bool
Baz (succ zero)     = 
Baz (succ (succ n)) = Bool  

grtz : Baz (succ zero)
grtz = succ (succ (succ (succ (succ zero))))


-- ─────────────────────────────────────────────
-- ────[ FIRST PROOFS WITH NATURAL NUMBERS ]────
-- ─────────────────────────────────────────────

-- The following function determines whether a given number is zero.
is-zero? :   Bool
is-zero? zero     = true
is-zero? (succ n) = false

-- For every natural number "n", there should be a type "IsZero n"
-- of witnesses that "n" is zero. For instance, the type "IsZero (succ zero)"
-- should be empty, because there shouldn't be any witnesses that "succ zero"
-- is zero. But the type "IsZero zero" should be inhabited.
data IsZero :   Set where
  case-zero : IsZero zero
-- In contrast, the function IsZero : ℕ → Set does not carry out any (nontrivial) computation.
-- Instead, the function IsZero reads a number "n" as input and outputs the type
-- of witnesses that "n" is zero. For most inputs, the resulting output types will be empty.
-- For instance, the type "IsZero (succ zero)" will be empty.

-- For every number "n", the type "IsNonzero n" is the type of witnesses that "n" is nonzero.
data IsNonzero :   Set where
  case-succ : {n : }  IsNonzero (succ n)

data  : Set where

IsNonzero' :   Set
IsNonzero' n = IsZero n  
-- logical reading: a number "n" is nonzero if and only if the assumption that it would be zero
-- implies a contradiction.
-- computational reading: a witness of a number "n" being nonzero is a function which
-- reads a witness that "n" is zero as input and outputs a witness of a contradiction.

-- Proposition: The number zero is zero.
proposition : IsZero zero
proposition = case-zero

-- Theorem: If a number "n" is zero, then it is zero.
-- Logical reading: For every number n, if n is zero, then n is zero.
-- Type-theoretic reading: "first-theorem" is a function which reads two inputs,
-- namely a number "n" and a witness that "n" is zero, and outputs a witness that "n" is zero.
first-theorem : (n : )  IsZero n  IsZero n
first-theorem n p = {!!}

-- EXERCISE: Prove that the sum of two numbers, both of which are zero, is zero again.
-- Computational reading: "sum-zero" is a function which reads as input
-- (1) a natural number "x",
-- (2) a natural number "y",
-- (3) a witness that "x" is zero, and
-- (4) a witness that "y" is zero,
-- and produces as output a witness that "x + y" is zero.
sum-zero : (x : )  (y : )  IsZero x  IsZero y  IsZero (x + y)
sum-zero zero zero case-zero case-zero = case-zero
-- sum-zero zero zero case-zero case-zero = proposition
-- sum-zero zero y case-zero q = q

-- EXERCISE: State and prove: The sum of two numbers, the first of which is nonzero, is nonzero.
-- Computational reading: "sum-nonzero" is a function which reads as input
-- (1) a natural number "x",
-- (2) a natural number "y",
-- (3) a witness that "x" is nonzero, and
-- produces as output a witness that "x + y" is nonzero.
sum-nonzero : {x y : }  IsNonzero x  IsNonzero (x + y)
sum-nonzero case-succ = case-succ

-- EXERCISE: Prove that the (contradictory) assumption that zero is nonzero implies
-- the (also contradictory) statement that succ zero is zero.
zero-is-not-nonzero : IsNonzero zero  IsZero (succ zero)
zero-is-not-nonzero ()

-- computational reading: "absurd" is a function which reads as input a type "A"
-- and a witness of a contradiction and outputs a value of type "A".
-- logical reading: For every A, if contradiction, then A.
absurd : {A : Set}    A
absurd {A} ()

if-isnonzero-zero-then-bot : IsNonzero zero  
if-isnonzero-zero-then-bot ()

zero-is-not-nonzero' : IsNonzero zero  IsZero (succ zero)
zero-is-not-nonzero' p = absurd (if-isnonzero-zero-then-bot p)

-- For every number "n", the type "Even n" should be the type of witnesses that "n" is even.
data Even :   Set where
  base : Even zero
  step : {n : }  Even n  Even (succ (succ n))
  -- logical reading: For every number n, if n is even, then succ (succ n) is even.
  -- computational reading: "step" is a function which reads a number "n" and a witness that "n" is even
  -- as input and outputs a new, freshly-minted witness that "succ (succ n)" is even.

four-is-even : Even (succ (succ (succ (succ zero))))
four-is-even = step {succ (succ zero)} (step {zero} base)
-- four-is-even = step (step base)

even? :   Bool
even? zero            = true
even? (succ zero)     = false
even? (succ (succ n)) = even? n

!_ : Bool  Bool
! false = true
! true  = false

even?' :   Bool
even?' zero     = true
even?' (succ n) = ! (even?' n)

data Maybe (A : Set) : Set where
  nothing : Maybe A
  just    : A  Maybe A
-- For every type "A", there is a new type "Maybe A".
-- Its inhabitants are:
-- (1) "nothing"
-- (2) for every value "x" of type "A", we have the inhabitant "just x"

_/_ :     Maybe 
x / zero   = nothing
x / succ y = {!!}

even?'' : (n : )  Maybe (Even n)
even?'' zero            = just base
even?'' (succ zero)     = nothing
even?'' (succ (succ n)) with even?'' n
... | nothing   = nothing
... | just    p = just (step p)

-- EXERCISE: Show that the sum of even numbers is even.
lemma-sum-even : {a b : }  Even a  Even b  Even (a + b)
lemma-sum-even base     q = q
lemma-sum-even (step p) q = step (lemma-sum-even p q)

{-
lemma-+-zero : (a : ℕ) → (a + zero) ≡ a
lemma-+-zero a = ?
-}

lemma-sum-even' : {a b : }  Even a  Even b  Even (a + b)
lemma-sum-even' p base     = {!p!}
lemma-sum-even' p (step q) = {!!}