Extended Euclidean Algorithm & Modular Inverse — Middle Level¶
Focus: the iterative extended Euclidean algorithm, inverse via extended Euclid vs Fermat (and when each applies), the
O(n)inverse table for1..n, solving the general linear congruencea·x ≡ b (mod m)(when it is solvable, and all its solutions), and the comparisons that let you pick the right tool.
Table of Contents¶
- Introduction
- Deeper Concepts
- Comparison with Alternatives
- Advanced Patterns
- The O(n) Inverse Table
- Solving Linear Congruences
- Code Examples
- Error Handling
- Performance Analysis
- Best Practices
- Visual Animation
- Summary
Introduction¶
At junior level the message was a chain of three facts: extended Euclid returns (g, x, y) with a·x + b·y = g; reducing mod m turns x into the inverse a⁻¹ when g = 1; and the inverse exists exactly when a and m are coprime. At middle level you start asking the engineering and mathematical questions that decide whether your solution is correct, fast, and general:
- How do you write extended Euclid iteratively so there is no recursion stack and the coefficient updates are explicit?
- Inverse via extended Euclid vs inverse via Fermat (
a^(p-2)): which is faster, which is more general, and when must you use one over the other? - How do you compute the inverses of all of
1..nmod a prime inO(n)total — notO(n log m)— using a single recurrence? - The general linear congruence
a·x ≡ b (mod m): when does it have a solution, how many, and what is the complete solution set? - How do these tools combine in CRT and linear Diophantine equations?
These are the questions that separate "I can invert one number" from "I can solve a family of congruences and pick the optimal method for the constraints in front of me."
Deeper Concepts¶
Extended Euclid, restated as a matrix recurrence¶
Each Euclidean step (r_{k-1}, r_k) → (r_k, r_{k-1} - q_k r_k) is a linear map. Track, alongside each remainder r, a pair (s, t) of coefficients such that the invariant
holds at every step. Initialize the first two rows as r₀ = a with (s₀, t₀) = (1, 0) and r₁ = b with (s₁, t₁) = (0, 1). Then the update r_{k+1} = r_{k-1} - q_k r_k (with q_k = r_{k-1} / r_k) is applied identically to r, s, and t. When r reaches 0, the previous row holds (g, x, y). This invariant is the entire correctness argument and the reason the iterative form works.
Why x is the inverse, precisely¶
From a·x + m·y = 1, reduce mod m: the m·y term is ≡ 0, so a·x ≡ 1 (mod m). The Bezout x is therefore a multiplicative inverse. It is unique mod m: if both x and x' satisfy a·x ≡ a·x' ≡ 1, then a(x - x') ≡ 0, and since a is invertible we get x ≡ x' (mod m). So there is one canonical inverse in [0, m).
The matrix view of the iteration¶
Each Euclidean step is a left-multiplication by an elementary matrix. If you stack [a, b] as a column and apply [[0, 1], [1, -q_k]] repeatedly, the product of these matrices is a 2×2 integer matrix T with det(T) = ±1 (unimodular) such that T·[a, b]ᵀ = [g, 0]ᵀ. The first row of T is exactly (x, y). This is why tracking two coefficient columns through the iteration recovers Bezout: you are accumulating the unimodular transform that diagonalizes the input. It also explains the bound |x| ≤ m/2 — a unimodular transform cannot blow up the coefficients arbitrarily.
Inverse existence is iff coprimality¶
This is worth restating as the governing law: a has an inverse mod m if and only if gcd(a, m) = 1. The forward direction is Bezout (coprime → inverse exists). The reverse: if gcd(a, m) = d > 1, then every multiple a·x is divisible by d, so a·x mod m always lands in the sublattice of multiples of d, which excludes 1. No inverse can exist. Every algorithm in this file checks g == 1 (or g | b for the general congruence) as its first correctness gate.
The family of Bezout coefficients¶
Extended Euclid returns one pair (x, y), but there are infinitely many. If a·x + m·y = g, then for any integer k:
is also valid (the added terms cancel). So the coefficient x is determined only modulo m/g. For the inverse case (g = 1), this means x is determined mod m — which is exactly why the inverse (a residue mod m) is unique even though the integer x is not. The algorithm returns the "small" representative; you normalize it into [0, m). Understanding this family explains why two correct implementations can return different raw x values that nonetheless give the same inverse after reduction.
Comparison with Alternatives¶
Extended Euclid inverse vs Fermat inverse¶
| Aspect | Extended Euclid | Fermat (a^(m-2)) |
|---|---|---|
| Modulus requirement | any m with gcd(a, m) = 1 | m must be prime |
| Formula | solve a·x + m·y = 1, take x mod m | a⁻¹ ≡ a^(m-2) mod m |
| Complexity | O(log m) | O(log m) (fast power) |
| Detects "no inverse" | yes — returns g ≠ 1 | no — silently wrong if gcd ≠ 1 |
| Constant factor | a few divisions per step | ~log m modular multiplications |
| Best when | composite modulus, need Bezout coeffs, CRT, Diophantine | known prime modulus, only need the inverse |
Both are logarithmic. Extended Euclid is strictly more general (works for any coprime m) and tells you when no inverse exists. Fermat is a clean one-liner when m is a known prime, and it composes nicely with an existing fast-power routine. Rule of thumb: prime modulus and just an inverse → Fermat; anything else → extended Euclid. For many inverses, neither — use the table below.
Worked side-by-side. Compute 7⁻¹ mod 13 both ways:
Extended Euclid: 13 = 1·7 + 6; 7 = 1·6 + 1; back: 1 = 7 - 6 = 7 - (13 - 7) = 2·7 - 13.
so 7·2 ≡ 1 (mod 13) → 7⁻¹ = 2.
Fermat: 7^(13-2) = 7^11 mod 13. Since 7^2 = 49 ≡ 10, 7^4 ≡ 100 ≡ 9, 7^8 ≡ 81 ≡ 3,
7^11 = 7^8·7^2·7^1 ≡ 3·10·7 = 210 ≡ 2 (mod 13). → 7⁻¹ = 2. ✓
Both give 2. Extended Euclid took 2 divisions; Fermat took ~4 multiplications (the squarings for 7^11). On a composite modulus (say 12), the Fermat line would compute 7^10 mod 12 = 1 — wrong, because 12 is not prime. That single failure mode is why you must know which method the modulus permits.
Euler's theorem generalization¶
Fermat is the prime case of Euler's theorem: if gcd(a, m) = 1, then a^φ(m) ≡ 1 (mod m), so a⁻¹ ≡ a^(φ(m)-1) (mod m). This works for composite m if you know φ(m) (Euler's totient). But computing φ(m) requires factoring m, which is often harder than just running extended Euclid. So Euler-based inversion is mostly of theoretical interest; extended Euclid remains the practical general tool.
Comparison table: choosing an inverse method¶
| Situation | Method | Why |
|---|---|---|
| One inverse, prime modulus | Fermat a^(p-2) | simplest, reuses fast power |
| One inverse, composite modulus | extended Euclid | Fermat does not apply |
| One inverse, modulus may not be coprime | extended Euclid | only it detects g ≠ 1 |
All of 1..n mod prime p > n | O(n) table trick | linear, beats n separate calls |
| Need Bezout coefficients too | extended Euclid | Fermat gives only the inverse |
| Inside CRT / Diophantine | extended Euclid | those are Bezout applications |
Advanced Patterns¶
Pattern: Iterative extended Euclid (production form)¶
This is the recommended default — no recursion, explicit invariant, easy to read.
def extgcd(a, b):
old_r, r = a, b
old_s, s = 1, 0
old_t, t = 0, 1
while r != 0:
q = old_r // r
old_r, r = r, old_r - q * r
old_s, s = s, old_s - q * s
old_t, t = t, old_t - q * t
return old_r, old_s, old_t # (g, x, y) with a*x + b*y = g
Pattern: Safe modular inverse with existence check¶
def mod_inverse(a, m):
g, x, _ = extgcd(a % m, m)
if g != 1:
return None # no inverse: gcd(a, m) != 1
return x % m
Pattern: Inverse as modular division helper¶
def mod_div(a, b, m):
"""Compute a / b mod m = a * b^{-1} mod m, or None if b not invertible."""
inv = mod_inverse(b, m)
if inv is None:
return None # b has no inverse mod m
return a * inv % m
This is the everyday "division mod a prime": you never literally divide, you multiply by the inverse. It is correct only when gcd(b, m) = 1; for the more general b·x ≡ a (mod m) (which can have solutions even when b⁻¹ does not exist), use the full congruence solver instead.
Pattern: Fermat inverse for a prime modulus¶
Worked iterative trace¶
Run the iterative form on (a, m) = (3, 11) to find 3⁻¹ mod 11. Each row is (r, s, t) with r = 3s + 11t:
row r s t q = floor(prev_r / r)
0 3 1 0 (init: 3 = 1·3 + 0·11)
1 11 0 1 q = 3 / 11 = 0
2 3 1 0 ... after first step (swap) -- 3 = 1·3
3 ... (continues) eventually r = 1 with s = 4
More cleanly, since 3 < 11 the algorithm effectively runs on (11, 3): 11 = 3·3 + 2, 3 = 1·2 + 1, 2 = 2·1 + 0. Back-tracking the coefficients gives 3·4 + 11·(-1) = 1, so the row with r = 1 has s = 4. Thus 3⁻¹ ≡ 4 (mod 11). The invariant r = 3s + 11t holds on every row — the single fact that makes the iterative form correct and easy to debug: print the rows and check the invariant.
The O(n) Inverse Table¶
A frequent need (e.g., precomputing factorials and their inverses for binomial coefficients) is the inverse of every integer 1..n modulo a prime p (with p > n, so every i is coprime to p). Doing n separate O(log p) inversions costs O(n log p). There is a beautiful O(n) recurrence.
Derivation¶
Fix i with 1 < i < p. Write p = q·i + r where q = p / i (floor) and r = p mod i, so 0 < r < i. Reduce mod p:
Multiply both sides by i⁻¹ · r⁻¹ (both exist since i, r < p are coprime to the prime p):
Because r = p mod i < i, the value (p mod i)⁻¹ was already computed earlier in the pass. So each inv[i] is one multiply and one negate, given the base case inv[1] = 1. Total: O(n).
Why the modulus must be prime (and > n)¶
The recurrence multiplies by (p mod i)⁻¹, which must exist for every i ≤ n. That holds when p is prime and p > n, guaranteeing gcd(i, p) = 1 for all 1 ≤ i ≤ n. For a composite modulus, some i may share a factor with m and have no inverse, breaking the table. Use a prime modulus larger than n.
Worked check¶
For p = 11, the recurrence yields inv[1..10] = [1, 6, 4, 3, 9, 2, 8, 7, 5, 10]. Verify a couple: 2·6 = 12 ≡ 1, 3·4 = 12 ≡ 1, 7·8 = 56 = 55 + 1 ≡ 1. All correct, and produced in a single linear pass.
Factorial inverses: the companion trick¶
A close cousin precomputes inverse factorials for binomial coefficients C(n, k) = n! / (k!(n-k)!) mod p. Instead of inverting each factorial separately, invert only fact[n] once, then sweep backward:
fact[0] = 1; for i in 1..n: fact[i] = fact[i-1]·i % p
invfact[n] = inverse(fact[n], p) # ONE inversion (Fermat or extgcd)
for i in n..1: invfact[i-1] = invfact[i]·i % p
This gives all n+1 factorial inverses in O(n) with a single inversion, because invfact[i-1] = 1/(i-1)! = (1/i!)·i = invfact[i]·i. The everyday competitive-programming pattern: precompute fact and invfact once, answer each C(n, k) in O(1). Element inverses inv[i] = invfact[i]·fact[i-1] fall out of the same tables.
Solving Linear Congruences¶
The general problem: solve a·x ≡ b (mod m) for x. This is the workhorse behind CRT, Diophantine equations, and countless "find x" problems.
Solvability¶
Let g = gcd(a, m). The congruence a·x ≡ b (mod m) has a solution if and only if g | b (g divides b). The reason: a·x and m together can only produce multiples of g (Bezout: the set {a·x mod m} is exactly the multiples of g in [0, m)), so b must be such a multiple.
Number of solutions¶
When solvable, there are exactly g distinct solutions modulo m, and they are evenly spaced m/g apart. So:
g = 1: a unique solution modm(the common, clean case —ais invertible).g > 1andg | b:gsolutions, namelyx₀, x₀ + m/g, x₀ + 2m/g, …, x₀ + (g-1)m/g(modm).g ∤ b: no solution.
The algorithm¶
- Compute
g = gcd(a, m)via extended Euclid, also gettingx'witha·x' + m·y' = g. - If
g ∤ b, report no solution. - Otherwise, scale: a particular solution is
x₀ = x' · (b / g) mod (m / g). Reduce into[0, m/g). - The full set is
{ x₀ + t·(m/g) : t = 0, 1, …, g-1 }modm.
Worked example¶
Solve 6·x ≡ 8 (mod 10). Here g = gcd(6, 10) = 2, and 2 | 8, so there are 2 solutions. Extended Euclid on (6, 10) gives 6·2 + 10·(-1) = 2, so x' = 2. Scale: b/g = 4, m/g = 5, x₀ = 2·4 mod 5 = 8 mod 5 = 3. The solutions mod 10 are 3 and 3 + 5 = 8. Check: 6·3 = 18 ≡ 8 (mod 10) ✓ and 6·8 = 48 ≡ 8 (mod 10) ✓.
Contrast 6·x ≡ 7 (mod 10): g = 2 does not divide 7, so no solution — no multiple of 6 is ever ≡ 7 (mod 10), because 6·x mod 10 is always even.
Cleaner approach: reduce to the coprime case¶
The scaling step x₀ = x'·(b/g) mod (m/g) is a little fiddly. A cleaner equivalent is to divide out g first:
a·x ≡ b (mod m), g = gcd(a, m), require g | b
⟹ (a/g)·x ≡ (b/g) (mod m/g) # now gcd(a/g, m/g) = 1
⟹ x₀ = (a/g)⁻¹ · (b/g) mod (m/g) # unique inverse exists
⟹ solutions = x₀ + t·(m/g), t = 0..g-1 (mod m)
Dividing through by g lands you in a coprime congruence with a unique inverse, which you solve cleanly, then lift to the g solutions mod m. This avoids reasoning about scaled Bezout coefficients and is the recommended production form.
Worked reduction¶
8·x ≡ 6 (mod 14): g = gcd(8, 14) = 2, 2 | 6 ✓. Reduce: 4·x ≡ 3 (mod 7). Now 4⁻¹ mod 7 = 2 (4·2 = 8 ≡ 1), so x₀ = 2·3 = 6 mod 7 = 6. Lift: solutions mod 14 are 6 and 6 + 7 = 13. Check: 8·6 = 48 ≡ 6 (mod 14) ✓, 8·13 = 104 = 7·14 + 6 ≡ 6 (mod 14) ✓.
Code Examples¶
Solve a·x ≡ b (mod m) returning all solutions¶
Go¶
package main
import "fmt"
func extgcd(a, b int64) (int64, int64, int64) {
if b == 0 {
return a, 1, 0
}
g, x1, y1 := extgcd(b, a%b)
return g, y1, x1 - (a/b)*y1
}
// solveCongruence returns all solutions of a*x ≡ b (mod m) in [0, m), or nil if none.
func solveCongruence(a, b, m int64) []int64 {
a = ((a % m) + m) % m
b = ((b % m) + m) % m
g, x, _ := extgcd(a, m)
if b%g != 0 {
return nil // no solution
}
step := m / g
x0 := ((x*(b/g))%step + step) % step // particular solution mod m/g
sols := make([]int64, 0, g)
for t := int64(0); t < g; t++ {
sols = append(sols, (x0+t*step)%m)
}
return sols
}
func main() {
fmt.Println(solveCongruence(6, 8, 10)) // [3 8]
fmt.Println(solveCongruence(6, 7, 10)) // [] (no solution)
fmt.Println(solveCongruence(3, 1, 11)) // [4] (the inverse of 3)
}
Java¶
import java.util.*;
public class LinearCongruence {
static long[] extgcd(long a, long b) {
if (b == 0) return new long[]{a, 1, 0};
long[] r = extgcd(b, a % b);
return new long[]{r[0], r[2], r[1] - (a / b) * r[2]};
}
static List<Long> solve(long a, long b, long m) {
a = ((a % m) + m) % m;
b = ((b % m) + m) % m;
long[] r = extgcd(a, m);
long g = r[0], x = r[1];
List<Long> sols = new ArrayList<>();
if (b % g != 0) return sols; // no solution
long step = m / g;
long x0 = ((x * (b / g)) % step + step) % step;
for (long t = 0; t < g; t++) sols.add((x0 + t * step) % m);
return sols;
}
public static void main(String[] args) {
System.out.println(solve(6, 8, 10)); // [3, 8]
System.out.println(solve(6, 7, 10)); // []
System.out.println(solve(3, 1, 11)); // [4]
}
}
Python¶
def extgcd(a, b):
if b == 0:
return a, 1, 0
g, x1, y1 = extgcd(b, a % b)
return g, y1, x1 - (a // b) * y1
def solve_congruence(a, b, m):
"""All solutions of a*x ≡ b (mod m) in [0, m); empty list if none."""
a %= m
b %= m
g, x, _ = extgcd(a, m)
if b % g != 0:
return [] # no solution
step = m // g
x0 = (x * (b // g)) % step # particular solution mod m/g
return [(x0 + t * step) % m for t in range(g)]
if __name__ == "__main__":
print(solve_congruence(6, 8, 10)) # [3, 8]
print(solve_congruence(6, 7, 10)) # []
print(solve_congruence(3, 1, 11)) # [4] (= 3^-1 mod 11)
The O(n) inverse table (counting semiring, prime modulus)¶
def inverse_table(n, p):
"""inv[i] = i^{-1} mod p for i in 1..n, in O(n). Requires p prime and p > n."""
inv = [0] * (n + 1)
inv[1] = 1
for i in range(2, n + 1):
inv[i] = (-(p // i) * inv[p % i]) % p
return inv
if __name__ == "__main__":
print(inverse_table(10, 11)) # [0, 1, 6, 4, 3, 9, 2, 8, 7, 5, 10]
Error Handling¶
| Scenario | What goes wrong | Correct approach |
|---|---|---|
| Fermat on composite modulus | a^(m-2) is not the inverse | use extended Euclid for non-prime m |
| No existence check | returns a number when gcd ≠ 1 | gate on g == 1 (inverse) or g | b (congruence) |
| Negative result | raw Bezout x or scaled x0 is negative | normalize: ((x % step) + step) % step, then into [0, m) |
Overflow in x * (b/g) | product exceeds 64-bit | use 128-bit product, or reduce factors mod step first |
| O(n) table on composite mod | some i not invertible → wrong table | require prime p > n |
Solving with m = 0 | division by zero | reject m ≤ 0; modulus must be positive |
| Dropping extra solutions | reported only x0 when g > 1 | emit all g solutions spaced m/g apart |
Confusing x and y | returned y as the inverse | the inverse is x (coefficient of a) |
inv[0] accessed in table | index 0 left as junk | never query inv[0]; start at inv[1] = 1 |
Worked overflow scenario¶
For m = 1_000_000_007 and a mid-run coefficient s ≈ 5·10^8 with quotient q ≈ 2, the product q*s ≈ 10^9 fits in 64 bits — but for m near 2^31 with larger quotients, q*s can approach m² ≈ 10^18, which still fits int64 (max ≈ 9.2·10^18) but the post-inversion multiply value * inv with both near 10^9 gives 10^18, also borderline. For m near 2^63, both overflow; use 128-bit products. The safe habit: any (big * big) % m uses a mulmod helper.
Performance Analysis¶
| Task | Naive | Optimized | When optimized matters |
|---|---|---|---|
| One inverse | O(log m) extended Euclid | same | always fine |
n inverses (separate) | O(n log m) | — | acceptable for small n |
n inverses (table) | — | O(n) | precomputing factorials/binomials |
Solve a·x ≡ b (mod m) | O(log m) | same | always fine |
Many congruences, same m | O(q log m) | precompute a⁻¹ once if a fixed | repeated divisions by the same a |
The two big wins at this level: the O(n) table turns O(n log m) into O(n) (a log m ≈ 30–60× speedup at scale), and choosing Fermat vs extended Euclid correctly avoids both wrong answers (Fermat on composite) and unnecessary generality.
import time
def bench(n, p):
start = time.perf_counter()
inverse_table(n, p) # O(n)
t_table = time.perf_counter() - start
start = time.perf_counter()
[pow(i, p - 2, p) for i in range(1, n + 1)] # O(n log p)
t_loop = time.perf_counter() - start
return t_table, t_loop
# For n = 10^6, p = 998244353: the table is roughly 20-40x faster than n Fermat calls.
Connections: CRT and Linear Diophantine¶
The extended Euclidean algorithm is not a standalone trick — it is the shared engine of two sibling topics you will meet next.
Linear Diophantine equations (sibling 07)¶
The equation a·x + b·y = c (find integers x, y) is solvable iff gcd(a, b) | c — the same divisibility gate as the congruence. The base solution comes straight from Bezout: if a·x₀ + b·y₀ = g and g | c, then (x₀·(c/g), y₀·(c/g)) is a particular solution, and the general solution adds multiples of (b/g, -a/g). So solving a·x ≡ b (mod m) is the same problem as the Diophantine a·x - m·y = b. Master one and you have the other.
Chinese Remainder Theorem (sibling 08)¶
CRT merges congruences x ≡ r₁ (mod m₁), x ≡ r₂ (mod m₂) (coprime moduli) into a single x mod (m₁·m₂). The merge formula is
and the crucial ingredient m₁⁻¹ mod m₂ is a modular inverse — exactly what extended Euclid produces. CRT is "one inverse per modulus, then a weighted sum." Every modular-inverse skill in this file is a prerequisite for CRT.
Worked CRT merge¶
Merge x ≡ 2 (mod 3) and x ≡ 3 (mod 5). Here m₁⁻¹ mod m₂ = 3⁻¹ mod 5 = 2 (since 3·2 = 6 ≡ 1). Then x = 2 + 3·((3 - 2)·2 mod 5) = 2 + 3·2 = 8. Check: 8 mod 3 = 2 ✓, 8 mod 5 = 3 ✓. The unique answer mod 15 is 8.
The single inverse 3⁻¹ mod 5 is the only "hard" part — everything else is multiplication and addition. This is the recurring shape: inverses are the scarce, expensive primitive, so you compute each one once and reuse it.
Best Practices¶
- Default to iterative extended Euclid — no stack, explicit invariant, detects non-existence.
- Use the O(n) table whenever you need inverses of a contiguous range mod a prime; never loop
nseparate inversions. - Pick Fermat only for a single inverse mod a known prime, reusing an existing fast-power.
- Always emit the full solution set of
a·x ≡ b (mod m):gsolutions wheng | b, none otherwise. - Normalize everything into the canonical residue range before returning.
- Test with
(a * inv) % m == 1and, for congruences, plug each solution back intoa·x ≡ b. - Reduce
amodmfirst so the algorithm runs on the smallest representative and products stay small. - Wrap big multiplications in a
mulmodhelper so a forgotten overflow never silently corrupts results. - Prefer the coprime-reduction form of the congruence solver over scaled Bezout coefficients — fewer sign traps.
Visual Animation¶
See
animation.htmlfor an interactive view.The middle-level view highlights: - The forward Euclidean table of quotients
qand remaindersr- The back-substitution assemblingx, ywitha·x + b·y = g- Reading the inversex mod mwheng = 1, and editablea,m
Decision Guide¶
| You have… | Use | Why |
|---|---|---|
| one inverse, prime modulus | Fermat a^(p-2) | one modpow, clean |
| one inverse, composite modulus | extended Euclid | Fermat invalid |
| modulus possibly non-coprime | extended Euclid | only it detects g ≠ 1 |
inverses of 1..n mod prime | O(n) table | linear, no inversions |
factorial inverses for C(n,k) | one inversion + backward sweep | O(n) total |
solve a·x ≡ b (mod m) | extended Euclid + reduce by g | full solution set |
| merge two congruences | CRT (needs an inverse) | unique result mod product |
Summary¶
The iterative extended Euclidean algorithm maintains the invariant r = s·a + t·b at every step, so when r hits 0 the previous row is exactly (g, x, y) with a·x + b·y = g. Reducing a·x + m·y = 1 mod m gives the modular inverse x, which exists iff gcd(a, m) = 1. Compared with Fermat's a^(p-2) (prime modulus only), extended Euclid is more general and detects non-existence. For inverses of all of 1..n mod a prime, the recurrence inv[i] = -(m/i)·inv[m%i] % m runs in O(n). The general congruence a·x ≡ b (mod m) is solvable iff gcd(a, m) | b, and then has exactly gcd(a, m) solutions spaced m/gcd(a, m) apart — cleanest to solve by dividing out g into a coprime congruence and lifting. These pieces — Bezout, inverse, congruence solving — are the building blocks of CRT (sibling 08) and linear Diophantine equations (sibling 07).
Key takeaways to carry into senior and professional study:
- The Bezout coefficient
xis unique only modm/g; the inverse (residue modm) is unique. Normalize. - "Inverse exists" and "coprime" are the same statement — always gate on
gcd(a, m) = 1. - Fermat is a prime-only shortcut; extended Euclid is the universal tool and the only one that reports non-existence.
- The
O(n)table and the factorial-inverse sweep both turnO(n log m)intoO(n)with structure-specific recurrences. - "No inverse" (
gcd > 1) is not "no solution": the congruence may still havegsolutions ifg | b.