module Functions.Equality_Proofs where

Import List


open import Data.Nat using (; zero; suc; _≤_; z≤n; s≤s)
open import Data.Nat using (; zero; suc; _+_; _*_; _≤_; z≤n; s≤s)
open import Data.List using (List; []; _∷_; _++_)
open import Data.Unit using (; tt)
open import Data.Product using (_×_; _,_)
open import Function using (_$_)         -- 

Propositional equality: _≡_

Recall the definition of propositional equality:


data _≡_ {A : Set} (x : A) : A  Set  where
  refl : x  x

infix 4 _≡_

yields

refl {ℕ} {0} : 0 ≡ 0
refl {ℕ} {1} : 1 ≡ 1
refl {ℕ} {2} : 2 ≡ 2
...
refl {Bool} {true} : true ≡ true
...
refl {Bool} {not b} : not b ≡ not b   -- if 'b : Bool' is a parameter
...

_≡_ is an equivalence and a congruence

_≡_ is an equivalence-relation:


refl'  :  {A} (a : A)  a  a
refl' a = refl

sym   :  {A} {a b : A}  a  b  b  a
sym refl = refl   -- after pattern matching on 'refl', 'a' and 'b' coincides

Exercise


trans :  {A} {a b c : A}  a  b  b  c  a  c

Prove that every function is compatible with the equivalence relation (congruency):


cong :  {A B} {m n : A}  (f : A  B)  m  n  f m  f n

Automatic reduction of types

Agda reduces types automatically during type checking.
This is safe because all functions terminate.

Example:

1 + 1 ≡ 22 ≡ 2.

Consequences:

refl : 1 + 1 ≡ 2
...
refl : 0 + n ≡ n    -- if 'n : ℕ' is a parameter
...

Definitional and propositional equality

x and y are definitionally equal iff refl : x ≡ y.

"Definitional equality is trivial equality."

Example


1+1≡2 : 1 + 1  2
1+1≡2 = refl

Counterexample

Suppose that n : ℕ is a parameter.

refl : n + 0 ≡ 0 + n   -- type error!

This doesn't typecheck because
n + 0 ≡ 0 + nn + 0 ≡ n
and n + 0 is not the same as n.

Proof of a + 0 ≡ a


+-right-identity :  n  n + 0  n
+-right-identity zero    = refl
+-right-identity (suc n) = cong suc (+-right-identity n)

So n + 0 ≡ n is not trivial,
but for any n we can construct the proof of it.

This is a bit funny though:

+-right-identity 0   ⇓  refl
+-right-identity 1   ⇓  refl
+-right-identity 2   ⇓  refl
...

So the code of +-right-identity is kind of dead code (doesn't do anything meaningful at run time).
We'll discuss this later.

Exercises

Finish the ingredients of the proof that (, _+_) is a commutative monoid!


+-left-identity  :  a  0 + a  a


+-assoc          :  a b c  a + (b + c)  (a + b) + c -- hint: use cong

For commutativity you need a helper function first:


m+1+n≡1+m+n :  m n  m + suc n  suc (m + n)

Exercise: List ⊤ ~

Let


fromList : List   
fromList [] = zero
fromList (x  xs) = suc (fromList xs)

toList :   List 
toList zero = []
toList (suc n) = tt  toList n

Let's prove that fromList and toList are inverses of each other and that they preserve the operations _++_ and _+_!


from-to :  a  fromList (toList a)  a


to-from :  a  toList (fromList a)  a


fromPreserves++ :  (a b : List )  fromList (a ++ b)  fromList a + fromList b


toPreserves+ :  (a b : )  toList (a + b)  toList a ++ toList b

Equational reasoning

Equational reasoning is not a new language construct!


_≡⟨_⟩_ :  {A : Set} (x : A) {y z : A}  x  y  y  z  x  z
x ≡⟨ refl  refl = refl

infixr 2 _≡⟨_⟩_

_∎ :  {A : Set} (x : A)  x  x
x  = refl

infix  2 _∎

Usage example:


+-comm' : (n m : )  n + m  m + n
+-comm' zero    n = sym (+-right-identity n)
+-comm' (suc m) n =
      suc m + n
    ≡⟨ refl 
      suc (m + n)
    ≡⟨ cong suc (+-comm' m n) 
      suc (n + m)
    ≡⟨ sym (m+1+n≡1+m+n n m) 
      n + suc m
    

Properties of _*_

We'd like to prove that (, _*_) is a commutative monoid.

We will need distributivity:


distribʳ-*-+ :  a b c  (a + b) * c  a * c + b * c
distribʳ-*-+ zero b c = refl
distribʳ-*-+ (suc a) b c =
    c + (a + b) * c
  ≡⟨ cong  x  c + x) (distribʳ-*-+ a b c) 
    c + (a * c + b * c)
  ≡⟨ +-assoc c (a * c) (b * c) 
    c + a * c + b * c
  

Define the following functions:


*-assoc        :  a b c  a * (b * c)  (a * b) * c


*-left-identity  :  a  1 * a  a


*-right-identity :  a  a * 1  a

Commutativity:


-- helper functions:
n*0≡0 :  n  n * 0  0


*-suc :  n m  n + n * m  n * suc m


*-comm :  m n  m * n  n * m

Semiring solver


module trySemiringSolver where

open import Data.Nat
open import Data.Nat.Properties
open SemiringSolver
open import Relation.Binary.PropositionalEquality renaming (_≡_ to _≡-official_)

f :  a b c  a + b * c + 1 ≡-official 1 + c * b + a
f = solve 3  a b c  a :+ b :* c :+ con 1 := con 1 :+ c :* b :+ a) refl