module Data.Nat.Base where
open import Prim.Data.Nat hiding (_<_) public
Natural numbers🔗
The natural numbers are the inductive type generated by zero
and closed under taking suc
cessors. Thus, they satisfy the
following induction principle, which is familiar:
: ∀ {ℓ} (P : Nat → Type ℓ)
Nat-elim → P 0
→ ({n : Nat} → P n → P (suc n))
→ (n : Nat) → P n
= pz
Nat-elim P pz ps zero (suc n) = ps (Nat-elim P pz ps n)
Nat-elim P pz ps
: ∀ {ℓ} {A : Type ℓ} → Nat → (A → A) → A → A
iter = id
iter zero f (suc n) f = f ∘ iter n f iter
Translating from type theoretic notation to mathematical English, the
type of Nat-elim
says that if a
predicate P
holds of zero
, and the truth of
P(suc n)
follows from P(n)
, then
P
is true for every natural number.
Discreteness🔗
An interesting property of the natural numbers, type-theoretically,
is that they are discrete
: given
any pair of natural numbers, there is an algorithm that can tell you
whether or not they are equal. First, observe that we can distinguish
zero from successor:
: {n : Nat} → ¬ zero ≡ suc n
zero≠suc = subst distinguish path tt where
zero≠suc path : Nat → Type
distinguish = ⊤
distinguish zero (suc x) = ⊥ distinguish
: {n : Nat} → ¬ suc n ≡ zero
suc≠zero = zero≠suc ∘ sym suc≠zero
The idea behind this proof is that we can write a predicate which is
true
for zero, and
false
for any
successor. Since we know that ⊤
is
inhabited (by tt
), we can transport
that along the claimed path to get an inhabitant of ⊥
, i.e., a contradiction.
: Nat → Nat
pred 0 = 0
pred (suc n) = n
pred
: {x y : Nat} → suc x ≡ suc y → x ≡ y
suc-inj = ap pred suc-inj
Furthermore, observe that the successor
operation is injective, i.e.,
we can “cancel” it on paths. Putting these together, we get a proof that
equality for the natural numbers is decidable:
private module _ where private
: Discrete Nat
Discrete-Nat {zero} {zero} = yes refl
Discrete-Nat {zero} {suc y} = no λ zero≡suc → absurd (zero≠suc zero≡suc)
Discrete-Nat {suc x} {zero} = no λ suc≡zero → absurd (suc≠zero suc≡zero)
Discrete-Nat {suc x} {suc y} with Discrete-Nat {x} {y}
Discrete-Nat ... | yes x≡y = yes (ap suc x≡y)
... | no ¬x≡y = no λ sucx≡sucy → ¬x≡y (suc-inj sucx≡sucy)
abstract
: ∀ {x y} → So (x == y) → x ≡ y
from-prim-eq {zero} {zero} p = refl
from-prim-eq {suc x} {suc y} p = ap suc (from-prim-eq p)
from-prim-eq
: ∀ {x p} → from-prim-eq {x} {x} p ≡ refl
from-prim-eq-refl {zero} = refl
from-prim-eq-refl {suc x} = ap (ap suc) (from-prim-eq-refl {x})
from-prim-eq-refl
{-# REWRITE from-prim-eq-refl #-}
: ∀ {x y} → x ≡ y → So (x == y)
to-prim-eq {zero} {zero} p = oh
to-prim-eq {zero} {suc y} p = absurd (zero≠suc p)
to-prim-eq {suc x} {zero} p = absurd (suc≠zero p)
to-prim-eq {suc x} {suc y} p = to-prim-eq (suc-inj p)
to-prim-eq
instance
: Discrete Nat
Discrete-Nat {x} {y} with oh? (x == y)
Discrete-Nat ... | yes p = yes (from-prim-eq p)
... | no ¬p = no (¬p ∘ to-prim-eq)
Hedberg’s
theorem implies that Nat
is a
set, i.e., it only has trivial
paths.
opaque: is-set Nat
Nat-is-set = Discrete→is-set Discrete-Nat
Nat-is-set
instance
: ∀ {n} → H-Level Nat (2 + n)
H-Level-Nat = basic-instance 2 Nat-is-set H-Level-Nat
Arithmetic🔗
Heads up! The arithmetic properties of operations on
the natural numbers are in the module Data.Nat.Properties
.
Agda already comes with definitions for addition and multiplication of natural numbers. They are reproduced below, using different names, for the sake of completeness:
: Nat → Nat → Nat
plus = y
plus zero y (suc x) y = suc (plus x y)
plus
: Nat → Nat → Nat
times = zero
times zero y (suc x) y = y + times x y times
These match up with the built-in definitions of _+_
and _*_
:
: plus ≡ _+_
plus≡+ = y
plus≡+ i zero y (suc x) y = suc (plus≡+ i x y)
plus≡+ i
: times ≡ _*_
times≡* = zero
times≡* i zero y (suc x) y = y + (times≡* i x y) times≡* i
The exponentiation operator ^
is
defined by recursion on the exponent.
_^_ : Nat → Nat → Nat
= 1
x ^ zero = x * (x ^ y)
x ^ suc y
infixr 10 _^_
Ordering🔗
We define the order relation _≤_
on the natural numbers as an inductive predicate. We could also define
the relation by recursion on the numbers to be compared, but the
inductive version has much better properties when it comes to type
inference.
data _≤_ : Nat → Nat → Type where
instance
: ∀ {x} → 0 ≤ x
0≤x : ∀ {x y} → x ≤ y → suc x ≤ suc y s≤s
instance
: ∀ {x y} → ⦃ x ≤ y ⦄ → suc x ≤ suc y
s≤s' = s≤s x
s≤s' ⦃ x ⦄
: ∀ {x} → x ≤ x
x≤x {zero} = 0≤x
x≤x {suc x} = s≤s x≤x
x≤x
: ∀ {x y} ⦃ p : x ≤ y ⦄ → x ≤ suc y
x≤sucy {.0} {y} ⦃ 0≤x ⦄ = 0≤x
x≤sucy {.(suc _)} {.(suc _)} ⦃ s≤s p ⦄ = s≤s (x≤sucy ⦃ p ⦄)
x≤sucy
{-# INCOHERENT x≤x x≤sucy #-}
: ∀ {x y : Nat} → suc x ≤ suc y → x ≤ y
≤-peel (s≤s p) = p
≤-peel
: ∀ {x} → suc x ≤ 0 → ⊥
¬suc≤0 ()
¬suc≤0
: ∀ {x y z} → x ≤ y → y ≤ z → x ≤ z
≤-trans = 0≤x
≤-trans 0≤x 0≤x (s≤s q) = 0≤x
≤-trans 0≤x (s≤s p) (s≤s q) = s≤s (≤-trans p q)
≤-trans
: Nat → Nat
factorial = 1
factorial zero (suc n) = suc n * factorial n
factorial
: Nat → Type
Positive = 1 ≤ n Positive n
We define the strict ordering on Nat
as well, re-using the definition of
_≤_
.
_<_ : Nat → Nat → Type
= suc m ≤ n
m < n infix 7 _<_ _≤_
: ∀ {x y : Nat} → x ≤ y → x ≤ suc y
≤-sucr = 0≤x
≤-sucr 0≤x (s≤s p) = s≤s (≤-sucr p)
≤-sucr
: ∀ {x y} → x < y → x ≤ y
<-weaken {x} {suc y} p = ≤-sucr (≤-peel p) <-weaken
As an “ordering combinator”, we can define the maximum of two natural numbers by recursion: The maximum of zero and a successor (on either side) is the successor, and the maximum of successors is the successor of their maximum.
: Nat → Nat → Nat
max = zero
max zero zero (suc y) = suc y
max zero (suc x) zero = suc x
max (suc x) (suc y) = suc (max x y) max
Similarly, we can define the minimum of two numbers:
: Nat → Nat → Nat
min = zero
min zero zero (suc y) = zero
min zero (suc x) zero = zero
min (suc x) (suc y) = suc (min x y) min