A puzzle from the Sunzi Suanjing
The Sunzi Suanjing ("Master Sun's Mathematical Manual"), a Chinese text from roughly the third to fifth century CE, contains this problem:
There are certain things whose number is unknown. Counted by threes, two are left over; counted by fives, three are left over; counted by sevens, two are left over. How many things are there?
The answer given is . But why , how would you find it, are there other answers, and would the method work for any remainders and any divisors? Questions about remainders are everywhere: which day of the week 1 January 2050 falls on (remainders on division by ), whether a mobile-money account number or a European bank's IBAN has been mistyped (check digits use remainders on division by , or ), and — the subject of the cryptography lesson — how a phone in Lagos and a server in Singapore can agree on a secret key in public. This lesson develops the arithmetic of remainders, which Gauss called congruences, and proves its four great theorems: the Chinese remainder theorem and the theorems of Fermat, Euler and Wilson.
Wrapping the number line around a circle
Fix a positive integer and wrap the number line around a circle of circumference . Integers that land on the same spot differ by a multiple of and have the same remainder on division by . There are exactly landing spots, labelled : the integers fall into families.
The remarkable fact is that we can calculate with whole families. If you add any member of the family of to any member of the family of (modulo ), the answer always lies in the family of : , , . Try it below: draw random members of two families and watch their sum always land in the same family.
Remainder families
Colour each integer by its remainder after dividing by k. Every integer lands in exactly one family.
- [0]{ …, −15, −12, −9, −6, −3, 0, … }
- [1]{ …, −14, −11, −8, −5, −2, 1, … }
- [2]{ …, −13, −10, −7, −4, −1, 2, … }
Add a member of one family to a member of another…
That "the answer does not depend on which members we chose" is a theorem, not an accident. It fails for other operations — for division, for instance — and much of the lesson is about understanding exactly when arithmetic on families works.
Definitions
Throughout, is a positive integer, the modulus. Recall that (" divides ") means for some integer (see divisibility and Euclid's algorithm).
Definition— congruence modulo n
Integers and are congruent modulo , written , if . Equivalently, and leave the same remainder on division by . For instance , and .
Definition— residue classes and Z/nZ
The residue class of modulo is , the set of integers congruent to . The set of residue classes is written . Any choice of one integer from each class, such as , is a complete residue system.
Definition— inverse modulo n, Euler's totient
An inverse of modulo is an integer with . Euler's totient is the number of integers in that are coprime to . For example (the numbers ) and for a prime .
Definition— linear congruence, system of congruences
A linear congruence is a condition on an unknown integer . A system of congruences asks for satisfying several conditions at once — Sunzi's problem is the system , , .
Arithmetic with congruences
Theorem— Theorem 1 (congruence is an equivalence relation)
Congruence modulo is reflexive, symmetric and transitive. Its equivalence classes are exactly the classes .
Proof
Reflexive: . Symmetric: if then . Transitive: if and then .
By the division algorithm every integer can be written with , and then ; so every class is one of . These are distinct: if then , which is not a multiple of .
∎
Theorem— Theorem 2 (congruences can be added and multiplied)
If and , then and . Consequently for every , and for every polynomial with integer coefficients.
Proof
is a sum of two multiples of . For the product, add and subtract :
again a sum of multiples of . Powers follow by induction on (), and polynomials by combining sums, products and powers.
∎
Theorem 2 is precisely the statement that the operations and on residue classes are well defined — the answer does not depend on the representatives — so becomes a commutative ring (see algebraic structures).
Example— Casting out nines, and the last digit of a power
Since , Theorem 2 gives , so a number is congruent modulo to the sum of its digits: . Since , a number is congruent modulo to the alternating sum of its digits, read from the right: .
For the last digit of , compute modulo : , . As , we get . The last digit is .
Division is the delicate operation. Since and , we have — yet . Cancelling the is wrong, because shares a factor with . The cure is the following theorem.
Theorem— Theorem 3 (inverses modulo n)
An integer has an inverse modulo if and only if . In that case the inverse is unique modulo , and with implies .
Proof
If , then for some , so , and every common divisor of and divides ; thus . Conversely, if , Bézout's identity gives integers with , and then .
If and are both inverses, then . Finally, if is an inverse of and , multiply by : .
∎
The proof is constructive: Euclid's algorithm, run backwards, produces . For , : , , , so , and the inverse of modulo is .
Theorem— Theorem 4 (linear congruences)
Let . The congruence has a solution if and only if . In that case it has exactly solutions modulo , namely for any one solution .
Proof
means for some integer . Every number of the form is a multiple of , and by Bézout every multiple of has this form. So a solution exists exactly when .
Suppose and write , , with . Then if and only if , so the solutions are those of . By Theorem 3 this has exactly one solution modulo , i.e. the solutions form the single class . That class splits into the classes , , modulo .
∎
For example has solutions modulo , namely and , while has none: is even and is odd.
The Chinese remainder theorem
Theorem— Theorem 5 (Chinese remainder theorem)
If , then for all integers the system
has a solution, and any two solutions are congruent modulo .
Proof
Existence. By Bézout there are with . Then and , while and . So
satisfies and .
Uniqueness. If and are both solutions, then and . Write . Since and , (by Euclid's lemma in its general form, itself a consequence of Bézout: is a multiple of ). So .
∎
By induction the theorem extends to any number of pairwise coprime moduli : a system has a solution, unique modulo . (Combine the first two congruences into one modulo , which is coprime to each remaining , and repeat.)
Example— Sunzi's problem
Solve , , . Combine the first two: , so with , the formula gives , and indeed , . So . Now combine with : , so . The solutions are exactly the integers — that is, — and is the smallest positive one, as the Sunzi Suanjing says.
The theorem has a structural reading. The map
is well defined and preserves sums and products; uniqueness says it is injective, so (both sides having elements) it is a bijection — a ring isomorphism. Units correspond to pairs of units, so when . Since (only the multiples of fail to be coprime to ), this gives the formula
Without coprimality the theorem fails: forces odd and forces even, so the system has no solution. In general, , is solvable exactly when , and the solution is then unique modulo .
Fermat, Euler and Wilson
Theorem— Theorem 6 (Fermat's little theorem)
Let be prime. For every integer , . If , then .
We give two proofs. The first is the one formalised in Lean below; the second generalises to Euler's theorem.
Proof
First proof (binomial theorem and induction). For the binomial coefficient is divisible by : the prime divides the numerator , but none of the factors and of the denominator, so by Euclid's lemma it does not divide the denominator, and it must survive in the integer . Hence, by the binomial theorem,
Now induct on : , and if then . Every integer is congruent to some , so for all (Theorem 2). If , then and we may cancel one factor from (Theorem 3).
Second proof (rearranging residues). Suppose . The numbers are non-zero modulo (by Euclid's lemma) and pairwise incongruent (if then by cancellation). So modulo they are just in some order, and multiplying them all together,
Since is coprime to , we may cancel it: .
∎
Theorem— Theorem 7 (Euler's theorem)
If , then .
Proof
Let be the integers in coprime to . The products are again coprime to and pairwise incongruent modulo (cancel , Theorem 3), so modulo they are the in some order. Multiplying, with , and is coprime to , so we may cancel it.
∎
For prime, and Euler's theorem is Fermat's. In the language of groups, the classes coprime to form the group of order , and Euler's theorem is a special case of Lagrange's theorem — the order of every element divides the order of the group (see groups).
Example— Large powers without large numbers
- : by Fermat , and , so .
- The last two digits of : , so Euler gives . In fact already , so . The last two digits are .
The exponent may be reduced modulo (or modulo any with ) — never modulo , and only when .
Theorem— Theorem 8 (Wilson's theorem)
For every prime , . Conversely, if and , then is prime.
Proof
For : . Let be an odd prime. Each has a unique inverse in the same range (Theorem 3). An element is its own inverse when , i.e. , which by Euclid's lemma means or . All the other factors of pair off with their inverses, each pair contributing to the product. So .
Conversely, suppose is composite, with a divisor such that . Then is one of the factors of , so . If , then as well, so divides the difference — impossible.
∎
Wilson's theorem characterises the primes, but computing is far too slow to be a practical test.
The converse of Fermat's theorem fails
Fermat's theorem gives a quick way to prove a number composite without finding a factor: if for some coprime to , then is not prime. For example , so is composite. Is the converse true — if for all coprime to , must be prime?
No. The number is composite, yet:
Theorem— Proposition 9 (561 is a Carmichael number)
For every integer coprime to , .
Proof
Since , is coprime to each of , and . By Fermat, , and . Now , so
The moduli , , are pairwise coprime, so by the uniqueness part of the Chinese remainder theorem .
∎
Composite numbers with this property are called Carmichael numbers. The same argument proves Korselt's criterion in one direction: a squarefree composite such that for every prime is a Carmichael number (and the converse holds too). The next ones are and . Alford, Granville and Pomerance proved in 1994 that there are infinitely many. Practical primality tests, such as the Miller–Rabin test, strengthen Fermat's test so that no composite number fools it for more than a quarter of the bases.
Congruences in Lean and Mathlib
Mathlib writes a ≡ b [ZMOD n] for congruence of integers ([MOD n] for natural numbers) and defines it as "same remainder", a % n = b % n. The first declaration shows that this agrees with the lesson's definition ; after that, the proofs work with divisibility, exactly as on paper.
Equivalence and compatibility (Theorems 1 and 2). Each proof exhibits the same decomposition as the human proof — for transitivity, for products.
/-- **The definition used in the lesson.** `a ≡ b (mod n)` means `n ∣ a - b`. Mathlib defines
`a ≡ b [ZMOD n]` as "same remainder", `a % n = b % n`; its theorem `Int.modEq_iff_dvd`
(`n ∣ b - a`) shows the two definitions agree. -/
theorem modEq_iff_dvd_sub {n a b : ℤ} : a ≡ b [ZMOD n] ↔ n ∣ a - b :=
Int.modEq_iff_dvd.trans dvd_sub_comm/-- **Congruence modulo `n` is an equivalence relation.** Reflexive: `n ∣ 0 = a - a`.
Symmetric: `b - a = -(a - b)`. Transitive: `a - c = (a - b) + (b - c)`. -/
theorem modEq_equivalence (n : ℤ) : Equivalence (Int.ModEq n) where
refl a := modEq_iff_dvd_sub.mpr (by simp)
symm {a b} h := by
rw [modEq_iff_dvd_sub] at h ⊢
have h' : n ∣ -(a - b) := dvd_neg.mpr h
rwa [neg_sub] at h'
trans {a b c} h₁ h₂ := by
rw [modEq_iff_dvd_sub] at h₁ h₂ ⊢
have h : n ∣ (a - b) + (b - c) := dvd_add h₁ h₂
rwa [sub_add_sub_cancel] at h/-- **Congruences can be added.** If `a ≡ b` and `c ≡ d (mod n)` then `a + c ≡ b + d`, because
`(a + c) - (b + d) = (a - b) + (c - d)`. -/
theorem modEq_add {n a b c d : ℤ} (h₁ : a ≡ b [ZMOD n]) (h₂ : c ≡ d [ZMOD n]) :
a + c ≡ b + d [ZMOD n] := by
rw [modEq_iff_dvd_sub] at h₁ h₂ ⊢
have h : (a + c) - (b + d) = (a - b) + (c - d) := by ring
rw [h]
exact dvd_add h₁ h₂/-- **Congruences can be multiplied.** If `a ≡ b` and `c ≡ d (mod n)` then `ac ≡ bd`, because
`ac - bd = a(c - d) + d(a - b)` and `n` divides both terms. -/
theorem modEq_mul {n a b c d : ℤ} (h₁ : a ≡ b [ZMOD n]) (h₂ : c ≡ d [ZMOD n]) :
a * c ≡ b * d [ZMOD n] := by
rw [modEq_iff_dvd_sub] at h₁ h₂ ⊢
have h : a * c - b * d = a * (c - d) + d * (a - b) := by ring
rw [h]
exact dvd_add (dvd_mul_of_dvd_right h₂ a) (dvd_mul_of_dvd_right h₁ d)Inverses and cancellation (Theorem 3). Mathlib's IsCoprime a n means " for some " — Bézout's form of coprimality — and Int.isCoprime_iff_gcd_eq_one connects it to the gcd. The linear_combination tactic checks that one equation is a linear combination of others, which is exactly how the two directions are proved on paper.
/-- **You may not always cancel.** `2 · 3 ≡ 2 · 9 (mod 12)`, since both sides are `6 (mod 12)`,
but `3 ≢ 9 (mod 12)`. The factor `2` shares the divisor `2` with `12`. Checked by `decide`. -/
theorem cancel_fails : 2 * 3 ≡ 2 * 9 [ZMOD 12] ∧ ¬ 3 ≡ 9 [ZMOD 12] := by
decide/-- **`a` has an inverse modulo `n` exactly when `a` and `n` are coprime**, in Bézout's form:
`ab ≡ 1 (mod n)` for some `b` if and only if `ua + vn = 1` for some integers `u, v`
(Mathlib's `IsCoprime a n`).
* (→) If `n ∣ ab - 1`, say `ab - 1 = nk`, then `b · a + (-k) · n = 1`.
* (←) If `ua + vn = 1` then `au - 1 = -vn` is a multiple of `n`, so `u` is an inverse. -/
theorem exists_inverse_iff_isCoprime {a n : ℤ} : (∃ b, a * b ≡ 1 [ZMOD n]) ↔ IsCoprime a n := by
constructor
· rintro ⟨b, hb⟩
obtain ⟨k, hk⟩ := modEq_iff_dvd_sub.mp hb
exact ⟨b, -k, by linear_combination hk⟩
· rintro ⟨u, v, huv⟩
refine ⟨u, modEq_iff_dvd_sub.mpr ⟨-v, ?_⟩⟩
linear_combination huv/-- The same criterion with the gcd: `a` is invertible modulo `n` if and only if
`gcd(a, n) = 1`. This combines `exists_inverse_iff_isCoprime` with Mathlib's
`Int.isCoprime_iff_gcd_eq_one` (Bézout's identity and its converse). -/
theorem exists_inverse_iff_gcd {a n : ℤ} : (∃ b, a * b ≡ 1 [ZMOD n]) ↔ Int.gcd a n = 1 :=
exists_inverse_iff_isCoprime.trans Int.isCoprime_iff_gcd_eq_one/-- **Cancelling a coprime factor is allowed.** If `c` is invertible modulo `n` and
`ca ≡ cb`, multiply both sides by an inverse `u` of `c`: `a ≡ (uc)a = u(ca) ≡ u(cb) = (uc)b ≡ b`. -/
theorem cancel_of_isCoprime {n a b c : ℤ} (hc : IsCoprime c n) (h : c * a ≡ c * b [ZMOD n]) :
a ≡ b [ZMOD n] := by
obtain ⟨u, hu⟩ := exists_inverse_iff_isCoprime.mpr hc
have hu' : u * c ≡ 1 [ZMOD n] := by rwa [mul_comm] at hu
have ha : u * c * a ≡ 1 * a [ZMOD n] := modEq_mul hu' (Int.ModEq.refl a)
have hb : u * c * b ≡ 1 * b [ZMOD n] := modEq_mul hu' (Int.ModEq.refl b)
have hab : u * (c * a) ≡ u * (c * b) [ZMOD n] := modEq_mul (Int.ModEq.refl u) h
rw [one_mul] at ha hb
rw [← mul_assoc, ← mul_assoc] at hab
exact (ha.symm.trans hab).trans hbThe Chinese remainder theorem (Theorem 5), both halves, with the explicit solution .
/-- **Chinese remainder theorem, existence.** If `um + vn = 1`, then `x = a·vn + b·um` solves
`x ≡ a (mod m)` and `x ≡ b (mod n)`: modulo `m` we have `vn = 1 - um ≡ 1`, and modulo `n` we have
`um ≡ 1`. Each congruence is checked by exhibiting the quotient. -/
theorem crt_exists {m n : ℤ} (h : IsCoprime m n) (a b : ℤ) :
∃ x, x ≡ a [ZMOD m] ∧ x ≡ b [ZMOD n] := by
obtain ⟨u, v, huv⟩ := h
refine ⟨a * v * n + b * u * m, ?_, ?_⟩
· exact modEq_iff_dvd_sub.mpr ⟨b * u - a * u, by linear_combination a * huv⟩
· exact modEq_iff_dvd_sub.mpr ⟨a * v - b * v, by linear_combination b * huv⟩/-- **Chinese remainder theorem, uniqueness.** If `m, n` are coprime and `x ≡ y` modulo both
`m` and `n`, then `x ≡ y (mod mn)`: `m` and `n` both divide `x - y`, and coprime divisors
multiply (Mathlib's `IsCoprime.mul_dvd`, itself proved from Bézout). -/
theorem crt_unique {m n x y : ℤ} (h : IsCoprime m n) (hm : x ≡ y [ZMOD m])
(hn : x ≡ y [ZMOD n]) : x ≡ y [ZMOD m * n] :=
modEq_iff_dvd_sub.mpr (h.mul_dvd (modEq_iff_dvd_sub.mp hm) (modEq_iff_dvd_sub.mp hn))Fermat (Theorem 6), by the first proof. In ZMod p the binomial step is Mathlib's add_pow_char — " in characteristic ", whose proof is the divisibility of by . The induction is ours. Mathlib's own version of Fermat is cited for comparison, followed by Euler and Wilson, which we cite rather than prove.
/-- **Fermat's little theorem, first form: `aᵖ ≡ a (mod p)` for every `a`.** In `ZMod p` this
reads `xᵖ = x`. Proof by induction on `a`, as in the lesson: `0ᵖ = 0`, and
`(a + 1)ᵖ = aᵖ + 1ᵖ` because `p` divides every binomial coefficient `C(p, k)` with `0 < k < p`
(Mathlib's `add_pow_char`, "the freshman's dream" in characteristic `p`). Every residue is the
image of a natural number, which finishes the proof. -/
theorem pow_prime_eq_self (p : ℕ) [Fact p.Prime] (x : ZMod p) : x ^ p = x := by
have key : ∀ k : ℕ, (k : ZMod p) ^ p = k := by
intro k
induction k with
| zero => simp [zero_pow (Fact.out : p.Prime).ne_zero]
| succ k ih => rw [Nat.cast_succ, add_pow_char, ih, one_pow]
obtain ⟨k, rfl⟩ := ZMod.natCast_zmod_surjective x
exact key k/-- **Fermat's little theorem: if `p ∤ a` then `aᵖ⁻¹ ≡ 1 (mod p)`.** From `a · aᵖ⁻¹ = aᵖ = a`
we may cancel `a ≠ 0`, because `ZMod p` is a field. -/
theorem fermat_little (p : ℕ) [Fact p.Prime] {a : ZMod p} (ha : a ≠ 0) : a ^ (p - 1) = 1 := by
have hp : 1 ≤ p := (Fact.out : p.Prime).one_lt.le
have h : a * a ^ (p - 1) = a * 1 := by
rw [← pow_succ', Nat.sub_add_cancel hp, pow_prime_eq_self, mul_one]
exact mul_left_cancel₀ ha h/-- Fermat's little theorem for integers, from Mathlib (`Int.ModEq.pow_card_sub_one_eq_one`):
if `p` is prime and `a` is coprime to `p`, then `aᵖ⁻¹ ≡ 1 (mod p)`. -/
theorem fermat_library {p : ℕ} (hp : p.Prime) {a : ℤ} (ha : IsCoprime a p) :
a ^ (p - 1) ≡ 1 [ZMOD p] :=
Int.ModEq.pow_card_sub_one_eq_one hp ha/-- **Euler's theorem:** if `gcd(a, n) = 1` then `a^φ(n) ≡ 1 (mod n)`. Mathlib's
`Nat.ModEq.pow_totient`, proved there by Lagrange's theorem in the group of units of `ZMod n`. -/
theorem euler_totient {a n : ℕ} (h : Nat.Coprime a n) : a ^ Nat.totient n ≡ 1 [MOD n] :=
Nat.ModEq.pow_totient h/-- **Wilson's theorem:** `(p - 1)! ≡ -1 (mod p)` for every prime `p`. Mathlib's
`ZMod.wilsons_lemma`, proved by pairing each residue with its inverse. -/
theorem wilson (p : ℕ) [Fact p.Prime] : ((p - 1).factorial : ZMod p) = -1 :=
ZMod.wilsons_lemma pComputations. Lean can also simply check arithmetic. decide evaluates a decidable statement; decide +kernel asks Lean's trusted kernel to do the evaluation, which it does efficiently for large natural numbers. Neither proves anything general: the general facts are the theorems above.
/-- A worked example: `3¹⁰⁰ ≡ 4 (mod 7)`. By Fermat `3⁶ ≡ 1`, and `100 = 6 · 16 + 4`, so
`3¹⁰⁰ ≡ 3⁴ = 81 ≡ 4`. Here Lean simply checks the arithmetic with `decide`. -/
theorem big_power : 3 ^ 100 ≡ 4 [MOD 7] := by
decide/-- **The converse of Fermat fails.** `561 = 3 · 11 · 17` is not prime, yet `2⁵⁶⁰ ≡ 1 (mod 561)`:
561 passes Fermat's test to base 2 (in fact to every base coprime to it — it is the smallest
Carmichael number). `norm_num` certifies compositeness; `decide` checks the power. -/
theorem carmichael_561 : ¬ Nat.Prime 561 ∧ 2 ^ 560 ≡ 1 [MOD 561] := by
refine ⟨by norm_num, ?_⟩
decide +kernelNot formalised here: Theorem 4 on linear congruences, the multiplicativity of (Mathlib: Nat.totient_mul), the converse of Wilson's theorem (Mathlib: Nat.prime_iff_fac_equiv_neg_one), and Proposition 9 for all bases coprime to (only base is checked).
Solving congruences, and hunting impostors
The extended Euclidean algorithm is the engine behind Theorems 3, 4 and 5. The first experiment uses it to compute inverses, to solve linear congruences (watch have two solutions modulo and none) and to solve Sunzi's problem; it also checks that reduction is a bijection, while is not.
"""Solving congruences: inverses, linear congruences and the Chinese remainder theorem.
The extended Euclidean algorithm writes gcd(a, n) = ua + vn. From it the
experiment computes inverses modulo n, solves linear congruences ax = b
(mod n) - which have gcd(a, n) solutions or none - and combines congruences
with the Chinese remainder theorem, starting with the remainder problem from
the Sunzi Suanjing. It then checks by brute force that x -> (x mod 3, x mod 5,
x mod 7) is a bijection from Z/105Z onto Z/3Z x Z/5Z x Z/7Z, and that two
congruences with non-coprime moduli can be inconsistent. The answers are
exact; the bijection check is exhaustive for this one modulus.
"""
import math
def ext_gcd(a, b):
"""Return (g, u, v) with g = gcd(a, b) = u*a + v*b and g >= 0."""
old_r, r, old_u, u, old_v, v = a, b, 1, 0, 0, 1
while r:
q = old_r // r
old_r, r = r, old_r - q * r
old_u, u = u, old_u - q * u
old_v, v = v, old_v - q * v
if old_r < 0:
old_r, old_u, old_v = -old_r, -old_u, -old_v
return old_r, old_u, old_v
def inverse_mod(a, n):
"""The inverse of a modulo n in range(n), or None if gcd(a, n) != 1."""
g, u, _ = ext_gcd(a, n)
return u % n if g == 1 else None
def solve_linear(a, b, n):
"""All x in range(n) with a*x = b (mod n), found from the gcd."""
g, u, _ = ext_gcd(a, n)
if b % g:
return []
x0 = (u * (b // g)) % (n // g)
return [x0 + k * (n // g) for k in range(g)]
def crt(residues, moduli):
"""Solve x = r_i (mod m_i) for pairwise coprime m_i; return (x, M)."""
x, m = 0, 1
for r, n in zip(residues, moduli):
g, u, v = ext_gcd(m, n)
if g != 1:
raise ValueError("moduli must be pairwise coprime")
# u*m + v*n = 1, so x*v*n + r*u*m is x mod m and r mod n.
x = (x * v * n + r * u * m) % (m * n)
m *= n
return x, m
def crt_is_bijection(moduli):
"""True if x -> (x mod m_1, ...) hits every tuple exactly once."""
big = math.prod(moduli)
images = {tuple(x % n for n in moduli) for x in range(big)}
return len(images) == big
def consistent(r1, m1, r2, m2):
"""Brute force: is there an x with x = r1 (mod m1) and x = r2 (mod m2)?"""
return any(x % m1 == r1 % m1 and x % m2 == r2 % m2
for x in range(m1 * m2))
def main():
g, u, v = ext_gcd(240, 46)
print(f"Bezout: gcd(240, 46) = {g} = ({u})*240 + ({v})*46")
for a, n in [(7, 26), (17, 43), (6, 9)]:
print(f"inverse of {a} mod {n}: {inverse_mod(a, n)}")
for a, b, n in [(6, 4, 10), (6, 5, 10), (5, 3, 7)]:
print(f"{a}x = {b} (mod {n}): x in {solve_linear(a, b, n)}")
print()
x, m = crt([2, 3, 2], [3, 5, 7])
print("Sunzi: x = 2 (mod 3), x = 3 (mod 5), x = 2 (mod 7)")
print(f" x = {x} (mod {m})")
x, m = crt([2, 3, 4], [5, 7, 9])
print(f"x = 2 (mod 5), 3 (mod 7), 4 (mod 9): x = {x} (mod {m})")
print(f"Z/105 -> Z/3 x Z/5 x Z/7 bijective: {crt_is_bijection([3, 5, 7])}")
print(f"Z/24 -> Z/4 x Z/6 bijective: {crt_is_bijection([4, 6])}")
print("x = 1 (mod 4) and x = 2 (mod 6) solvable:", consistent(1, 4, 2, 6))
print("x = 1 (mod 4) and x = 3 (mod 6) solvable:", consistent(1, 4, 3, 6))
print()
print("Epistemic status: every answer is exact and can be checked by")
print("substituting it back; the bijection and consistency checks are")
print("exhaustive for these moduli only. That the method always works is")
print("the content of Bezout's identity and the Chinese remainder theorem.")
if __name__ == "__main__":
main()The second experiment checks Fermat, Euler and Wilson on every case in a range, then goes hunting for composite numbers that pass Fermat's test. Many composites pass for the single base — the first is — but are exposed by another base. Only seven below fool every coprime base.
"""Fermat, Euler and Wilson checked by machine - and the numbers that fool Fermat.
For every prime p < 200 and every a in 1..p-1 the experiment checks Fermat's
little theorem, for every n < 200 it checks Euler's theorem, and it confirms
Wilson's theorem in both directions: (n-1)! = -1 (mod n) exactly for the
primes n < 400. Then it searches all composite n < 10 000 for Fermat liars:
it finds the pseudoprimes to base 2, and tests each of them against every base
coprime to it, which leaves exactly the Carmichael numbers below 10 000. The
search is exhaustive and exact for the stated ranges, and proves nothing
beyond them.
"""
import math
def is_prime(n):
"""Trial division."""
return n > 1 and all(n % d for d in range(2, math.isqrt(n) + 1))
def phi(n):
"""Euler's totient by counting."""
return sum(1 for a in range(1, n + 1) if math.gcd(a, n) == 1)
def fermat_holds(p):
"""a^(p-1) = 1 (mod p) for every a in 1..p-1."""
return all(pow(a, p - 1, p) == 1 for a in range(1, p))
def euler_holds(n):
"""a^phi(n) = 1 (mod n) for every a in 1..n coprime to n."""
f = phi(n)
return all(pow(a, f, n) == 1 % n
for a in range(1, n + 1) if math.gcd(a, n) == 1)
def wilson_residue(n):
"""(n - 1)! mod n, computed with reductions along the way."""
r = 1 % n
for k in range(2, n):
r = r * k % n
return r
def fermat_liar_bases(n):
"""Bases a coprime to n with a^(n-1) = 1 (mod n)."""
return [a for a in range(1, n)
if math.gcd(a, n) == 1 and pow(a, n - 1, n) == 1]
def pseudoprimes_base2(limit):
"""Composite n < limit with 2^(n-1) = 1 (mod n).
Only odd n are tried: for even n, 2^(n-1) mod n is even, never 1.
"""
return [n for n in range(3, limit, 2)
if pow(2, n - 1, n) == 1 and not is_prime(n)]
def carmichael_numbers(limit):
"""Composite n < limit with a^(n-1) = 1 for every a coprime to n.
Such an n passes base 2 if it is odd, and it cannot be even: a = n - 1
would give (-1)^(n-1) = -1 = 1 (mod n), forcing n <= 2.
"""
return [n for n in pseudoprimes_base2(limit)
if len(fermat_liar_bases(n)) == phi(n)]
def korselt(n):
"""Korselt's criterion: n squarefree, and p-1 | n-1 for each p | n."""
primes = [p for p in range(2, n) if n % p == 0 and is_prime(p)]
return (len(primes) > 1 and all(n % (p * p) for p in primes)
and all((n - 1) % (p - 1) == 0 for p in primes))
def main():
primes = [p for p in range(2, 200) if is_prime(p)]
print(f"Fermat holds for all {len(primes)} primes below 200:",
all(fermat_holds(p) for p in primes))
print("Euler holds for every n from 1 to 199:",
all(euler_holds(n) for n in range(1, 200)))
wilson = [n for n in range(2, 400) if wilson_residue(n) == n - 1]
print("Wilson: (n-1)! = -1 (mod n) exactly for the primes below 400:",
wilson == [n for n in range(2, 400) if is_prime(n)])
print(" (n-1)! mod n for n = 4, 6, 8, 9:",
[wilson_residue(n) for n in (4, 6, 8, 9)])
print()
psp = pseudoprimes_base2(10_000)
print(f"Composite n < 10000 with 2^(n-1) = 1 (mod n): {len(psp)}")
print(" first few:", psp[:6])
print(" bases that expose 341:", [a for a in range(2, 20)
if math.gcd(a, 341) == 1
and pow(a, 340, 341) != 1][:4])
carm = carmichael_numbers(10_000)
print("Fool every coprime base (Carmichael numbers):")
print(" " + ", ".join(map(str, carm)))
print(" all satisfy Korselt's criterion:", all(korselt(n) for n in carm))
print()
print("Epistemic status: exact and exhaustive for the stated ranges. The")
print("checks agree with the theorems of Fermat, Euler and Wilson, which")
print("are proved in the lesson; the Carmichael numbers found here are")
print("genuine counterexamples to the converse of Fermat's theorem.")
if __name__ == "__main__":
main()Both experiments are exhaustive within their ranges, and exact. They illustrate the theorems; they do not prove them, and the Carmichael numbers they find show why no amount of checking Fermat's condition proves a number prime.
Explore: inverses on a clock, and Euclid's algorithm
On the "multiply" tab, pick a modulus and look for the classes with a partner — a with . Check that they are exactly the with , so that there are of them.
The clock laboratory
On a clock with k hours, counting wraps around. Explore adding and multiplying.
9 + 5 = 14 ≡ 2 (mod 12)
14 hours forward on a 12-hour clock lands on 2: the remainder when 14 is divided by 12.
Euclid's algorithm on and , drawn as squares cut from a rectangle. Reading the steps backwards gives , so .
Euclid's algorithm finds inverses
When the last square has side 1, the two numbers are coprime and inverses exist.
26 × 7
Step 1 of 5
A 26 by 7 rectangle. Press “Next step” to cut off the biggest squares that fit.
- …
Exercises
1. Congruences
Warm-upHumanWhich of these statements are true? (Choose all that apply.)
2. Congruences
Warm-upHumanWhat is the last digit of ?
3. Congruences
CoreHumanFind the inverse of modulo : the with .
4. Congruences
CoreHumanFind the smallest positive integer with , and .
5. Congruences
CoreHumanHow many solutions modulo does the congruence have?
6. Congruences
CoreHumanThe second proof of Fermat's theorem reaches and then cancels . What justifies the cancellation?
7. Congruences
StretchHumanClaim: "If for every coprime to , then is prime." Which number is a counterexample?
8. Congruences
CoreFormalWhich Lean statement correctly formalises the existence half of the Chinese remainder theorem? (IsCoprime m n means for some .)
9. Congruences
StretchComputationalWrite crt(residues, moduli) that returns the smallest with for all , assuming the moduli are pairwise coprime. Combine the congruences one at a time, using an inverse modulo (Python's pow(a, -1, m) computes one). Then check Sunzi's answer.
Common misconceptions
Misconception
Cancelling a factor that is not coprime to . , but . A factor may be cancelled when . In general, only gives : here .
Misconception
Assuming the converse of Fermat's theorem. for one base, or even for every base coprime to , does not make prime: satisfies for every coprime to . Fermat's test can prove a number composite; on its own it can never prove a number prime.
Misconception
Reducing exponents modulo . , but . Bases may be reduced modulo (Theorem 2); exponents may be reduced modulo , and only for bases coprime to (Euler).
Misconception
"The Chinese remainder theorem works for any moduli." It needs coprime moduli. and have no common solution (one makes odd, the other even). With non-coprime moduli the system is solvable exactly when the remainders agree modulo the gcd.
Misconception
" means is the remainder of ." Congruence is a symmetric relation between two integers, and either may be negative or larger than : and are both true. (Programming languages also disagree about the remainder of a negative number: in some, is , in others . Congruence does not care.)
Many ways of seeing
Algebra— Quotient rings and an isomorphism
The multiples of form an ideal of the ring , and is the quotient ring: reduction modulo is a surjective ring homomorphism with kernel (see quotients). Theorem 2 is the statement that this quotient exists. The Chinese remainder theorem is the ring isomorphism for coprime , and the same statement holds in any commutative ring for coprime ideals . Euler's theorem is Lagrange's theorem for the group . A deeper fact due to Gauss: this group is cyclic — it has an element, a primitive root, whose powers give every unit — exactly when is or for an odd prime . For example is a primitive root modulo : its powers are .
Topology— Wrapping a line around a circle, and walking on a torus
The map , , wraps the real line around the circle, sending two numbers to the same point exactly when they differ by an integer: . It is the basic example of a covering map. Reduction modulo is its discrete shadow: is a group isomorphism from to the -th roots of unity, the corners of a regular -gon inscribed in the circle. The Chinese remainder theorem has a picture too. is an grid whose opposite edges are glued — a discrete torus — and the element walks diagonally across it. The walk returns to its start after steps, so it visits all cells exactly when : the same reason a line of rational slope on a torus closes up, and covers a lot of it when and are coprime.
Analysis— Congruence as closeness: the p-adic numbers
Fix a prime and call two integers close when their difference is divisible by a high power of : the -adic absolute value , where is the largest power of dividing , makes equivalent to . A sequence of congruences modulo , each refining the last, is then a Cauchy sequence, and completing for this metric gives the -adic integers — exactly as completing for the usual absolute value gives (see constructing the number systems). Newton's method has a -adic version, Hensel's lemma: if and , the root lifts uniquely modulo every . For instance lifts to , and so on for ever: has a square root in .
Combinatorics— Fermat's theorem by counting necklaces
Here is a third proof of , for . Count the strings of beads, each in one of colours: there are . Exactly of them are single-coloured. Rotating a string by one place generates a group of rotations acting on the strings, and a string's orbit has size dividing — so size or , since is prime. Size means the string is unchanged by rotation, i.e. single-coloured. So the multi-coloured strings fall into orbits of exactly strings each (the distinct necklaces), and . The argument fails for composite : a string such as ABAB has an orbit of size under the four rotations.
Computation— Fast powers and the security of the internet
Computing by multiplying by itself times is hopeless when has hundreds of digits. Repeated squaring — , , reducing modulo at every step — needs only about multiplications of numbers below . Together with Euclid's algorithm for inverses, this makes it fast to compute with congruences modulo numbers with hundreds of digits, which is what RSA and Diffie–Hellman key exchange do (see cryptography). Computers also use the Chinese remainder theorem to speed up arithmetic: a computation modulo can be done separately modulo and modulo and the results recombined, and RSA implementations decrypt this way (see algorithms and efficiency).
Wonder Trails
History and global context
- China. The remainder problem above appears in the Sunzi Suanjing (third to fifth century CE). Qin Jiushao's Shushu Jiuzhang ("Mathematical Treatise in Nine Sections", 1247) gave a general algorithm, the dayan qiuyi shu ("great extension method of finding one"), for solving systems of congruences, including the step of finding an inverse modulo a number.
- India. Āryabhaṭa's Āryabhaṭīya (499 CE) describes the kuṭṭaka ("pulveriser"), a method for solving linear equations in integers by repeated division — Euclid's algorithm run backwards, the heart of Theorems 3 and 4. Brahmagupta and Bhāskara II developed it further.
- The Islamic world. Ibn al-Haytham (around 1000 CE) solved problems of the Sunzi type, finding numbers that leave remainder on division by and are divisible by , in a way that suggests he knew the statement now called Wilson's theorem.
- Europe. Pierre de Fermat stated his little theorem in a letter to Frénicle de Bessy in 1640, without proof. Gottfried Wilhelm Leibniz wrote a proof that he did not publish; Leonhard Euler published proofs from 1736 and in 1763 proved the generalisation with . Edward Waring published Wilson's theorem in 1770, attributing it to his student John Wilson, and Joseph-Louis Lagrange proved it in 1771. Carl Friedrich Gauss introduced the word congruence and the symbol in his Disquisitiones Arithmeticae (1801).
- Impostors. Václav Šimerka listed the first seven Carmichael numbers in 1885; Alwin Korselt gave his criterion in 1899; Robert Carmichael found independently in 1910. Alford, Granville and Pomerance proved in 1994 that there are infinitely many.
Further reading
- Kenneth Ireland and Michael Rosen, A Classical Introduction to Modern Number Theory (1982; 2nd ed. 1990) — congruences, primitive roots and far beyond.
- Carl Friedrich Gauss, Disquisitiones Arithmeticae (1801; English translation by Arthur A. Clarke, 1966) — where the theory of congruences began.
- G. H. Hardy and E. M. Wright, An Introduction to the Theory of Numbers (1938; 6th ed. 2008) — chapters 5–8 on congruences.
- Ivan Niven, Herbert S. Zuckerman and Hugh L. Montgomery, An Introduction to the Theory of Numbers (5th ed. 1991) — a standard undergraduate text.
- Joseph H. Silverman, A Friendly Introduction to Number Theory (1997; 4th ed. 2012) — gentle, with a chapter on cryptography.