module Data.Nat.DivMod where
Natural division🔗
This module implements the basics of the theory of
division (not divisibility, see there) for the
natural numbers. In particular, we define what it means to divide in the
naturals (the type DivMod
), and
implement a division procedure that works for positive denominators.
The result of division isn’t a single number, but rather a pair of numbers such that The number is the quotient and the number is the remainder
record DivMod (a b : Nat) : Type where
constructor divmod
field
: Nat
quot : Nat
rem .quotient : a ≡ quot * b + rem
.smaller : rem < b
The easy case is to divide zero by anything, in which case the result is zero with remainder zero. The more interesting case comes when we have some successor, and we want to divide it.
module _ where private
-- This is a nice explanation of the division algorithm but it ends up
-- being extremely slow when compared to the builtins. So we define the
-- nice prosaic version first, then define the fast version hidden..
: ∀ a b → .⦃ _ : Positive b ⦄ → DivMod a b
divide-pos (suc b) = divmod 0 0 refl (s≤s 0≤x) divide-pos zero
It suffices to assume — since is smaller than — that we have already computed numbers with Since the ordering on is trichotomous, we can proceed by cases on whether
(suc a) b with divide-pos a b
divide-pos (suc a) b | divmod q' r' p s with ≤-split (suc r') b divide-pos
First, suppose that i.e., can serve as a remainder for the division of In that case, we have our divisor! It remains to show, by a slightly annoying computation, that
(suc a) b | divmod q' r' p s | inl r'+1<b =
divide-pos (suc r') (ap suc p ∙ sym (+-sucr (q' * b) r')) r'+1<b divmod q'
The other case — that in which — is more interesting. Then, rather than incrementing the remainder, our remainder has “overflown”, and we have to increment the quotient instead. We take, in this case, and which works out because ( and) of some arithmetic. See for yourself:
(suc a) (suc b') | divmod q' r' p s | inr (inr r'+1=b) =
divide-pos (suc q') 0
divmod ( suc a ≡⟨ ap suc p ⟩
(q' * (suc b') + r') ≡˘⟨ ap (λ e → suc (q' * e + r')) r'+1=b ⟩
suc (q' * (suc r') + r') ≡⟨ nat! ⟩
suc (r' + q' * (suc r') + zero) ≡⟨ ap (λ e → e + q' * e + 0) r'+1=b ⟩
suc (suc b') + q' * (suc b') + 0 ∎ )
(s≤s 0≤x)
Note that we actually have one more case to consider – or rather, discard – that in which It’s impossible because, by the definition of division, we have meaning
(suc a) (suc b') | divmod q' r' p s | inr (inl b<r'+1) =
divide-pos (<-not-equal b<r'+1
absurd (≤-antisym (≤-sucr (≤-peel b<r'+1)) (recover s)))
As a finishing touch, we define short operators to produce the result
of applying divide-pos
to a pair of
numbers. Note that the procedure above only works when the denominator
is nonzero, so we have a degree of freedom when defining
and
The canonical choice is to yield
in both cases.
_%_ : Nat → Nat → Nat
= zero
a % zero = divide-pos a (suc b) .DivMod.rem
a % suc b
_/ₙ_ : Nat → Nat → Nat
= zero
a /ₙ zero = divide-pos a (suc b) .DivMod.quot
a /ₙ suc b
: ∀ x y → .⦃ _ : Positive y ⦄ → x ≡ (x /ₙ y) * y + x % y
is-divmod (suc y) with divide-pos x (suc y)
is-divmod x ... | divmod q r α β = recover α
: ∀ x y → .⦃ _ : Positive y ⦄ → (x % y) < y
x%y<y (suc y) with divide-pos x (suc y)
x%y<y x ... | divmod q r α β = recover β
With this, we can decide whether two numbers divide each other by checking whether the remainder is zero!
: (k m n j : Nat) → Nat
mod-helper = k
mod-helper k m zero j (suc n) zero = mod-helper 0 m n m
mod-helper k m (suc n) (suc j) = mod-helper (suc k) m n j
mod-helper k m
: (k m n j : Nat) → Nat
div-helper = k
div-helper k m zero j (suc n) zero = div-helper (suc k) m n m
div-helper k m (suc n) (suc j) = div-helper k m n j
div-helper k m
{-# BUILTIN NATDIVSUCAUX div-helper #-}
{-# BUILTIN NATMODSUCAUX mod-helper #-}
-- _ = {! mod-helper 0 0 4294967296 2 !}
_/ₙ_ : (d1 d2 : Nat) .⦃ _ : Positive d2 ⦄ → Nat
= div-helper 0 d2 d1 d2
d1 /ₙ suc d2
_%_ : (d1 d2 : Nat) .⦃ _ : Positive d2 ⦄ → Nat
= mod-helper 0 d2 d1 d2
d1 % suc d2
infixl 9 _/ₙ_ _%_
abstract
private
: ∀ am ad d n → am + ad * suc (am + n) + d ≡ mod-helper am (am + n) d n + div-helper ad (am + n) d n * suc (am + n)
div-mod-lemma = +-zeror _
div-mod-lemma am ad zero n (suc d) zero rewrite Id≃path.from (+-zeror am) = +-sucr _ d ∙ div-mod-lemma zero (suc ad) d am
div-mod-lemma am ad (suc d) (suc n) rewrite Id≃path.from (+-sucr am n) = +-sucr _ d ∙ div-mod-lemma (suc am) ad d n
div-mod-lemma am ad
: ∀ a d n → mod-helper a (a + n) d n ≤ a + n
mod-le = +-≤l a n
mod-le a zero n (suc d) zero = mod-le zero d (a + 0)
mod-le a (suc d) (suc n) rewrite Id≃path.from (+-sucr a n) = mod-le (suc a) d n
mod-le a
: ∀ x y → .⦃ _ : Positive y ⦄ → x ≡ (x /ₙ y) * y + x % y
is-divmod (suc y) = div-mod-lemma 0 0 x y ∙ +-commutative (x % (suc y)) _
is-divmod x
: ∀ x y → .⦃ _ : Positive y ⦄ → (x % y) < y
x%y<y (suc y) = s≤s (mod-le 0 x y)
x%y<y x
: ∀ d1 d2 .⦃ _ : Positive d2 ⦄ → d1 % d2 ≡ 0 → d2 ∣ d1
from-fast-mod = fibre→∣ (d1 /ₙ d2 , sym (is-divmod d1 d2 ∙ ap (d1 /ₙ d2 * d2 +_) p ∙ +-zeror _))
from-fast-mod d1 d2 p
: ∀ a b → .⦃ _ : Positive b ⦄ → DivMod a b
divide-pos = divmod (a /ₙ b) (a % b) (is-divmod a b) (x%y<y a b) divide-pos a b
instance
: ∀ {n m} → Dec (n ∣ m)
Dec-∣ {zero} {m} = m ≡? 0
Dec-∣ @{suc _} {m} with divide-pos m n
Dec-∣ n... | divmod q 0 α β = yes (q , sym (+-zeror _) ∙ sym (recover α))
... | divmod q r@(suc _) α β = no λ (q' , p) →
let
: (q' - q) * n ≡ r
n∣r = monus-distribr q' q n ∙ sym (monus-swapl _ _ _ (sym (p ∙ recover α)))
n∣r in <-≤-asym (recover β) (m∣sn→m≤sn (q' - q , recover n∣r))