open import 1Lab.HLevel.Sets open import 1Lab.Type.Dec open import 1Lab.HLevel open import 1Lab.Path open import 1Lab.Type module Data.Nat.Base where
Natural Numbers🔗
The natural numbers are the inductive type generated by zero and closed under taking successors. Thus, they satisfy the following induction principle, which is familiar:
Nat-elim : ∀ {ℓ} (P : Nat → Type ℓ) → P 0 → ({n : Nat} → P n → P (suc n)) → (n : Nat) → P n Nat-elim P pz ps zero = pz Nat-elim P pz ps (suc n) = Nat-elim (λ z → P (suc z)) (ps pz) ps n
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:
zero≠suc : {n : Nat} → zero ≡ suc n → ⊥ zero≠suc path = subst distinguish path tt where distinguish : Nat → Type distinguish zero = ⊤ distinguish (suc x) = ⊥
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.
suc-inj : {x y : Nat} → suc x ≡ suc y → x ≡ y suc-inj = ap pred where pred : Nat → Nat pred (suc x) = x pred zero = zero
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:
Discrete-Nat : 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 (zero≠suc (sym suc≡zero)) Discrete-Nat (suc x) (suc y) with Discrete-Nat x y ... | yes x≡y = yes (ap suc x≡y) ... | no ¬x≡y = no λ sucx≡sucy → ¬x≡y (suc-inj sucx≡sucy)
Hedberg’s theorem implies that Nat is a set, i.e., it only has trivial paths.
Nat-is-set : is-set Nat Nat-is-set = Discrete→is-set Discrete-Nat
Arithmetic🔗
Heads up! The arithmetic properties of operations on the natural numbers are in the module 1Lab.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:
plus : Nat → Nat → Nat plus zero y = y plus (suc x) y = suc (plus x y) times : Nat → Nat → Nat times zero y = zero times (suc x) y = y + times x y
These match up with the built-in definitions of _+_ and _*_:
plus≡+ : plus ≡ _+_ plus≡+ i zero y = y plus≡+ i (suc x) y = suc (plus≡+ i x y) times≡* : times ≡ _*_ times≡* i zero y = zero times≡* i (suc x) y = y + (times≡* i x y)
The exponentiation operator ^ is defined by recursion on the exponent.
_^_ : Nat → Nat → Nat x ^ zero = 1 x ^ suc y = x * (x ^ y) infixr 8 _^_
Ordering🔗
We define the order relation _≤_ on the natural numbers by recursion:
_≤_ : Nat → Nat → Type zero ≤ zero = ⊤ zero ≤ suc y = ⊤ suc x ≤ zero = ⊥ suc x ≤ suc y = x ≤ y infix 3 _≤_
We define the strict ordering on Nat as well, re-using the definition of _≤_.
_<_ : Nat → Nat → Type m < n = suc m ≤ n infix 3 _<_
Then we can prove it is reflexive, transitive and antisymmetric.
≤-refl : (x : Nat) → x ≤ x ≤-refl zero = tt ≤-refl (suc x) = ≤-refl x 0≤x : (x : Nat) → zero ≤ x 0≤x zero = tt 0≤x (suc x) = tt ≤-trans : (x y z : Nat) → x ≤ y → y ≤ z → x ≤ z ≤-trans zero zero zero _ _ = tt ≤-trans zero zero (suc z) _ _ = tt ≤-trans zero (suc y) z p q = 0≤x z ≤-trans (suc x) (suc y) (suc z) p q = ≤-trans x y z p q ≤-antisym : (x y : Nat) → x ≤ y → y ≤ x → x ≡ y ≤-antisym zero zero p q = refl ≤-antisym (suc x) (suc y) p q = ap suc (≤-antisym x y p q)
A simple inductive argument proves that _≤_ always takes values in propositions, i.e. any “two” proofs that x ≤ y are identical:
≤-prop : (x y : Nat) → is-prop (x ≤ y) ≤-prop zero zero p q = refl ≤-prop zero (suc y) p q = refl ≤-prop (suc x) (suc y) p q = ≤-prop x y p q
Furthermore, _≤_ is decidable:
≤-flip : (x y : Nat) → (x ≤ y → ⊥) → y ≤ x ≤-flip zero zero ¬x≤y = tt ≤-flip zero (suc y) ¬x≤y = ¬x≤y tt ≤-flip (suc x) zero ¬x≤y = tt ≤-flip (suc x) (suc y) ¬x≤y = ≤-flip x y ¬x≤y ≤-dec : (x y : Nat) → Dec (x ≤ y) ≤-dec zero zero = yes tt ≤-dec zero (suc y) = yes tt ≤-dec (suc x) zero = no (λ z → z) ≤-dec (suc x) (suc y) = ≤-dec x y
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.
max : Nat → Nat → Nat max zero zero = zero max zero (suc y) = suc y max (suc x) zero = suc x max (suc x) (suc y) = suc (max x y)
Similarly, we can define the minimum of two numbers:
min : Nat → Nat → Nat min zero zero = zero min zero (suc y) = zero min (suc x) zero = zero min (suc x) (suc y) = suc (min x y)