1-- Modular arithmetic on the natural numbers: the remainder is determined by2-- the division equation, so remainders of sums and products reduce to the3-- remainders of their parts, and a vanishing remainder means divisibility4-- Part of Cumulus, the Grace17 mathematics library.5-- Copyright (c) 2026 Grant Passmore.6-- Released under the Apache License 2.0. See LICENSE in the repository root.7-- Authors: Grant Passmore89module Grace.Cumulus.NumberTheory.Modular10import Nat.Division11import Divisibility1213-- One step of the remainder: it wraps to zero exactly at the modulus.14theorem mod.succ_wrap (n k : Nat)15requires wraps : succ(k % n) = n16shows succ(k) % n =017proof18have condition := equalNat.true_iff.backward with wraps19simp [mod, nextRemainder, equalNat.reflexive, condition, chooseNat]2021theorem mod.succ_advance (n k : Nat)22requires advances : succ(k % n) ≠ n23shows succ(k) % n = succ(k % n)24proof25have condition := equalNat.false_iff.backward with advances26simp [mod, nextRemainder, equalNat.reflexive, condition, chooseNat]2728-- The remainder is the unique r < n with a = n·q + r.29theorem mod.unique_aux (n a : Nat)30requires nonzero : n ≠031shows∀ q, Nat(q) →∀ r, Nat(r) → a = n * q + r → r < n → a % n = r32proof33induction a34| zero =>35intros q qNat r rNat equation bound36have parts := add.eq_zero at r, n * q37 symm at equation38have both := parts with equation39have rZero := both.right40rw rZero41exact mod.zero_left at n42| succ(k) using ih =>43intros q qNat r rNat equation bound44have shape := natural.zero_or_successor at r45cases shape with rZero | rSucc46 . have qShape := natural.zero_or_successor at q47cases qShape with qZero | qSucc48 . exfalso49rw [rZero, qZero] at equation50have zero : n *0+0=0byring51rw zero at equation52have notSucc : succ(k) ≠0byarith53exact notSucc with equation54 . obtain⟨qB, qBNat, qForm⟩:= qSucc55have nShape := natural.zero_or_successor at n56cases nShape with nZero | nSucc57 . exfalso58exact nonzero with nZero59 . obtain⟨p, pNat, nForm⟩:= nSucc60rw [rZero, qForm] at equation61have expand : n * succ(qB) +0= (n * qB + p) +162rw nForm63ring64rw expand at equation65have unfoldK : succ(k) = k +1byring66rw unfoldK at equation67have cancel := add.right_cancel at k, n * qB + p, 168have previous := cancel with equation69have pBelow : p < n70rw nForm71arith72have remainder := ih with qB, qBNat, p, pNat, previous, pBelow73have wraps : succ(k % n) = n74rw [remainder, nForm]75rfl76have wrap := mod.succ_wrap at n, k with wraps77rw [wrap, rZero]78rfl79 . obtain⟨rP, rPNat, rForm⟩:= rSucc80rw rForm at equation81rw rForm at bound82have unfoldK : succ(k) = k +1byring83have unfoldR : n * q + succ(rP) = (n * q + rP) +1byring84rw [unfoldK, unfoldR] at equation85have cancel := add.right_cancel at k, n * q + rP, 186have previous := cancel with equation87have rPBelow : rP < n byarith88have remainder := ih with q, qNat, rP, rPNat, previous, rPBelow89have different : succ(k % n) ≠ n90intro same91rw remainder at same92rw same at bound93exact Less.irreflexive at n with*, bound94have advance := mod.succ_advance at n, k with different95rw [advance, remainder, rForm]96rfl9798theorem mod.unique (n a q r : Nat)99requires nonzero : n ≠0100requires equation : a = n * q + r101requires bound : r < n102shows a % n = r103proof104have all := mod.unique_aux at n, a with nonzero105have qNat : Nat(q) bytypecheck106have rNat : Nat(r) bytypecheck107exact all with q, r, equation, bound108109theorem mod.of_less (n a : Nat)110requires bound : a < n111shows a % n = a112proof113have nonzero : n ≠0114intro zero115rw zero at bound116have impossible : ¬a < 0byarith117exact impossible with bound118have equation : a = n *0+ a byring119exact mod.unique with nonzero, equation, bound120121theorem mod.mod (n a : Nat)122requires nonzero : n ≠0123shows (a % n) % n = a % n124proof125have bound := mod.less at a, n with nonzero126exact mod.of_less with bound127128theorem mod.add_multiple (n a s : Nat)129requires nonzero : n ≠0130shows (a + n * s) % n = a % n131proof132obtain⟨q, qNat, equation⟩:= mod.equation at a, n with nonzero133have bound := mod.less at a, n with nonzero134have shifted : a + n * s = n * (q + s) + a % n135have same : a + n * s = (n * q + a % n) + n * s136rw← equation137rfl138rw same139ring140exact mod.unique with nonzero, shifted, bound141142theorem mod.add (n a b : Nat)143requires nonzero : n ≠0144shows (a + b) % n = (a % n + b % n) % n145proof146obtain⟨q, qNat, eqA⟩:= mod.equation at a, n with nonzero147obtain⟨qB, qBNat, eqB⟩:= mod.equation at b, n with nonzero148obtain⟨qS, qSNat, eqS⟩:= mod.equation at a % n + b % n, n with nonzero149have bound := mod.less at a % n + b % n, n with nonzero150have combined : a + b = n * (q + qB + qS) + (a % n + b % n) % n151have same : a + b = (n * q + a % n) + (n * qB + b % n)152rw [← eqA, ← eqB]153rfl154have expand : n * (q + qB + qS) + (a % n + b % n) % n = n * q + n * qB + (n * qS + (a % n + b % n) % n) byring155rw [same, expand, ← eqS]156ring157exact mod.unique with nonzero, combined, bound158159theorem mod.mul (n a b : Nat)160requires nonzero : n ≠0161shows (a * b) % n = ((a % n) * (b % n)) % n162proof163obtain⟨q, qNat, eqA⟩:= mod.equation at a, n with nonzero164obtain⟨qB, qBNat, eqB⟩:= mod.equation at b, n with nonzero165obtain⟨qS, qSNat, eqP⟩:= mod.equation at (a % n) * (b % n), n with nonzero166have bound := mod.less at (a % n) * (b % n), n with nonzero167have combined : a * b = n * (n * q * qB + q * (b % n) + (a % n) * qB + qS) + ((a % n) * (b % n)) % n168have same : a * b = (n * q + a % n) * (n * qB + b % n)169rw [← eqA, ← eqB]170rfl171have expand : n * (n * q * qB + q * (b % n) + (a % n) * qB + qS) + ((a % n) * (b % n)) % n = n * (n * q * qB + q * (b % n) + (a % n) * qB) + (n * qS + ((a % n) * (b % n)) % n) byring172rw [same, expand, ← eqP]173ring174exact mod.unique with nonzero, combined, bound175176theorem mod.mul_left (n a b : Nat)177requires nonzero : n ≠0178shows ((a % n) * b) % n = (a * b) % n179proof180have first := mod.mul at n, a % n, b with nonzero181have second := mod.mul at n, a, b with nonzero182have idempotent := mod.mod at n, a with nonzero183rw [first, idempotent, second]184rfl185186theorem mod.mul_right (n a b : Nat)187requires nonzero : n ≠0188shows (a * (b % n)) % n = (a * b) % n189proof190have first := mod.mul at n, a, b % n with nonzero191have second := mod.mul at n, a, b with nonzero192have idempotent := mod.mod at n, b with nonzero193rw [first, idempotent, second]194rfl195196theorem mod.zero_iff (n a : Nat)197requires nonzero : n ≠0198shows a % n =0↔ n ∣ a199proof200constructor201 . intro zero202obtain⟨q, qNat, equation⟩:= mod.equation at a, n with nonzero203rw zero at equation204witness q205rw equation206ring207 . intro divides208obtain⟨k, kNat, equation⟩:= divides209have shape : a = n * k +0210rw← equation211ring212have positive : 0 < n213have alternatives := natural.zero_or_successor at n214cases alternatives with zero | successor215 . exfalso216exact nonzero with zero217 . obtain⟨p, pNat, form⟩:= successor218rw form219arith220exact mod.unique with nonzero, shape, positive221222-- Adding c does not change the remainder exactly when n divides c.223theorem mod.add_cancel (n b c : Nat)224requires nonzero : n ≠0225requires same : (b + c) % n = b % n226shows n ∣ c227proof228obtain⟨q, qNat, eqB⟩:= mod.equation at b, n with nonzero229obtain⟨qB, qBNat, eqS⟩:= mod.equation at b + c, n with nonzero230rw same at eqS231have expanded : (n * q + c) + b % n = n * qB + b % n232have arranged : (n * q + c) + b % n = (n * q + b % n) + c byring233 rwa [arranged, ← eqB]234have cancel := add.right_cancel at n * q + c, n * qB, b % n235have reduced := cancel with expanded236have total := LessEqual.total at q, qB237cases total with below | above238 . obtain⟨d, dNat, shape⟩:= below239rw← shape at reduced240have expand : n * (q + d) = n * q + n * d byring241rw expand at reduced242have cancel2 := add.left_cancel at c, n * d, n * q243have form := cancel2 with reduced244witness d245rw form246rfl247 . obtain⟨d, dNat, shape⟩:= above248rw← shape at reduced249have expand : n * (qB + d) = n * qB + n * d byring250rw expand at reduced251have shifted : n * qB + (n * d + c) = n * qB +0252have arranged : n * qB + (n * d + c) = n * qB + n * d + c byring253have zeroSum : n * qB +0= n * qB byring254 rwa [arranged, zeroSum]255have cancel2 := add.left_cancel at n * d + c, 0, n * qB256have sumZero := cancel2 with shifted257have parts := add.eq_zero at c, n * d258have both := parts with sumZero259rw both.right260exact divides.zero261