module Types.equational where
open import Agda.Primitive using (Level; _⊔_; lsuc; lzero)
open import Types.equality renaming (cong-≡ to cong)
open import Types.product using (Σ; _,_; _×_)
Though we looked at the type theory way of constructively proving propositions, there is another way which we tend to be more familiar with: the way we solve equations on paper.
For example, consider the problem of proving a well known identity for natural numbers from linear algebra:
∀ a, b ∈ ℕ: (a+b)² ≡ a² + 2 * a * b + b²
(a + b) × (a + b)
Applying distributivity of + and × (a + b) * c ≡ (a × c) + (b × c)
= a × (a + b) + b × (a + b)
Applying distributivity again, this time from the left-hand side
= a × a + a × b + b × a + b²
Applying
1. The fact that a × a ≡ a²
2. Commutativity of multiplication: a × b ≡ b × a
3. Reflexivity of ≡ for a × b and b × a to add to 2 times
= a² + 2 * a * b + b²
Q.E.D.
Each step of such a solution essentially follows through the rule of transitivity of equality. Hence setp₂ = step₁ + actions₁ and so on. We can write an apparatus to let us do exactly that in Agda. That apparatus is “equational reasoning”. Here, we define the framework on top of equivalence relations:
module ≡-Reasoning {a} {A : Set a} where
open ≡-properties {A = A}
open IsEquivalence isEquivalence public
infix 3 _∎
infixr 2 _≡⟨⟩_ _≡⟨_⟩_
infix 1 begin_
-- This is just syntactical sugar to define the start of the proof
_ : ∀ {x y : A} → x ≡ y → x ≡ y
begin_ x≡y = x≡y
begin
-- Apply reflexivity, arguments required within the ⟨⟩
_≡⟨⟩_ : ∀ (x {y} : A) → x ≡ y → x ≡ y
_ ≡⟨⟩ x≡y = x≡y
-- Transitivity with arguments applied within the ⟨⟩
_≡⟨_⟩_ : ∀ (x {y z} : A) → x ≡ y → y ≡ z → x ≡ z
_ ≡⟨ x≡y ⟩ y≡z = trans x≡y y≡z
-- The QED or "hence, proved" marker to end the proof.
_∎ : ∀ (x : A) → x ≡ x
_∎ _ = rfl
In order to define \((a+b) × (a-b) ≡ a² - b²\) in Agda, we have to define some
properties of +
, -
and *
:
+
over ×
from the right side+
over ×
from the left side×
≡
Since we are proving this literally from scratch, we have to prove quite a few basic constructions:
module Properties {a} {A : Set a} where
open ≡-Reasoning
We start with the type of natural numbers:
data ℕ : Set where
: ℕ
zero : ℕ → ℕ succ
We then proceed to define the required operations on natural numbers:
infixl 6 _+_ _−_
infixl 7 _*_
_+_ : ℕ → ℕ → ℕ
= m
m + zero = succ (m + n)
m + succ n
_−_ : ℕ → ℕ → ℕ
= zero
zero − m = succ n
succ n − zero = n − m
succ n − succ m
_*_ : ℕ → ℕ → ℕ
= zero
zero * x (succ x) * y = y + (x * y)
_² : ℕ → ℕ
= x * x x ²
We now prove properties of addition, upto commutativity:
= succ zero
one = succ (succ zero)
two
: ∀ x y → x + (succ y) ≡ succ (x + y)
+-succ-l = refl
+-succ-l zero n (succ m) n = refl
+-succ-l
: ∀ x y → (succ x) + y ≡ succ (x + y)
+-succ-r = refl
+-succ-r n zero (succ n) = cong succ (+-succ-r m n)
+-succ-r m
: ∀ x y → succ (x + y) ≡ x + (succ y)
+-unsucc-l = refl
+-unsucc-l n zero (succ m) = refl
+-unsucc-l n
: ∀ x y → succ (x + y) ≡ (succ x) + y
+-unsucc-r = refl
+-unsucc-r m zero (succ n) = cong succ (+-unsucc-r m n)
+-unsucc-r m
: ∀ x y z → ((x + y) + z) ≡ (x + (y + z))
+-assoc-l = refl
+-assoc-l l m zero (succ n) = cong succ (+-assoc-l l m n)
+-assoc-l l m
: ∀ x y z → (x + (y + z)) ≡ ((x + y) + z)
+-assoc-r = refl
+-assoc-r l m zero (succ n) = cong succ (+-assoc-r l m n)
+-assoc-r l m
: ∀ x → x + zero ≡ x
+-identity-l _ = refl
+-identity-l
: ∀ x → zero + x ≡ x
+-identity-r = refl
+-identity-r zero (succ n) = cong succ (+-identity-r n)
+-identity-r
: ∀ x → (x + zero ≡ x) × (zero + x ≡ x)
+-identity = (+-identity-l n) , (+-identity-r n)
+-identity n
-- we finally use equational reasoning to prove this bit
: ∀ x y → (x + y) ≡ (y + x)
+-comm = +-identity-r n
+-comm zero n (succ m) n = begin
+-comm (succ m) + n ≡⟨ +-succ-r m n ⟩
(m + n) ≡⟨ cong succ (+-comm m n) ⟩
succ (n + m) ≡⟨ refl ⟩
succ n + succ m ∎
As we saw, we had to prove quite a few propositions in their right and left handed versions. These will come in handy later, in fact some of them have been written while trying to prove later propositions. This highlights a central way of solving mathematics - proofs are broken up into pieces and each piece is solved separately. Mostly such smaller proofs tend to be inductively proved and used to prove more complex proofs using equational reasoning.
We now proceed to prove some propositions for multiplication:
: ∀ x y → succ x * y ≡ y + (x * y)
*-succ-l = refl
*-succ-l m zero (succ n) = refl
*-succ-l m
: ∀ x y → x * succ y ≡ x + (x * y)
*-succ-r = refl
*-succ-r zero m (succ m) n = begin
*-succ-r
succ m * succ n ≡⟨⟩(_+_ (succ n)) (*-succ-r m n) ⟩
succ n + m * succ n ≡⟨ cong (m + m * n) ≡⟨ +-succ-r n (m + (m * n)) ⟩
succ n + (n + (m + m * n)) ≡⟨⟩
succ (n + (m + (m * n))) ≡⟨ cong succ (+-assoc-r n m (m * n)) ⟩
succ (n + m + m * n) ≡⟨ cong (λ x → succ (x + m * n)) (+-comm n m) ⟩
succ ((m + n) + m * n) ≡⟨ cong succ (+-assoc-l m n (m * n)) ⟩
succ (m + (n + m * n)) ≡⟨ +-unsucc-r m (n + m * n) ⟩
succ (succ m) + (n + m * n) ≡⟨⟩
succ m + succ m * n ∎
: ∀ x → x * zero ≡ zero
*-zero-r = refl
*-zero-r zero (succ n) = begin
*-zero-r (succ n) * zero ≡⟨⟩
(n * zero) ⟩
zero + n * zero ≡⟨ +-identity-r
n * zero ≡⟨ *-zero-r n ⟩
zero ∎
: ∀ x → zero * x ≡ zero
*-zero-l = refl
*-zero-l zero (succ n) = refl
*-zero-l
: ∀ x → one * x ≡ x
*-identity-l = refl
*-identity-l zero (succ n) = refl
*-identity-l
: ∀ x y → x * y ≡ y * x
*-comm = *-zero-r m
*-comm m zero (succ n) = begin
*-comm m (succ n) ≡⟨ *-succ-r m n ⟩
m * (m +_) (*-comm m n) ⟩
m + m * n ≡⟨ cong (*-succ-l n m) ⟩
m + n * m ≡⟨ sym (succ n) * m ∎
: ∀ x → x * one ≡ x
*-identity-r = refl
*-identity-r zero (succ n) = begin
*-identity-r (succ n) * one ≡⟨⟩
(_+_ one) (*-identity-r n) ⟩
one + n * one ≡⟨ cong (succ zero) + n ≡⟨ +-succ-r zero n ⟩
(zero + n) ≡⟨ cong (λ x → succ x) (+-comm zero n) ⟩
succ (n + zero) ≡⟨⟩
succ
succ n ∎
: ∀ x y → x * succ y ≡ (x * y) + x
*-succ-r-r = refl
*-succ-r-r zero m (succ m) n = begin
*-succ-r-r (succ m) n ⟩
succ m * succ n ≡⟨ *-succ-r (succ m) (succ m * n) ⟩
succ m + succ m * n ≡⟨ +-comm succ m * n + succ m ∎
Here we see a noteworthy pattern,
To prove for example a + (b * c) + d ≡ a + (c * b) + d
, we have to apply commutativity only to
(b * c)
only while keeping the rest as it is. This is often achieved by using congruence over lambda
pattern matching expressions in agda like cong (λ x a + x + d) (*-comm b c)
. Or in other words,
+ (b * c) + d ≡⟨ cong (λ x a + x + d) (*-comm b c) ⟩
a + (c * b) + d ∎ a
Note that the above proofs might not be the best way to prove these propositions.
We now prove the distributive property of *
over +
:
: ∀ x y z → x * (y + z) ≡ (x * y) + (x * z)
+-distrib-*-r = begin
+-distrib-*-r l m zero (m + zero) ≡⟨⟩
l *
l * m ≡⟨⟩(l * m) + zero ≡⟨ cong ((l * m) +_) (sym (*-zero-r l)) ⟩
(l * m) + (l * zero) ∎
--------------------------------------------------------
(succ n) = begin
+-distrib-*-r l m (m + (succ n)) ≡⟨⟩
l * (m + n) ≡⟨ *-succ-r l (m + n) ⟩
l * succ (m + n) ≡⟨ +-comm l (l * (m + n)) ⟩
l + l * (m + n) + l ≡⟨ cong (λ z → z + l) (+-distrib-*-r l m n) ⟩
l * ((l * m) + (l * n)) + l ≡⟨ +-assoc-l (l * m) (l * n) l ⟩
(l * m) + ((l * n) + l) ≡⟨ cong ((l * m) +_) (sym (*-succ-r-r l n)) ⟩
(l * m) + l * (succ n) ∎
: ∀ x y z → (x + y) * z ≡ (x * z) + (y * z)
+-distrib-*-l = begin
+-distrib-*-l zero m n (zero + m) * n ≡⟨ *-comm (zero + m) n ⟩
(zero + m) ≡⟨ +-distrib-*-r n zero m ⟩
n * (n * zero) + (n * m) ≡⟨ cong (λ x → x + (n * m)) (*-comm n zero) ⟩
(zero * n) + (n * m) ≡⟨ cong (λ x → (zero * n) + x) (*-comm n m) ⟩
(zero * n) + (m * n) ∎
--------------------------------------------------------
(succ l) m n = begin
+-distrib-*-l ((succ l) + m) * n ≡⟨ cong (λ x → x * n) (+-succ-r l m) ⟩
(l + m) * n ≡⟨⟩
succ ((l + m) * n) ≡⟨ cong (_+_ n) (+-distrib-*-l l m n) ⟩
n + (l * n + m * n) ≡⟨ +-assoc-r n (l * n) (m * n) ⟩
n + (n + l * n) + (m * n) ≡⟨⟩
(succ l) * n + m * n ∎
And now, finally the proof of our original proposition:
: ∀ x y → (x + y) ² ≡ (x ² + two * (x * y) + y ²)
proof --------------------------------------------------------
= begin
proof zero n (zero + n) * (zero + n)
(λ x → (zero + n) * x) (+-identity-r n) ⟩
≡⟨ cong (zero + n) * n
(λ x → x * n) (+-identity-r n) ⟩
≡⟨ cong
n * n
≡⟨⟩
n ²
≡⟨⟩
n ² + zero((n ²) +_) (sym (*-zero-r zero)) ⟩
≡⟨ cong (zero * zero)
n ² +
≡⟨⟩
n ² + zero ²(n ²) (zero ²) ⟩
≡⟨ +-comm (zero ² + n ²)
≡⟨⟩(zero ² + n ²) + zero
((zero ² + n ²) +_) (sym (*-zero-r (two))) ⟩
≡⟨ cong (zero ² + n ²) + (two * zero)
≡⟨⟩(zero ² + n ²) + (two * zero * n)
(zero ²) (n ²) (two * zero * n) ⟩
≡⟨ +-assoc-l (n ² + (two * zero * n))
zero ² + (λ x → zero ² + x) (+-comm (n ²) (two * zero * n)) ⟩
≡⟨ cong ((two * zero * n) + n ²)
zero ² + (zero ²) (two * zero * n) (n ²) ⟩
≡⟨ +-assoc-r
zero ² + two * zero * n + n ²
∎--------------------------------------------------------
(succ m) n = begin
proof ((succ m) + n) * ((succ m) + n)
(succ m) n (succ m + n) ⟩
≡⟨ +-distrib-*-l (succ m) * (succ m + n) + n * (succ m + n)
(λ x → x + n * (succ m + n)) (+-distrib-*-r (succ m) (succ m) n) ⟩
≡⟨ cong (succ m) * (succ m) + (succ m) * n + n * (succ m + n)
(λ x → (succ m) * (succ m) + (succ m) * n + x) (+-distrib-*-r n (succ m) n) ⟩
≡⟨ cong (succ m) * (succ m) + (succ m) * n + (n * (succ m) + n * n)
((succ m) * (succ m) + (succ m) * n) (n * (succ m)) (n * n) ⟩
≡⟨ +-assoc-r (succ m) * (succ m) + (succ m) * n + n * (succ m) + n * n
(λ x → (succ m) * (succ m) + (succ m) * n + x + n * n) (*-comm n (succ m)) ⟩
≡⟨ cong (succ m) * (succ m) + (succ m) * n + (succ m) * n + n * n
≡⟨⟩(succ m) ² + (succ m) * n + (succ m) * n + n * n
≡⟨⟩((succ m) ² + (succ m) * n) + (succ m) * n + n ²
(_+ n ²) (+-assoc-l ((succ m) ²) ((succ m) * n) ((succ m) * n)) ⟩
≡⟨ cong (succ m) ² + (succ m * n + succ m * n) + n ²
(λ x → (succ m) ² + x + n ²) (sym (*-identity-r ((succ m) * n + (succ m) * n))) ⟩
≡⟨ cong (succ m) ² + ( (succ m * n) + (succ m * n) ) * one + n ²
≡⟨ refl ⟩(succ m) ² + ((succ m * n) + (succ m * n)) * one + n ²
(λ x → (succ m) ² + x + n ²) (+-distrib-*-l (succ m * n) (succ m * n) one) ⟩
≡⟨ cong (succ m) ² + ((succ m * n) * one + (succ m * n) * one) + n ²
(λ x → (succ m) ² + x + n ²) (sym (+-distrib-*-r (succ m * n) one one)) ⟩
≡⟨ cong (succ m) ² + ((succ m * n) * (one + one)) + n ²
≡⟨⟩(succ m) ² + ((succ m * n) * two) + n ²
(λ x → (succ m) ² + x + n ²) (*-comm (succ m * n) two) ⟩
≡⟨ cong (succ m) ² + two * ((succ m) * n) + n ²
∎