1-- More modular arithmetic: remainders of multiples and sums, powers, the2-- exhaustive small remainders, congruence modulo n as a predicate with its3-- equivalence and compatibility laws, cancellation of a coprime factor, and4-- the congruence forms of Euler's and Fermat's theorems5-- Part of Cumulus, the Grace17 mathematics library.6-- Copyright (c) 2026 Grant Passmore.7-- Released under the Apache License 2.0. See LICENSE in the repository root.8-- Authors: Grant Passmore910module Grace.Cumulus.NumberTheory.ModularMore11import Euler12import DivisibilityMore1314theorem mod.self (n : Nat)15shows n % n =016proof17have reflexive := divides.reflexive at n18have characterization := divides.iff_mod_zero at n, n19exact characterization.forward with reflexive2021theorem mod.mul_self_left (n k : Nat)22shows (n * k) % n =023proof24have multiple := divides.mul_left_self at n, k25have characterization := divides.iff_mod_zero at n, n * k26exact characterization.forward with multiple2728theorem mod.mul_self_right (n k : Nat)29shows (k * n) % n =030proof31have multiple := divides.mul_right_self at n, k32have characterization := divides.iff_mod_zero at n, k * n33exact characterization.forward with multiple3435theorem mod.of_divides (d a : Nat)36requires hda : d ∣ a37shows a % d =038proof39have characterization := divides.iff_mod_zero at d, a40exact characterization.forward with hda4142theorem mod.add_self (n a : Nat)43requires nonzero : n ≠044shows (a + n) % n = a % n45proof46have shifted := mod.add_multiple at n, a, 1with nonzero47have one : a + n *1= a + n byring48rw one at shifted49exact shifted5051theorem mod.self_add (n a : Nat)52requires nonzero : n ≠053shows (n + a) % n = a % n54proof55have commute : n + a = a + n byring56rw commute57exact mod.add_self at n, a with nonzero5859theorem mod.multiple_add (n a s : Nat)60requires nonzero : n ≠061shows (n * s + a) % n = a % n62proof63have commute : n * s + a = a + n * s byring64rw commute65exact mod.add_multiple at n, a, s with nonzero6667theorem mod.le (n a : Nat)68shows a % n ≤ a69proof70havesplit : n =0∨ n ≠0bytauto71casessplitwith zero | nonzero72 . rw zero73have value := mod.zero_right at a74rw value75exact LessEqual.reflexive at a76 . obtain⟨q, qNat, equation⟩:= mod.equation at a, n with nonzero77witness n * q78 symm79linear_combination equation8081theorem mod.eq_self_iff (n a : Nat)82requires nonzero : n ≠083shows a % n = a ↔ a < n84proof85constructor86 . intro same87have bound := mod.less at a, n with nonzero88rw same at bound89exact bound90 . intro below91exact mod.of_less with below9293theorem mod.one_left (n : Nat)94requires above : 1 < n95shows1% n =196proof97exact mod.of_less with above9899theorem mod.add_mod_left (n a b : Nat)100requires nonzero : n ≠0101shows (a % n + b) % n = (a + b) % n102proof103have first := mod.add at n, a % n, b with nonzero104have second := mod.add at n, a, b with nonzero105have idempotent := mod.mod at n, a with nonzero106rw [first, idempotent, second]107rfl108109theorem mod.add_mod_right (n a b : Nat)110requires nonzero : n ≠0111shows (a + b % n) % n = (a + b) % n112proof113have first := mod.add at n, a, b % n with nonzero114have second := mod.add at n, a, b with nonzero115have idempotent := mod.mod at n, b with nonzero116rw [first, idempotent, second]117rfl118119theorem mod.pow (n a k : Nat)120requires nonzero : n ≠0121shows (a ^ k) % n = ((a % n) ^ k) % n122proof123induction k124| zero =>125haveleft:= pow.zero at a126haveright:= pow.zero at a % n127rw [left, right]128rfl129| j +1using ih =>130haveleft:= pow.add_one at a, j131haveright:= pow.add_one at a % n, j132rw [left, right]133have first := mod.mul at n, a, a ^ j with nonzero134rw [first, ih]135exact mod.mul_right at n, a % n, (a % n) ^ j with nonzero136137theorem natural.below_two (r : Nat)138requires below : r < 2139shows r =0∨ r =1140proof141have le1 : r ≤1byarith142have alternatives := LessEqual.cases at r, 1with le1143cases alternatives with lt1 | eq1144 . left145have le0 : r ≤0byarith146exact LessEqual.eq_zero with le0147 . right148exact eq1149150theorem natural.below_three (r : Nat)151requires below : r < 3152shows r =0∨ r =1∨ r =2153proof154have le2 : r ≤2byarith155have alternatives := LessEqual.cases at r, 2with le2156cases alternatives with lt2 | eq2157 . have small := natural.below_two with lt2158cases small with zero | one159 . left160exact zero161 . right162left163exact one164 . right165right166exact eq2167168theorem natural.below_four (r : Nat)169requires below : r < 4170shows r =0∨ r =1∨ r =2∨ r =3171proof172have le3 : r ≤3byarith173have alternatives := LessEqual.cases at r, 3with le3174cases alternatives with lt3 | eq3175 . have small := natural.below_three with lt3176rcases small with zero | one | two177 . left178exact zero179 . right180left181exact one182 . right183right184left185exact two186 . right187right188right189exact eq3190191theorem mod.two_cases (a : Nat)192shows a %2=0∨ a %2=1193proof194have two : 2≠0byarith195have bound := mod.less at a, 2with two196exact natural.below_two with bound197198theorem mod.three_cases (a : Nat)199shows a %3=0∨ a %3=1∨ a %3=2200proof201have three : 3≠0byarith202have bound := mod.less at a, 3with three203exact natural.below_three with bound204205theorem mod.four_cases (a : Nat)206shows a %4=0∨ a %4=1∨ a %4=2∨ a %4=3207proof208have four : 4≠0byarith209have bound := mod.less at a, 4with four210exact natural.below_four with bound211212-- Adding d preserves the remainder exactly when n divides d.213theorem mod.add_eq_iff (n a d : Nat)214shows (a + d) % n = a % n ↔ n ∣ d215proof216havesplit : n =0∨ n ≠0bytauto217casessplitwith zero | nonzero218 . rw zero219haveleft:= mod.zero_right at a + d220haveright:= mod.zero_right at a221rw [left, right]222constructor223 . intro same224have le0 : d ≤0byarith225have dZero := LessEqual.eq_zero with le0226rw dZero227exact divides.zero at0228 . intro h229have dZero := zero.divides_only_zero with h230rw dZero231ring232 . constructor233 . intro same234exact mod.add_cancel at n, a, d with nonzero, same235 . intro h236obtain⟨s, sNat, equation⟩:= h237rw← equation238exact mod.add_multiple at n, a, s with nonzero239240theorem mod.eq_iff_divides_natDist (n a b : Nat)241shows a % n = b % n ↔ n ∣ natDist(a, b)242proof243have total := LessEqual.total at a, b244cases total with below | above245 . obtain⟨d, dNat, split⟩:= LessEqual.exists_add with below246have distance : natDist(a, b) = d247have value := natDist.of_le at a, b with below248have sum :=split249 symm at sum250have difference := sub.of_add_eq_left at a, d, b with sum251rw [value, difference]252rfl253rw [distance, split]254have shifted := mod.add_eq_iff at n, a, d255constructor256 . intro same257 symm at same258exact shifted.forward with same259 . intro h260 symm261exact shifted.backward with h262 . obtain⟨d, dNat, split⟩:= LessEqual.exists_add with above263have distance : natDist(a, b) = d264have value := natDist.of_ge at a, b with above265have sum :=split266 symm at sum267have difference := sub.of_add_eq_left at b, d, a with sum268rw [value, difference]269rfl270rw [distance, split]271exact mod.add_eq_iff at n, b, d272273predicate CongruentMod (a b n : Nat) := a % n = b % n274275theorem CongruentMod.reflexive (a n : Nat)276shows CongruentMod(a, a, n)277proof278unfold CongruentMod279rfl280281theorem CongruentMod.symmetric (a b n : Nat)282requires congruent : CongruentMod(a, b, n)283shows CongruentMod(b, a, n)284proof285unfold CongruentMod at congruent286unfold CongruentMod287 symm288exact congruent289290theorem CongruentMod.transitive (a b c n : Nat)291requires first : CongruentMod(a, b, n)292requires second : CongruentMod(b, c, n)293shows CongruentMod(a, c, n)294proof295unfold CongruentMod at first296unfold CongruentMod at second297unfold CongruentMod298rw [first, second]299rfl300301theorem CongruentMod.of_eq (a b n : Nat)302requires same : a = b303shows CongruentMod(a, b, n)304proof305rw same306exact CongruentMod.reflexive at b, n307308theorem CongruentMod.zero_modulus (a b : Nat)309shows CongruentMod(a, b, 0) ↔ a = b310proof311haveleft:= mod.zero_right at a312haveright:= mod.zero_right at b313constructor314 . intro congruent315unfold CongruentMod at congruent316rw [left, right] at congruent317exact congruent318 . intro same319unfold CongruentMod320rw [left, right]321exact same322323theorem CongruentMod.one_modulus (a b : Nat)324shows CongruentMod(a, b, 1)325proof326unfold CongruentMod327haveleft:= mod.one at a328haveright:= mod.one at b329rw [left, right]330rfl331332theorem CongruentMod.iff_divides_natDist (a b n : Nat)333shows CongruentMod(a, b, n) ↔ n ∣ natDist(a, b)334proof335have characterization := mod.eq_iff_divides_natDist at n, a, b336constructor337 . intro congruent338unfold CongruentMod at congruent339exact characterization.forward with congruent340 . intro distance341unfold CongruentMod342exact characterization.backward with distance343344theorem CongruentMod.divides_natDist (a b n : Nat)345requires congruent : CongruentMod(a, b, n)346shows n ∣ natDist(a, b)347proof348have characterization := CongruentMod.iff_divides_natDist at a, b, n349exact characterization.forward with congruent350351theorem CongruentMod.of_divides_natDist (a b n : Nat)352requires distance : n ∣ natDist(a, b)353shows CongruentMod(a, b, n)354proof355have characterization := CongruentMod.iff_divides_natDist at a, b, n356exact characterization.backward with distance357358theorem CongruentMod.of_add (a d n : Nat)359requires multiple : n ∣ d360shows CongruentMod(a + d, a, n)361proof362unfold CongruentMod363have shifted := mod.add_eq_iff at n, a, d364exact shifted.backward with multiple365366theorem CongruentMod.zero_right_iff (a n : Nat)367shows CongruentMod(a, 0, n) ↔ n ∣ a368proof369have characterization := CongruentMod.iff_divides_natDist at a, 0, n370have value := natDist.zero_right at a371rw value at characterization372exact characterization373374theorem CongruentMod.mod_right (a n : Nat)375shows CongruentMod(a, a % n, n)376proof377unfold CongruentMod378havesplit : n =0∨ n ≠0bytauto379casessplitwith zero | nonzero380 . rw zero381have value := mod.zero_right at a382rw value383rw value384rfl385 . have idempotent := mod.mod at n, a with nonzero386rw idempotent387rfl388389theorem CongruentMod.mod_left (a n : Nat)390shows CongruentMod(a % n, a, n)391proof392haveright:= CongruentMod.mod_right at a, n393exact CongruentMod.symmetric withright394395theorem CongruentMod.divides_modulus (a b m n : Nat)396requires hmn : m ∣ n397requires congruent : CongruentMod(a, b, n)398shows CongruentMod(a, b, m)399proof400have distance := CongruentMod.divides_natDist with congruent401have smaller := divides.transitive at m, n, natDist(a, b) with hmn, distance402exact CongruentMod.of_divides_natDist with smaller403404theorem CongruentMod.add (a b c d n : Nat)405requires first : CongruentMod(a, b, n)406requires second : CongruentMod(c, d, n)407shows CongruentMod(a + c, b + d, n)408proof409havesplit : n =0∨ n ≠0bytauto410casessplitwith zero | nonzero411 . rw zero at first412rw zero at second413rw zero414have firstEq := CongruentMod.zero_modulus at a, b415have secondEq := CongruentMod.zero_modulus at c, d416have ab := firstEq.forward with first417have cd := secondEq.forward with second418rw [ab, cd]419exact CongruentMod.reflexive at b + d, 0420 . unfold CongruentMod at first421unfold CongruentMod at second422unfold CongruentMod423haveleft:= mod.add at n, a, c with nonzero424haveright:= mod.add at n, b, d with nonzero425rw [left, right, first, second]426rfl427428theorem CongruentMod.mul (a b c d n : Nat)429requires first : CongruentMod(a, b, n)430requires second : CongruentMod(c, d, n)431shows CongruentMod(a * c, b * d, n)432proof433havesplit : n =0∨ n ≠0bytauto434casessplitwith zero | nonzero435 . rw zero at first436rw zero at second437rw zero438have firstEq := CongruentMod.zero_modulus at a, b439have secondEq := CongruentMod.zero_modulus at c, d440have ab := firstEq.forward with first441have cd := secondEq.forward with second442rw [ab, cd]443exact CongruentMod.reflexive at b * d, 0444 . unfold CongruentMod at first445unfold CongruentMod at second446unfold CongruentMod447haveleft:= mod.mul at n, a, c with nonzero448haveright:= mod.mul at n, b, d with nonzero449rw [left, right, first, second]450rfl451452theorem CongruentMod.add_left (a b c n : Nat)453requires congruent : CongruentMod(a, b, n)454shows CongruentMod(c + a, c + b, n)455proof456have reflexive := CongruentMod.reflexive at c, n457exact CongruentMod.add at c, c, a, b, n with reflexive, congruent458459theorem CongruentMod.add_right (a b c n : Nat)460requires congruent : CongruentMod(a, b, n)461shows CongruentMod(a + c, b + c, n)462proof463have reflexive := CongruentMod.reflexive at c, n464exact CongruentMod.add at a, b, c, c, n with congruent, reflexive465466theorem CongruentMod.mul_left (a b c n : Nat)467requires congruent : CongruentMod(a, b, n)468shows CongruentMod(c * a, c * b, n)469proof470have reflexive := CongruentMod.reflexive at c, n471exact CongruentMod.mul at c, c, a, b, n with reflexive, congruent472473theorem CongruentMod.mul_right (a b c n : Nat)474requires congruent : CongruentMod(a, b, n)475shows CongruentMod(a * c, b * c, n)476proof477have reflexive := CongruentMod.reflexive at c, n478exact CongruentMod.mul at a, b, c, c, n with congruent, reflexive479480theorem CongruentMod.pow (a b k n : Nat)481requires congruent : CongruentMod(a, b, n)482shows CongruentMod(a ^ k, b ^ k, n)483proof484induction k485| zero =>486haveleft:= pow.zero at a487haveright:= pow.zero at b488rw [left, right]489exact CongruentMod.reflexive at1, n490| j +1using ih =>491haveleft:= pow.add_one at a, j492haveright:= pow.add_one at b, j493rw [left, right]494exact CongruentMod.mul at a, b, a ^ j, b ^ j, n with congruent, ih495496theorem CongruentMod.add_right_cancel (a b c n : Nat)497requires congruent : CongruentMod(a + c, b + c, n)498shows CongruentMod(a, b, n)499proof500have distance := CongruentMod.divides_natDist with congruent501have shift := natDist.add_right at a, b, c502rw shift at distance503exact CongruentMod.of_divides_natDist with distance504505theorem CongruentMod.add_left_cancel (a b c n : Nat)506requires congruent : CongruentMod(c + a, c + b, n)507shows CongruentMod(a, b, n)508proof509have distance := CongruentMod.divides_natDist with congruent510have shift := natDist.add_left at a, b, c511rw shift at distance512exact CongruentMod.of_divides_natDist with distance513514theorem CongruentMod.mul_cancel_coprime (a x y n : Nat)515requires coprimeAN : gcd(a, n) =1516requires congruent : CongruentMod(a * x, a * y, n)517shows CongruentMod(x, y, n)518proof519havesplit : n =0∨ n ≠0bytauto520casessplitwith zero | nonzero521 . rw zero at congruent522rw zero at coprimeAN523rw zero524have value := gcd.zero_right at a525rw value at coprimeAN526have products := CongruentMod.zero_modulus at a * x, a * y527have same := products.forward with congruent528rw coprimeAN at same529haveleft : 1* x = x byring530haveright : 1* y = y byring531rw [left, right] at same532have characterization := CongruentMod.zero_modulus at x, y533exact characterization.backward with same534 . unfold CongruentMod at congruent535unfold CongruentMod536exact coprime.mul_cancel_mod at a, n, x, y537with nonzero, coprimeAN, congruent538539theorem CongruentMod.euler (a n : Nat)540requires aboveOne : 1 < n541requires coprimeAN : gcd(a, n) =1542shows CongruentMod(a ^ totient(n), 1, n)543proof544unfold CongruentMod545have power := euler.theorem with aboveOne, coprimeAN546have one := mod.one_left at n with aboveOne547rw [power, one]548rfl549550theorem CongruentMod.pow_totient_period (a k n : Nat)551requires aboveOne : 1 < n552requires coprimeAN : gcd(a, n) =1553shows CongruentMod(a ^ (k + totient(n)), a ^ k, n)554proof555have expand := pow.add at a, totient(n), k556rw expand557have unit := CongruentMod.euler with aboveOne, coprimeAN558have scaled := CongruentMod.mul_left at a ^ totient(n), 1, a ^ k, n with unit559have one : a ^ k *1= a ^ k byring560rw one at scaled561exact scaled562563theorem CongruentMod.fermat (a p : Nat)564requires prime : Prime(p)565requires notDivides : ¬(p ∣ a)566shows CongruentMod(a ^ (p - 1), 1, p)567proof568unfold CongruentMod569have power := fermat.little with prime, notDivides570have aboveOne := Prime.greater_than_one with prime571have one := mod.one_left at p with aboveOne572rw [power, one]573rfl574575theorem CongruentMod.fermat_pow (a p : Nat)576requires prime : Prime(p)577shows CongruentMod(a ^ p, a, p)578proof579have aboveOne := Prime.greater_than_one with prime580have positive : 1≤ p byarith581have predecessor := sub.add_cancel at p, 1with positive582have expand := pow.add_one at a, p - 1583rw predecessor at expand584rw expand585havesplit : p ∣ a ∨¬(p ∣ a) bytauto586casessplitwith yes | no587 . haveleft : p ∣ a * a ^ (p - 1)588exact divides.multiply_right at p, a, a ^ (p - 1) with yes589have zeroLeft := CongruentMod.zero_right_iff at a * a ^ (p - 1), p590have zeroRight := CongruentMod.zero_right_iff at a, p591have first := zeroLeft.backward withleft592have second := zeroRight.backward with yes593have swapped := CongruentMod.symmetric with second594exact CongruentMod.transitive at a * a ^ (p - 1), 0, a, p595with first, swapped596 . have unit := CongruentMod.fermat with prime, no597have scaled := CongruentMod.mul_left at a ^ (p - 1), 1, a, p with unit598have one : a *1= a byring599rw one at scaled600exact scaled601