module Data.Int.DivMod where

Integer division🔗

We define the division where is an integer and is a positive natural number.

record DivModℤ (a : Int) (b : Nat) : Type where
  constructor divmodℤ

  field
    quotℤ : Int
    remℤ  : Nat
    .quotient : a  quotℤ *ℤ pos b +ℤ pos remℤ
    .smaller  : remℤ < b

open DivModℤ

In order to compute integer division, we reuse natural division. We proceed by cases: if is positive, we can simply compute the division as natural numbers.

divide-posℤ :  a b  .⦃ _ : Positive b   DivModℤ a b
divide-posℤ (pos a) b using divmod q r p sdivide-pos a b
  = divmodℤ (pos q) r (ap pos p  sym (ap (_+ℤ pos r) (assign-pos _))) s

If is negative, we can compute as natural numbers and distinguish two cases: if the remainder is then divides so we simply negate the quotient and set the remainder to

divide-posℤ (negsuc a) b@(suc b') with divide-pos (suc a) b
... | divmod q zero p s = divmodℤ (assign neg q) 0
  (ap (assign neg) p  assign-+ (q * b) 0  ap (_+ℤ 0) (assign-*l q b))
  (s≤s 0≤x)

If the remainder is non-zero, we have to correct for the fact that integer division as we’ve defined it rounds towards while natural division rounds towards (hence towards for negative numbers), so we take as the quotient and as the remainder.

... | divmod q (suc r) p s = divmodℤ (negsuc q) (b - suc r)
  (ap (assign neg) p  lemma)
  (s≤s (monus-≤ b' r))
One annoying lemma later, we are done.
  where
    lemma : assign neg (q * b + suc r)  pos (b' - r) +ℤ negsuc (b' + q * b)
    lemma =
      assign neg (q * b + suc r)                               ≡⟨ ap (assign neg) (+-commutative (q * b) _) 
      negsuc (r + q * b)                                       ≡˘⟨ negℤ-+ℤ-negsuc r (q * b) ≡˘
      negℤ (pos r) +ℤ negsuc (q * b)                           ≡⟨ ap (_+ℤ negsuc (q * b)) (ℤ.insertl {negℤ (pos b')} (+ℤ-invl (pos b')) {negℤ (pos r)}) 
       negℤ (pos b')  +ℤ (pos b' -ℤ pos r) +ℤ negsuc (q * b) ≡˘⟨ ap¡ (assign-neg b') ≡˘
      assign neg b' +ℤ (pos b' -ℤ pos r) +ℤ negsuc (q * b)     ≡⟨ ap (_+ℤ negsuc (q * b)) (+ℤ-commutative (assign neg b') (pos b' -ℤ pos r)) 
      (pos b' -ℤ pos r) +ℤ assign neg b' +ℤ negsuc (q * b)     ≡˘⟨ +ℤ-assoc (pos b' -ℤ pos r) _ _ ≡˘
      (pos b' -ℤ pos r) +ℤ (assign neg b' +ℤ negsuc (q * b))   ≡⟨ ap₂ _+ℤ_ (pos-pos b' r) (ap (_+ℤ negsuc (q * b)) (assign-neg b')) 
      (b' ℕ- r) +ℤ (negℤ (pos b') +ℤ negsuc (q * b))           ≡⟨ ap ((b' ℕ- r) +ℤ_) (negℤ-+ℤ-negsuc b' (q * b)) 
      (b' ℕ- r) +ℤ negsuc (b' + q * b)                         ≡⟨ ap₂ _+ℤ_ (nat-diff-monus b' r (≤-peel (<-weaken (recover s)))) refl 
      pos (b' - r) +ℤ negsuc (b' + q * b)                      
_/ℤ_ : Int  (b : Nat)  .⦃ _ : Positive b   Int
a /ℤ b = divide-posℤ a b .quotℤ

_%ℤ_ : Int  (b : Nat)  .⦃ _ : Positive b   Nat
a %ℤ b = divide-posℤ a b .remℤ

is-divmodℤ :  x y  .⦃ _ : Positive y   x  (x /ℤ y) *ℤ pos y +ℤ pos (x %ℤ y)
is-divmodℤ x (suc y) with divide-posℤ x (suc y)
... | divmodℤ q r α β = recover α

x%ℤy<y :  x y  .⦃ _ : Positive y   (x %ℤ y) < y
x%ℤy<y x (suc y) with divide-posℤ x (suc y)
... | divmodℤ q r α β = recover β

infixl 9 _/ℤ_ _%ℤ_

Properties🔗

With the way we’ve set things up above, there is exactly one way to divide by that is, the type DivModℤ a b is a proposition for all positive b.

DivModℤ-unique :  a b  .⦃ _ : Positive b   is-prop (DivModℤ a b)
DivModℤ-unique a b@(suc b') x@(divmodℤ q r p s) y@(divmodℤ q' r' p' s') = go where

To see this, we start by showing that the remainders and are equal. To that end, we show that divides their difference; since both and are less than their difference must also be, hence it must be zero.

  b∣r'-r : b ∣ℤ (r' ℕ- r)
  b∣r'-r .fst = q -ℤ q'
  b∣r'-r .snd =
    (q -ℤ q') *ℤ pos b        ≡⟨ *ℤ-distribr-minus (pos b) q q' 
    q *ℤ pos b -ℤ q' *ℤ pos b ≡⟨ ℤ.equal-sum→equal-diff (q *ℤ pos b) (pos r) (q' *ℤ pos b) (pos r') (sym (recover p)  recover p') 
    pos r' -ℤ pos r           ≡⟨ pos-pos r' r 
    r' ℕ- r                   

  same-r : r  r'
  same-r = dec→dne λ r≠r'  <-≤-asym
    (s≤s (nat-diff-bounded r' r b' (≤-peel (recover s')) (≤-peel (recover s))))
    (m∣ℤsn→m≤sn  k  r≠r' (sym (nat-diff-positive r' r k))) b∣r'-r)

Since the remainders are equal, we have since multiplication by a positive integer is injective, we must also have

  same-q : q  q'
  same-q = *ℤ-injectiver-possuc b' q q' $
    q *ℤ pos b  ≡⟨ -ℤ-swapl _ _ _ (recover (sym p)) 
    a -ℤ pos r  ≡⟨ ap  r  a -ℤ pos r) same-r 
    a -ℤ pos r' ≡˘⟨ -ℤ-swapl _ _ _ (recover (sym p')) ≡˘
    q' *ℤ pos b 

The last two fields are propositions, so this shows that the two DivModℤs are equal.

  go : x  y
  go i = divmodℤ (same-q i) (same-r i)
    (is-prop→pathp
       i  hlevel {T = a  same-q i *ℤ pos b +ℤ pos (same-r i)} 1) p p' i)
    (is-prop→pathp
       i  Nat.≤-is-prop {suc (same-r i)} {b}) s s' i)

From this, we deduce that divides the difference of two integers if and only if and have the same remainder modulo

divides-diff→same-rem
  :  n x y  .⦃ _ : Positive n 
   n ∣ℤ (x -ℤ y)  x %ℤ n  y %ℤ n
divides-diff→same-rem n x y n∣x-y
  using k , z∣ℤ→fibre n∣x-y
  using divmodℤ q r p sdivide-posℤ x n
  = ap remℤ (DivModℤ-unique _ _ (divmodℤ (q -ℤ k) r p' s) (divide-posℤ y n))
  where
    p' : y  (q -ℤ k) *ℤ pos n +ℤ pos r
    p' =
      y                                            ≡˘⟨ negℤ-negℤ y ≡˘
      negℤ (negℤ y)                                ≡⟨ ℤ.insertr (ℤ.inversel {x}) {negℤ (negℤ y)} 
       negℤ (negℤ y) +ℤ negℤ x  +ℤ x             ≡⟨ ap! (+ℤ-commutative (negℤ (negℤ y)) _) 
       negℤ x -ℤ negℤ y  +ℤ x                    ≡˘⟨ ap¡ (negℤ-distrib x (negℤ y)) ≡˘
      negℤ  x -ℤ y  +ℤ x                         ≡˘⟨ ap¡ z ≡˘
      negℤ (k *ℤ pos n) +ℤ x                       ≡⟨ ap (negℤ (k *ℤ pos n) +ℤ_) (recover p) 
      negℤ (k *ℤ pos n) +ℤ (q *ℤ pos n +ℤ pos r)   ≡⟨ +ℤ-assoc (negℤ (k *ℤ pos n)) _ _ 
       negℤ (k *ℤ pos n) +ℤ q *ℤ pos n  +ℤ pos r ≡⟨ ap! (+ℤ-commutative (negℤ (k *ℤ pos n)) _) 
       q *ℤ pos n -ℤ k *ℤ pos n  +ℤ pos r        ≡˘⟨ ap¡ (*ℤ-distribr-minus (pos n) q k) ≡˘
      (q -ℤ k) *ℤ pos n +ℤ pos r                   

same-rem→divides-diff
  :  n x y  .⦃ _ : Positive n 
   x %ℤ n  y %ℤ n  n ∣ℤ (x -ℤ y)
same-rem→divides-diff n x y p = dividesℤ (x /ℤ n -ℤ y /ℤ n) $
  (x /ℤ n -ℤ y /ℤ n) *ℤ pos n                                            ≡⟨ *ℤ-distribr-minus (pos n) (x /ℤ n) (y /ℤ n) 
  x /ℤ n *ℤ pos n -ℤ y /ℤ n *ℤ pos n                                     ≡˘⟨ -ℤ-cancelr (pos (x %ℤ n)) (x /ℤ n *ℤ pos n) _ ≡˘
  x /ℤ n *ℤ pos n +ℤ pos (x %ℤ n) -ℤ (y /ℤ n *ℤ pos n +ℤ pos  x %ℤ n ) ≡⟨ ap! p 
  x /ℤ n *ℤ pos n +ℤ pos (x %ℤ n) -ℤ (y /ℤ n *ℤ pos n +ℤ pos (y %ℤ n))   ≡˘⟨ ap₂ _-ℤ_ (is-divmodℤ x n) (is-divmodℤ y n) ≡˘
  x -ℤ y                                                                 

…and that natural numbers below are their own remainder modulo

ℕ<-%ℤ :  {n} (i : ℕ< n)  .⦃ _ : Positive n   pos (i .fst) %ℤ n  i .fst
ℕ<-%ℤ {n} (i , p) = ap remℤ (DivModℤ-unique (pos i) n
  (divide-posℤ (pos i) n) (divmodℤ 0 i refl p))

We also show that divides if and only if

rem-zero→divides :  n x  .⦃ _ : Positive n   x %ℤ n  0  n ∣ℤ x
rem-zero→divides n x p = dividesℤ (x /ℤ n) $
  x /ℤ n *ℤ pos n                 ≡˘⟨ +ℤ-zeror _ ≡˘
  x /ℤ n *ℤ pos n +ℤ pos  0     ≡˘⟨ ap¡ p ≡˘
  x /ℤ n *ℤ pos n +ℤ pos (x %ℤ n) ≡˘⟨ is-divmodℤ x n ≡˘
  x                               

divides→rem-zero :  n x  .⦃ _ : Positive n   n ∣ℤ x  x %ℤ n  0
divides→rem-zero n@(suc _) x  p  n∣x = divides-diff→same-rem n x 0  p 
  (subst (fibre (_*ℤ pos n)) (sym (+ℤ-zeror x)) (∣ℤ→fibre n∣x))