Modular Inverse Bug — Fermat's Fails on Composite Modulus
RSA decryption failed because Fermat's modular inverse was used on a composite modulus.
20+ years shipping performance-critical code where algorithms decide the bill. Everything here is grounded in real deployments.
- ✓Solid grasp of fundamentals
- ✓Comfortable reading code examples
- ✓Basic production concepts
- Modular arithmetic keeps numbers bounded (wraps at modulus) while preserving addition, subtraction, and multiplication.
- Fast exponentiation (repeated squaring) computes a^b mod m in O(log b) – essential for RSA and CP.
- Use modular inverse for division: multiply by b^(-1) instead of dividing. Inverse exists iff gcd(a,m)=1.
- Precompute factorials and inverse factorials for O(1) nCr queries; precompute all inverses 1..n in O(n) with recurrence.
- Matrix exponentiation solves linear recurrences (Fibonacci) in O(k^3 log n) – crucial for n = 10^18.
- Biggest mistake: using integer division (/) mod m instead of modular inverse – gives wrong answer silently.
Modular arithmetic is a system of integer arithmetic where numbers 'wrap around' after reaching a fixed modulus, like a 12-hour clock where 15:00 is 3:00. It's the mathematical foundation for cryptography (RSA, Diffie-Hellman), hash functions, and checksums — anywhere you need to keep numbers bounded or exploit cyclic properties.
The core guarantee is that operations like addition, subtraction, and multiplication are well-defined modulo n, but division is not: you need a modular inverse, which exists only when the divisor and modulus are coprime. This is where Fermat's Little Theorem works perfectly for prime moduli (a^(p-1) ≡ 1 mod p) but fails catastrophically on composite moduli, producing wrong inverses or none at all.
In practice, you'd use the Extended Euclidean Algorithm for arbitrary moduli, or Euler's theorem for composite ones where you know φ(n). The bug surfaces when developers blindly apply Fermat's theorem without checking primality — common in naive RSA implementations or custom crypto code — leading to silent data corruption or security holes.
Real-world tools like OpenSSL, GMP, and Boost.Multiprecision all handle this correctly, but roll-your-own solutions often trip here, especially in embedded systems or CTF challenges.
Clock arithmetic is modular arithmetic — 10 hours after 5pm is 3am, not 15:00. Everything wraps around at the modulus. In computer science, modular arithmetic enables working with huge numbers (like in cryptography) by keeping results in a manageable range, while preserving mathematical properties.
The deeper insight: modular arithmetic isn't just 'wraparound.' It preserves the structure of addition, subtraction, and multiplication within the wrapped range. You can compute 2^(10^18) mod (10^9+7) — a number with 300 trillion digits — in under a microsecond, because modular exponentiation only needs O(log exponent) multiplications. This is why every RSA operation, every Diffie-Hellman key exchange, and every competitive programming problem involving large numbers lives inside modular arithmetic.
Every competitive programming problem involving large numbers ends with 'output the answer modulo 10^9+7'. Every RSA operation involves modular exponentiation — computing m^e mod n. Every Diffie-Hellman key exchange involves computing g^x mod p. Every elliptic curve operation reduces coordinates mod p. Understanding modular arithmetic is not optional for systems engineers or competitive programmers — it is the foundation layer that makes everything else possible.
The key insight that unlocks modular arithmetic: (a × b) mod m = ((a mod m) × (b mod m)) mod m. This distributivity means you can reduce intermediate values at every step, keeping numbers in range, without affecting the final result. Without this, even simple operations on 'big' numbers would overflow. With it, you can compute 2^(10^18) mod (10^9+7) with Python's built-in pow(2, 1018, 109+7) — under a microsecond.
I learned this the hard way during a coding competition. The problem asked for the 10^18-th Fibonacci number mod 10^9+7. I wrote a naive loop. It ran for 30 seconds before the judge killed it. My teammate wrote 4 lines using matrix exponentiation — O(log n) instead of O(n). His solution ran in 0.001 seconds. Same problem, same modulus, completely different algorithm. That was the day I understood that modular arithmetic isn't a topic — it's a toolkit, and the tools you choose determine whether your solution runs in microseconds or heat-death-of-the-universe.
This guide covers every modular arithmetic concept you need: from basic properties to matrix exponentiation, from Fermat's theorem to the Chinese Remainder Theorem, with working code in Python and Java, competitive programming patterns, and the cryptographic applications that make this math matter in production systems.
What Modular Arithmetic Actually Guarantees
Modular arithmetic is arithmetic on a circle of integers, where numbers wrap around after reaching a fixed modulus m. Instead of an infinite line, you work in the finite set {0, 1, ..., m-1}, and two numbers are equivalent if they differ by a multiple of m. This is the foundation of hashing, cryptography, and checksums — any system that needs bounded values from unbounded inputs.
The core mechanic: addition, subtraction, and multiplication all preserve the modulus — (a + b) mod m = ((a mod m) + (b mod m)) mod m. Division does not. You cannot simply divide residues; you need a modular inverse, which exists only when the divisor and modulus are coprime. This is where Fermat's Little Theorem applies for prime moduli, but fails catastrophically for composite moduli.
Use modular arithmetic whenever you need to keep numbers in a fixed range without losing algebraic structure — hash table indices, RSA encryption, cyclic redundancy checks. The cost is that you must treat division as a special operation, and the modulus choice determines whether inverses exist at all.
Modular Arithmetic Properties: The Complete Reference
Modular arithmetic preserves addition, subtraction, and multiplication within the wrapped range. Division is the exception — it requires the modular inverse.
Addition: (a + b) mod m = ((a mod m) + (b mod m)) mod m Subtraction: (a - b) mod m = ((a mod m) - (b mod m) + m) mod m — the '+m' prevents negative results Multiplication: (a × b) mod m = ((a mod m) × (b mod m)) mod m Exponentiation: (a^b) mod m — use fast modular exponentiation (next section) Division: (a / b) mod m = (a × b⁻¹) mod m — requires modular inverse
Critical gotcha: modular arithmetic does NOT distribute over division. (a/b) mod m ≠ ((a mod m) / (b mod m)) mod m. You must convert division to multiplication by the modular inverse.
Another gotcha: negative numbers. In Python, (-3) % 7 = 4 (always non-negative). In C/C++, (-3) % 7 = -3 (keeps the sign). This difference causes bugs when porting algorithms between languages. Always ensure your result is non-negative: ((a % m) + m) % m.
# io.thecodeforge: Modular Arithmetic Properties — Complete Reference # Every property verified with concrete examples. MOD = 10**9 + 7 # ────────────────────────────────────────────────────────────────────── # ADDITION: (a + b) mod m = ((a mod m) + (b mod m)) mod m # ────────────────────────────────────────────────────────────────────── a, b, m = 10**18, 10**18, 10**9 + 7 result_naive = (a + b) % m result_distributed = ((a % m) + (b % m)) % m print('=== Addition ===') print(f'({a} + {b}) mod {m} = {result_naive}') print(f'(({a} mod {m}) + ({b} mod {m})) mod {m} = {result_distributed}') print(f'Match: {result_naive == result_distributed}') print() # ────────────────────────────────────────────────────────────────────── # SUBTRACTION: (a - b) mod m = ((a mod m) - (b mod m) + m) mod m # The '+m' prevents negative results. # ────────────────────────────────────────────────────────────────────── a, b, m = 5, 10, 7 result = (a - b) % m result_safe = ((a % m) - (b % m) + m) % m print('=== Subtraction ===') print(f'({a} - {b}) mod {m} = {result} (Python handles negatives correctly)') print(f'(({a} mod {m}) - ({b} mod {m}) + {m}) mod {m} = {result_safe}') print(f'Match: {result == result_safe}') print() # ────────────────────────────────────────────────────────────────────── # MULTIPLICATION: (a × b) mod m = ((a mod m) × (b mod m)) mod m # This is the property that makes RSA possible. # ────────────────────────────────────────────────────────────────────── a, b, m = 10**18, 10**18, 10**9 + 7 result_naive = (a * b) % m result_distributed = ((a % m) * (b % m)) % m print('=== Multiplication ===') print(f'({a} × {b}) mod {m} = {result_naive}') print(f'(({a} mod {m}) × ({b} mod {m})) mod {m} = {result_distributed}') print(f'Match: {result_naive == result_distributed}') print() # ────────────────────────────────────────────────────────────────────── # DIVISION: (a / b) mod m = (a × b⁻¹) mod m # Division does NOT distribute over mod. Use modular inverse. # ────────────────────────────────────────────────────────────────────── a, b, m = 10, 3, 10**9 + 7 # Can't just do (a / b) mod m — division doesn't work in mod # Instead: find b⁻¹ such that b × b⁻¹ ≡ 1 (mod m) b_inv = pow(b, m - 2, m) # Fermat's little theorem (m must be prime) result = a * b_inv % m # Verify: result × b mod m should equal a verify = result * b % m print('=== Division ===') print(f'{a} / {b} mod {m} = {result}') print(f'Verify: {result} × {b} mod {m} = {verify} (should be {a})') print(f'Match: {verify == a}') print() # ────────────────────────────────────────────────────────────────────── # NEGATIVE MODULO: Python vs C++ behavior # This is a common interview trap. # ────────────────────────────────────────────────────────────────────── print('=== Negative Modulo ===') print(f'Python: (-3) % 7 = {(-3) % 7} (always non-negative)') print(f'Python: (-10) % 3 = {(-10) % 3}') print() print('C/C++: (-3) % 7 = -3 (keeps the sign)') print('C/C++: (-10) % 3 = -1') print() print('Fix for C/C++: ((a % m) + m) % m') print(f'Python equivalent: ((-3) % 7 + 7) % 7 = {((-3) % 7 + 7) % 7}') print() # ────────────────────────────────────────────────────────────────────── # PROPERTIES SUMMARY TABLE # ────────────────────────────────────────────────────────────────────── print('=== Properties Summary ===') properties = [ ('(a + b) mod m', 'Distributes', 'Yes'), ('(a - b) mod m', 'Distributes (add m to fix negatives)', 'Yes'), ('(a × b) mod m', 'Distributes', 'Yes'), ('(a^b) mod m', 'Use fast modexp', 'Yes'), ('(a / b) mod m', 'Does NOT distribute — use inverse', 'No'), ('a mod (-m)', 'Undefined convention — avoid negative moduli', 'N/A'), ] for expr, note, distributes in properties: print(f'{expr:20s} | Distributes: {distributes:3s} | {note}')
((a % m) + m) % m, you'll get negative intermediate results that break downstream calculations. This has caused bugs in competitive programming submissions and production systems alike. Always normalize: ((a % m) + m) % m.Visualizing Modular Arithmetic: The Clock Model
The easiest way to understand modular arithmetic is to think of a clock. A standard clock has 12 hours — it's modulo 12 arithmetic. If the current time is 10 o'clock and you add 3 hours, you get 1 o'clock (not 13). That's because 10 + 3 ≡ 1 (mod 12). The numbers wrap around after reaching the modulus.
This model extends directly to any modulus. The Quotient-Remainder Theorem formalizes the intuition: for any integer a and positive modulus m, there exist unique integers q (quotient) and r (remainder) such that a = q·m + r, where 0 ≤ r < m. The remainder r is exactly a mod m. This is the fundamental theorem underlying all modular arithmetic — it guarantees that every integer has a unique representation modulo m.
When you add two numbers modulo m, you add the remainders, then subtract m if the sum exceeds m-1. For example, on a 12-hour clock, 10 + 3 = 13, but 13 − 12 = 1. That's the same as (10+3) % 12 = 1. Multiplication works similarly but in multiple wraps.
The diagram below shows step-by-step the addition of 3 hours to 10 on a clock, illustrating the wrap-around at 12.
Fast Modular Exponentiation: O(log exp) Power
Computing a^b mod m naively (multiply a by itself b times, then mod m) is O(b) — impossibly slow when b is 10^18. Fast modular exponentiation uses repeated squaring to compute the result in O(log b) multiplications.
The algorithm: write b in binary. For each bit, square the current base. If the bit is 1, multiply the result by the current base. Reduce mod m at every step to keep numbers in range.
Example: 3^13 mod 100. Binary of 13 = 1101. - Start: result=1, base=3 - Bit 1 (rightmost): result=1×3=3, base=3²=9 - Bit 0: base=9²=81 - Bit 1: result=3×81=243%100=43, base=81²=6561%100=61 - Bit 1: result=43×61=2623%100=23 - Answer: 3^13 mod 100 = 23
Python's built-in pow(a, b, m) uses this exact algorithm — it's implemented in C and is the fastest option available. Use it. Don't reimplement unless you need to understand the internals.
# io.thecodeforge: Fast Modular Exponentiation # O(log exp) using repeated squaring. The most important algorithm in this guide. import time def mod_exp_manual(base: int, exp: int, mod: int) -> int: """ Compute base^exp mod mod using repeated squaring. O(log exp) multiplications. How it works: - Write exp in binary - For each bit (right to left): - If bit is 1: result = result * base mod m - Square: base = base * base mod m - Return result """ result = 1 base %= mod # reduce base first (handles base > mod) while exp > 0: if exp & 1: # if current bit is 1 result = result * base % mod base = base * base % mod # square the base exp >>= 1 # shift to next bit return result # ────────────────────────────────────────────────────────────────────── # VERIFICATION: manual vs Python built-in # ────────────────────────────────────────────────────────────────────── test_cases = [ (2, 10, 1000), # 2^10 mod 1000 = 1024 mod 1000 = 24 (3, 200, 13), # 3^200 mod 13 (7, 1000000, 10**9+7), # large exponent (2, 10**18, 10**9+7), # massive exponent ] print('=== Manual vs Built-in ===') for base, exp, mod in test_cases: manual = mod_exp_manual(base, exp, mod) builtin = pow(base, exp, mod) match = manual == builtin exp_str = f'{exp}' if exp < 10000 else f'10^{len(str(exp))-1}' print(f'{base}^{exp_str} mod {mod} = {manual} | Match: {match}') print() # ────────────────────────────────────────────────────────────────────── # PERFORMANCE: built-in pow() is ~100x faster (C implementation) # ────────────────────────────────────────────────────────────────────── base, exp, mod = 7, 10**18, 10**9 + 7 iterations = 10000 start = time.perf_counter() for _ in range(iterations): mod_exp_manual(base, exp, mod) manual_time = (time.perf_counter() - start) / iterations start = time.perf_counter() for _ in range(iterations): pow(base, exp, mod) builtin_time = (time.perf_counter() - start) / iterations print('=== Performance ===') print(f'Manual mod_exp: {manual_time*1e6:.2f} µs per call') print(f'Built-in pow(): {builtin_time*1e6:.2f} µs per call') print(f'Speedup: {manual_time/builtin_time:.1f}x') print('Always use pow(base, exp, mod) in production and competitions.') print() # ────────────────────────────────────────────────────────────────────── # STEP-BY-STEP TRACE: understand what happens inside # ────────────────────────────────────────────────────────────────────── print('=== Step-by-Step Trace: 3^13 mod 100 ===') base, exp, mod = 3, 13, 100 result = 1 base = base % mod step = 0 while exp > 0: step += 1 bit = exp & 1 print(f'Step {step}: exp={exp:>4} ({bin(exp):>8}) bit={bit} | result={result:>3} base={base:>3}', end='') if bit: result = result * base % mod print(f' → result={result} (multiply)') else: print() base = base * base % mod exp >>= 1 print(f'Final answer: {result}')
C++ Modular Exponentiation: Performance and Pitfalls
In C++, there is no built-in three-argument pow for modular exponentiation in the standard library (std::pow works on floating-point, not modular). You must implement fast exponentiation yourself. However, C++ is often used in competitive programming and production systems where performance is critical, so a correct and fast implementation is essential.
The implementation follows the same repeated squaring algorithm. Key pitfalls: integer overflow in the multiplication (a * b % mod can overflow if a and b are near INT64_MAX). Use __int128 for multiplication when dealing with moduli up to 10^18, or use a technique like Russian peasant multiplication for arbitrary precision.
The code below uses unsigned long long and __int128 for safe multiplication. For moduli up to ~2e9 (most CP), a simple (long long) (a * b) % mod is safe after casting to 64-bit? Actually, if a,b < MOD and MOD < 2^31, product < 2^62, which fits in signed 64-bit (9.22e18). For MOD up to 10^9+7, product < 1e18, safe. For RSA moduli (3072-bit), arbitrary precision is needed.
Important: use long long instead of int to avoid overflow. Also, the algorithm is identical to Python's manual version but with explicit type handling.
// io.thecodeforge: Fast Modular Exponentiation in C++ // Uses unsigned long long and __int128 for safe multiplication #include <cstdint> #include <iostream> using namespace std; typedef unsigned long long ull; typedef __int128 int128; // Modular exponentiation: (base^exp) % mod ull mod_pow(ull base, ull exp, ull mod) { ull result = 1; base %= mod; while (exp > 0) { if (exp & 1) { result = (int128)result * base % mod; } base = (int128)base * base % mod; exp >>= 1; } return result; } // If __int128 is not available (e.g., MSVC), use more portable approach: // ull mul_mod(ull a, ull b, ull m) { // ull res = 0; // a %= m; // while (b) { // if (b & 1) res = (res + a) % m; // a = (a << 1) % m; // b >>= 1; // } // return res; // } int main() { // Test cases cout << "3^13 mod 100 = " << mod_pow(3, 13, 100) << endl; cout << "2^10 mod 1000 = " << mod_pow(2, 10, 1000) << endl; cout << "7^(10^6) mod (10^9+7) = " << mod_pow(7, 1000000, 1000000007ULL) << endl; cout << "3^1000000005 mod (10^9+7) = " << mod_pow(3, 1000000005ULL, 1000000007ULL) << endl; // Fermat inverse return 0; }
(a*b)%mod without ensuring product fits in the type's range.(a * b) % MOD and got silent wrap-around, causing hash collisions. Switching to __int128 multiplication fixed it.Modular Inverse: Three Methods Compared
The modular inverse of a mod m is x such that a×x ≡ 1 (mod m). It exists if and only if gcd(a, m) = 1. Three methods:
Method 1: Fermat's Little Theorem — When m is prime: a⁻¹ ≡ a^(m-2) mod m. O(log m) via modular exponentiation. Simple, fast, but only works when m is prime.
Method 2: Extended Euclidean Algorithm — Finds x, y such that a×x + m×y = gcd(a, m). If gcd(a, m) = 1, then a×x ≡ 1 (mod m), so x is the inverse. Works for any m (not just primes). O(log m).
Method 3: Euler's Theorem — When gcd(a, m) = 1: a^φ(m) ≡ 1 (mod m), so a⁻¹ ≡ a^(φ(m)-1) mod m. Generalises Fermat's theorem but requires computing φ(m), which requires factoring m.
For competitive programming (m = 10^9+7, prime): use Fermat's — one line. For cryptography (m = φ(n), not necessarily prime): use Extended GCD. For understanding: implement all three and verify they agree.
# io.thecodeforge: Modular Inverse — Three Methods Compared from math import gcd # ────────────────────────────────────────────────────────────────────── # METHOD 1: FERMAT'S LITTLE THEOREM # a^(-1) ≡ a^(p-2) mod p (only when p is prime) # O(log p) — one modular exponentiation # ────────────────────────────────────────────────────────────────────── def mod_inv_fermat(a: int, p: int) -> int: """Modular inverse using Fermat's little theorem. p must be prime.""" return pow(a, p - 2, p) # ────────────────────────────────────────────────────────────────────── # METHOD 2: EXTENDED EUCLIDEAN ALGORITHM # Finds x such that a*x + m*y = gcd(a, m) # If gcd(a, m) = 1, then a*x ≡ 1 (mod m), so x = a^(-1) mod m # Works for ANY m (not just primes). O(log m). # ────────────────────────────────────────────────────────────────────── def extended_gcd(a: int, b: int) -> tuple: """Returns (gcd, x, y) such that a*x + b*y = gcd(a, b).""" if b == 0: return a, 1, 0 g, x, y = extended_gcd(b, a % b) return g, y, x - (a // b) * y def mod_inv_extended_gcd(a: int, m: int) -> int: """Modular inverse using Extended GCD. Works for any m where gcd(a,m)=1.""" g, x, y = extended_gcd(a, m) if g != 1: raise ValueError(f"Modular inverse does not exist: gcd({a}, {m}) = {g}") return x % m # ────────────────────────────────────────────────────────────────────── # METHOD 3: EULER'S THEOREM # a^phi(m) ≡ 1 mod m, so a^(-1) ≡ a^(phi(m)-1) mod m # Requires computing phi(m), which factors m. Educational only. # ────────────────────────────────────────────────────────────────────── def euler_totient_simple(n: int) -> int: """Compute Euler's totient phi(n) by trial division.""" result = n p = 2 temp = n while p * p <= temp: if temp % p == 0: while temp % p == 0: temp //= p result -= result // p p += 1 if temp > 1: result -= result // temp return result def mod_inv_euler(a: int, m: int) -> int: """Modular inverse using Euler's theorem. Works for any m where gcd(a,m)=1.""" phi = euler_totient_simple(m) return pow(a, phi - 1, m) # ────────────────────────────────────────────────────────────────────── # COMPARISON: all three methods give the same result # ────────────────────────────────────────────────────────────────────── print('=== Comparison (m = 101, prime) ===') m = 101 # prime for a in [3, 7, 42, 99]: fermat = mod_inv_fermat(a, m) ext_gcd = mod_inv_extended_gcd(a, m) euler = mod_inv_euler(a, m) print(f'a={a:>3}: Fermat={fermat:>4} | ExtGCD={ext_gcd:>4} | Euler={euler:>4} | Match: {fermat == ext_gcd == euler}') print() print('=== Extended GCD works for non-prime moduli ===') m = 100 # NOT prime for a in [3, 7, 11, 99]: try: inv = mod_inv_extended_gcd(a, m) verify = a * inv % m print(f'a={a:>3}, m={m}: inverse={inv:>4} | verify: {a}×{inv} mod {m} = {verify}') except ValueError as e: print(f'a={a:>3}, m={m}: {e}') print() print('Note: a=99 has no inverse mod 100 because gcd(99, 100) = 1... wait, it does!') print('But a=2 has no inverse mod 100 because gcd(2, 100) = 2 ≠ 1') inv_2 = None try: inv_2 = mod_inv_extended_gcd(2, 100) except ValueError as e: print(f'a=2, m=100: {e}') print() # ────────────────────────────────────────────────────────────────────── # PYTHON 3.8+ BUILT-IN: pow(a, -1, m) # ────────────────────────────────────────────────────────────────────── print('=== Python 3.8+ Built-in ===') a, m = 3, 10**9 + 7 builtin_inv = pow(a, -1, m) fermat_inv = pow(a, m - 2, m) print(f'pow({a}, -1, {m}) = {builtin_inv}') print(f'pow({a}, {m}-2, {m}) = {fermat_inv}') print(f'Match: {builtin_inv == fermat_inv}') print(f'Verify: {a} × {builtin_inv} mod {m} = {a * builtin_inv % m}')
Fermat vs Extended GCD: Advantages and Disadvantages
Choosing between Fermat's Little Theorem and the Extended Euclidean Algorithm for computing modular inverses depends on the context. Here is a direct comparison:
| Criteria | Fermat's Little Theorem | Extended GCD |
|---|---|---|
| Works with | Only prime modulus p | Any modulus m (provided gcd(a,m)=1) |
| Complexity | O(log p) — one modular exponentiation | O(log m) — similar asymptotic cost |
| Code length | Very short: pow(a, p-2, p) | Slightly longer: 6-10 lines to implement ext_gcd |
| Edge cases | Does not check gcd; silently gives wrong answer if p is composite | Correctly reports failure if inverse does not exist (gcd != 1) |
| Multiplication overflow | Same risk as any modular exponentiation | No additional risk; uses additions and subtractions |
| Precomputation possible | No direct precomputation for all inverses | Yes, can be extended to compute inverses of many numbers (via recurrence) |
When to use Fermat: - Modulus is guaranteed prime (10^9+7, 998244353, etc.) - You need one-liner simplicity - Performance is critical and modulus is prime
When to use Extended GCD: - Modulus is composite or unknown primality - You need to detect non-existence of inverse (gcd(a,m) != 1) - You are writing library code that must handle any modulus - You need the inverse for multiple numbers (can derive recurrence from it)
In practice, for competitive programming: if the problem states modulus is prime, Fermat is fine. For production systems or library code, always prefer Extended GCD (or Python's pow(a,-1,m)) because it's safer and only marginally slower.
The production incident at the top of this article is a perfect example: using Fermat on an RSA modulus (composite) caused silent data corruption. Extended GCD would have saved hours of debugging.
# io.thecodeforge: Comparison of Fermat vs Extended GCD MOD_PRIME = 998244353 # common CP prime MOD_COMPOSITE = 1000000 # not prime def modinv_fermat(a, mod): return pow(a, mod - 2, mod) def modinv_extgcd(a, mod): # Extended GCD: solves a*x + mod*y = 1 def egcd(a, b): if b == 0: return a, 1, 0 g, x, y = egcd(b, a % b) return g, y, x - (a // b) * y g, x, y = egcd(a, mod) if g != 1: return None return x % mod print("=== On prime modulus ===") a = 3 print(f"Fermat: {modinv_fermat(a, MOD_PRIME)}") print(f"ExtGCD: {modinv_extgcd(a, MOD_PRIME)}") print("\n=== On composite modulus ===") print(f"Fermat: {modinv_fermat(a, MOD_COMPOSITE)}") print(f"ExtGCD: {modinv_extgcd(a, MOD_COMPOSITE)}") # Verify inv = modinv_extgcd(a, MOD_COMPOSITE) print(f"Verify: {a} * {inv} % {MOD_COMPOSITE} = {a * inv % MOD_COMPOSITE} (should be 1)") # Fermat gives wrong result fermat_inv = modinv_fermat(a, MOD_COMPOSITE) print(f"Fermat's inverse computed: {fermat_inv}") print(f"Verify Fermat result: {a} * {fermat_inv} % {MOD_COMPOSITE} = {a * fermat_inv % MOD_COMPOSITE} (should be 1)")
Modular Division: The Pattern That Trips Everyone Up
You cannot divide in modular arithmetic. Full stop. The expression (a / b) mod m has no meaning in modular arithmetic — division is not defined. Instead, you multiply by the modular inverse: (a / b) mod m = (a × b⁻¹) mod m.
This pattern appears everywhere: computing nCr mod p (which involves dividing by r! and (n-r)!), solving linear equations mod p, and normalising fractions in competitive programming. The conversion is always the same: replace /b with *inv(b).
Common mistake: computing (a b) % MOD / b — this doesn't work because the division happens in integer arithmetic, not modular arithmetic. The correct approach: (a inv(b)) % MOD.
# io.thecodeforge: Modular Division — The Pattern That Trips Everyone Up MOD = 10**9 + 7 def mod_inv(a, mod=MOD): return pow(a, mod - 2, mod) # ────────────────────────────────────────────────────────────────────── # WRONG WAY: integer division after mod — gives wrong answer # ────────────────────────────────────────────────────────────────────── a, b = 10, 3 wrong = (a % MOD) // b # integer division — WRONG in modular arithmetic right = (a % MOD) * mod_inv(b) % MOD # modular inverse — CORRECT print('=== Modular Division ===') print(f'{a} / {b} mod {MOD} = ') print(f' Integer division (WRONG): {wrong}') print(f' Modular inverse (CORRECT): {right}') print(f' Verify: {right} * {b} % {MOD} = {right * b % MOD} (should be {a})') print() # Large number example a_large = 123456789012345678 print(f'{a_large} / {b} mod {MOD} = {a_large * mod_inv(b) % MOD}') print(f'Verify: {a_large % MOD} = {a_large * mod_inv(b) % MOD * b % MOD}') print() print('=== Floor Division (Not Defined) ===') print('In modular arithmetic, division yields the unique result in [0, m-1] such that') print('a = (a/b) * b mod m. There is no floor or truncation.') print('If you need floor division, you must work in integers and only reduce at the end.')
/b with *inv(b). This includes nCr computations, solving equations, and normalizing fractions./ with multiplication by modular inverse.Fermat's Little Theorem: The Cryptographic Cornerstone
Fermat's Little Theorem (FLT) states: if p is prime and a is not divisible by p, then a^(p-1) ≡ 1 (mod p). This implies a^(p-2) is the modular inverse of a mod p, and a^b mod p can be reduced modulo p-1 in the exponent: a^b ≡ a^(b mod (p-1)) mod p (if a not divisible by p).
- Primality testing (Fermat primality test)
- Reducing exponents: a^b mod p where b is huge → compute b_mod = b % (p-1), then a^b_mod mod p
- Constructing RSA: ed ≡ 1 mod φ(n), φ(n) = (p-1)(q-1)
But FLT has a critical limitation: it only works for prime moduli. Using it on a composite modulus gives wrong results, as shown in the production incident.
Proof sketch: Consider the set {1,2,...,p-1}. Multiply each by a mod p. This permutes the set. The product of all elements is (p-1)! on both sides, leading to a^(p-1) ≡ 1.
# io.thecodeforge: Fermat's Little Theorem — Applications and Proof Verification import random MOD = 13 # prime # Verify FLT for random a def check_flt(a, p): return pow(a, p-1, p) == 1 print('=== Fermat Verification (mod 13) ===') for a in range(1, MOD): print(f'a={a:2d}: a^{MOD-1} mod {MOD} = {pow(a, MOD-1, MOD)}') print() # Exponent reduction: a^b mod p = a^(b mod (p-1)) mod p print('=== Exponent Reduction ===') a = 5 b_large = 10**18 b_reduced = b_large % (MOD - 1) full = pow(a, b_large, MOD) reduced = pow(a, b_reduced, MOD) print(f'5^(10^18) mod {MOD} = {full}') print(f'5^(10^18 mod {MOD-1}) mod {MOD} = {reduced}') print(f'Match: {full == reduced}') print() # Caution: FLT gives wrong result on composite modulus print('=== FLT on Composite Modulus ===') a = 2 n = 15 # not prime print(f'2^{n-1} mod {n} = {pow(a, n-1, n)} (should be 1 if n prime, but is {pow(a, n-1, n)})') print('This is why Fermat's test can have false positives for Carmichael numbers.')
- Consider the set {1,2,...,p-1} mod p.
- Multiplying every element by a gives {a, 2a, ..., (p-1)a} mod p.
- This is a permutation of the original set because a is invertible mod p.
- The product of all elements = (p-1)! on both sides.
- Cancel (p-1)! (since p prime, it's invertible) to get a^(p-1) ≡ 1.
Chinese Remainder Theorem: Combining Moduli
The Chinese Remainder Theorem (CRT) states that if moduli m1, m2, ..., mk are pairwise coprime, then the system of congruences x ≡ ai (mod mi) has a unique solution modulo M = m1m2...mk. The solution can be found by: 1. Compute M = product of all mi. 2. For each i, compute Mi = M / mi. 3. Find the inverse yi of Mi modulo mi. 4. The solution is x = sum(ai Mi * yi) mod M.
- RSA optimization: decrypt mod p and q separately, then combine (CRT-RSA).
- Counting problems where the answer is mod a product of primes.
- Representing large numbers by residues mod small primes (residue number system).
Example: Find x such that x ≡ 2 mod 3, x ≡ 3 mod 5, x ≡ 2 mod 7. M = 357 = 105. M1 = 35, inv(35) mod 3 = 2 (since 352=70≡1 mod 3). M2 = 21, inv(21) mod 5 = 1 (since 21≡1 mod 5). M3 = 15, inv(15) mod 7 = 1 (since 15≡1 mod 7). x = (2352 + 3211 + 215*1) mod 105 = (140 + 63 + 30) mod 105 = 233 mod 105 = 23. Check: 23 mod 3 = 2, 23 mod 5 = 3, 23 mod 7 = 2. Correct.
# io.thecodeforge: Chinese Remainder Theorem — Complete Implementation from math import prod def extended_gcd(a, b): if b == 0: return a, 1, 0 g, x, y = extended_gcd(b, a % b) return g, x, y # returns (g, x, y) with a*x + b*y = g def crt(remainders, moduli): """ Solve the system of congruences: x ≡ remainders[i] (mod moduli[i]) Assumes moduli are pairwise coprime. Returns (x, M) where x is the unique solution modulo M. """ M = prod(moduli) result = 0 for a, m in zip(remainders, moduli): Mi = M // m g, x, y = extended_gcd(Mi, m) # Mi*x + m*y = 1 inv = x % m result += a * Mi * inv return result % M, M # Example remainders = [2, 3, 2] moduli = [3, 5, 7] x, M = crt(remainders, moduli) print(f'Solution: x ≡ {x} (mod {M})') for a, m in zip(remainders, moduli): print(f' Check: {x} mod {m} = {x % m} (expected {a})') print() # Large example: x mod 11 = 10, mod 13 = 12, mod 17 = 16 remainders2 = [10, 12, 16] moduli2 = [11, 13, 17] x2, M2 = crt(remainders2, moduli2) print(f'Solution: x ≡ {x2} (mod {M2})') for a, m in zip(remainders2, moduli2): print(f' Check: {x2} mod {m} = {x2 % m} (expected {a})')
Precomputation for nCr Mod p: The Gold Standard
Computing nCr (n choose r) modulo a prime p requires dividing by factorials. Without precomputation, each query costs O(n) to compute factorials and inverse factorials. By precomputing factorial and inverse factorial arrays up to the maximum n, we achieve O(1) per query.
Algorithm: 1. Precompute fact[0..N] where fact[0] = 1, fact[i] = fact[i-1] i % p. 2. Compute inv_fact[N] = fact[N]^(p-2) mod p (using Fermat). 3. Precompute inv_fact[i] for i from N-1 down to 0: inv_fact[i] = inv_fact[i+1] (i+1) % p. 4. Then nCr = fact[n] inv_fact[r] % p inv_fact[n-r] % p.
This works for p prime and n < p (Lucas theorem needed for n >= p). For n < p, it's the standard method for competitive programming.
Complexity: O(N) precomputation, O(1) per query.
Memory: O(N) for two arrays of size N+1.
Edge Cases: r > n returns 0. For n = 0, nCr = 1 only for r = 0.
# io.thecodeforge: Fast nCr Mod p — Precomputation and Queries MOD = 10**9 + 7 MAX_N = 10**6 # adjust as needed # Precompute factorials and inverse factorials fact = [1] * (MAX_N + 1) inv_fact = [1] * (MAX_N + 1) for i in range(1, MAX_N + 1): fact[i] = fact[i-1] * i % MOD inv_fact[MAX_N] = pow(fact[MAX_N], MOD - 2, MOD) for i in range(MAX_N, 0, -1): inv_fact[i-1] = inv_fact[i] * i % MOD def nCr(n: int, r: int) -> int: if r < 0 or r > n: return 0 return fact[n] * inv_fact[r] % MOD * inv_fact[n-r] % MOD # Test queries queries = [(5,2), (10,3), (100,50), (0,0), (10,10)] for n,r in queries: print(f'C({n},{r}) mod {MOD} = {nCr(n,r)}') print() # Verify small values manually from math import comb print('=== Verification with Python int comb ===') for n,r in [(5,2), (10,3), (0,0)]: expected = comb(n,r) % MOD result = nCr(n,r) print(f'C({n},{r}) = {result}, expected {expected}, match: {result == expected}')
Implementation of Modular Arithmetic: Stop Writing Buggy Hand-Rolled Code
Most junior devs treat modular arithmetic like a bag of random 'mod' operations you sprinkle over your code when numbers get big. That's how you get silent overflow bugs that only surface in production at 3 AM.
The real trick is understanding that modular arithmetic isn't just about the final result—it's about every intermediate value. Every addition, subtraction, and multiplication must stay within bounds. In Java, that means you can't just slap % on everything. Multiplication of two 1e9+7 values overflows a 32-bit int before you even hit the modulo. Use long for intermediate calculations, or write a safe multiplication that does modular reduction at every step.
Stop copy-pasting random formulas. Build a single ModInt class that enforces invariants. Your future self (and your team) will thank you when the production hash table doesn't randomly corrupt data every millionth insert.
// io.thecodeforge — dsa tutorial public class ModInt { private static final long MOD = 1_000_000_007L; private final long value; private ModInt(long value) { this.value = ((value % MOD) + MOD) % MOD; } public static ModInt of(long raw) { return new ModInt(raw); } public ModInt add(ModInt other) { return new ModInt(this.value + other.value); } public ModInt multiply(ModInt other) { // Java long multiplication won't overflow for MOD up to 1e9+7 return new ModInt((this.value * other.value) % MOD); } public long toLong() { return value; } public static void main(String[] args) { ModInt a = ModInt.of(500000004); ModInt b = ModInt.of(3); // 500000004 * 3 = 1500000012 mod 1e9+7 = 500000005 System.out.println(a.multiply(b).toLong()); } }
-3 % 10 = -3, not 7. Always normalize with ((x % mod) + mod) % mod unless you're absolutely sure your input is non-negative.long for intermediates so multiplication doesn't silently overflow.Use Cases of Modular Arithmetic in Competitive Programming: Where It Actually Wins You Points
Modular arithmetic isn't just a theoretical curiosity—it's the difference between a TLE on a combinatorics problem and a clean AC. Three scenarios where it's non-negotiable:
1. Combinatorial Tasks: Computing nCr mod p for n up to 10^6. Precompute factorials and inverse factorials in O(n), then answer each query in O(1). Without modular arithmetic, you're stuck with BigInteger, and you're losing.
2. Hashing Algorithms: Rolling hash for string matching relies on modular arithmetic to keep hash values bounded. Pick a large prime (like 10^9+7) and base (like 131). The modulo operation keeps your hash in range, and modular inverse lets you slide the window efficiently. Don't use power-of-two mods—those are polynomial hashes and vulnerable to collisions.
3. Solving Linear Congruences: When you need to find x such that x ≡ a mod m and x ≡ b mod n, you hit the Chinese Remainder Theorem immediately. That's a direct application of modular inverses. Without modular arithmetic, you're solving diophantine equations from scratch every time.
Every one of these starts with the same foundation: pick your modulus, precompute inverses, and never trust a raw int in a tight loop.
// io.thecodeforge — dsa tutorial public class RollingHashSearch { private static final long BASE = 131L; private static final long MOD = 1_000_000_007L; public static int findPattern(String text, String pattern) { int n = text.length(), m = pattern.length(); if (m > n) return -1; long[] pow = new long[n + 1]; long[] hash = new long[n + 1]; pow[0] = 1; for (int i = 1; i <= n; i++) { pow[i] = (pow[i-1] * BASE) % MOD; hash[i] = (hash[i-1] * BASE + (text.charAt(i-1) - 'a' + 1)) % MOD; } long patternHash = 0; for (int i = 0; i < m; i++) patternHash = (patternHash * BASE + (pattern.charAt(i) - 'a' + 1)) % MOD; for (int i = 0; i <= n - m; i++) { long subHash = (hash[i + m] - hash[i] * pow[m] % MOD + MOD) % MOD; if (subHash == patternHash) return i; } return -1; } public static void main(String[] args) { System.out.println(findPattern("competitiveprogramming", "prog")); } }
Modular Arithmetic Operations: The Four That Actually Matter in Code
You'll see pages listing ten operations like 'modular subtraction' as if it's a separate thing. It's not. Subtraction is just addition of the negative modulo the modulus. The four operations that matter in practice: addition, multiplication, exponentiation, and division via inverse.
Addition: (a + b) mod m = ((a mod m) + (b mod m)) mod m. Simple. But if a and b are big, use long to avoid overflow. In Java, (a + b) % m works fine for ints only if m < 2^31 and the sum doesn't overflow. Otherwise, do the mod on each operand first.
Multiplication: This is where most bugs live. (a b) % m = ((a % m) (b % m)) % m. But if m is near 2^31, the product of two modded values can overflow a 32-bit int. Cast to long before multiplying, then mod, then cast back. Every. Single. Time.
Exponentiation: Fast modular exponentiation is O(log exp). Never compute power then mod—intermediate values explode. Use binary exponentiation: square the base, multiply when the exponent bit is 1.
Division: You can't just divide. You need the modular inverse of the divisor. If the modulus is prime, use Fermat's little theorem (inverse = divisor^(mod-2)). If not, use extended Euclidean algorithm. Never assume the inverse exists—it only does if gcd(divisor, mod) = 1. Check it.
// io.thecodeforge — dsa tutorial public class SafeOperations { static final long MOD = 1_000_000_007L; static long safeAdd(long a, long b) { return ((a % MOD) + (b % MOD)) % MOD; } static long safeMul(long a, long b) { return ((a % MOD) * (b % MOD)) % MOD; } // Fermat inverse: b^(MOD-2) mod MOD (MOD must be prime) static long modInverse(long b) { return fastPow(b, MOD - 2); } static long fastPow(long base, long exp) { long result = 1; base %= MOD; while (exp > 0) { if ((exp & 1) == 1) result = (result * base) % MOD; base = (base * base) % MOD; exp >>= 1; } return result; } public static void main(String[] args) { long a = 500000004, b = 3; System.out.println(safeMul(a, b)); // 500000005 System.out.println(modInverse(2)); // 500000004 System.out.println(safeMul(10, modInverse(3))); // 333333336 (10/3 mod 1e9+7) } }
(long) a * b % mod.Payment Gateway Downtime Due to Wrong Modular Inverse
- Never use Fermat's little theorem for modular inverse unless you are 100% sure the modulus is prime.
- Always verify: a * inv(a) % mod == 1 after computing an inverse.
- Use Extended GCD (pow(a, -1, mod) in Python) for any modulus – it works for composites too.
inv_b = pow(b, MOD-2, MOD) # if MOD primeinv_b = pow(b, -1, MOD) # Python 3.8+ (any MOD)result = pow(base, exp, mod) # O(log exp)safe_sub = (a - b) % MOD # Python workssafe_sub_cpp = ((a % MOD) - (b % MOD) + MOD) % MODfact[0]=1; for i in 1..n: fact[i]=fact[i-1]*i%MODinv_fact[n]=pow(fact[n], MOD-2, MOD); for i in n-1..0: inv_fact[i]=inv_fact[i+1]*(i+1)%MODM = [[1,1],[1,0]]F_n = mat_pow(M, n-1)[0][0]| Criteria | Fermat's Little Theorem | Extended GCD | Euler's Theorem |
|---|---|---|---|
| Works with modulus | Prime only | Any (coprime) | Any (coprime) |
| Complexity | O(log m) | O(log m) | O(log m) + factoring |
| Code length | 1 line | 6-10 lines | 10+ lines |
| Detects non-existence | No | Yes | No |
| Precomputation for all inverses | No | Recurrence possible | No |
| When to use | Prime moduli in CP | Always safer | Educational only |
| File | Command / Code | Purpose |
|---|---|---|
| io | MOD = 10**9 + 7 | Modular Arithmetic Properties |
| io | def mod_exp_manual(base: int, exp: int, mod: int) -> int: | Fast Modular Exponentiation |
| io | using namespace std; | C++ Modular Exponentiation |
| io | from math import gcd | Modular Inverse |
| io | MOD_PRIME = 998244353 # common CP prime | Fermat vs Extended GCD |
| io | MOD = 10**9 + 7 | Modular Division |
| io | MOD = 13 # prime | Fermat's Little Theorem |
| io | from math import prod | Chinese Remainder Theorem |
| io | MOD = 10**9 + 7 | Precomputation for nCr Mod p |
| ModIntWrapper.java | public class ModInt { | Implementation of Modular Arithmetic |
| RollingHashSearch.java | public class RollingHashSearch { | Use Cases of Modular Arithmetic in Competitive Programming |
| SafeOperations.java | public class SafeOperations { | Modular Arithmetic Operations |
Key takeaways
Common mistakes to avoid
5 patternsUsing integer division (/) in modular arithmetic
Assuming Fermat's theorem works for composite modulus
Forgetting to normalize negative results in C++
Using two-argument pow then % for modular exponentiation
Not checking gcd before computing modular inverse
Practice These on LeetCode
Interview Questions on This Topic
Explain modular arithmetic and why it's useful in cryptography.
How do you compute the modular inverse of a number mod m? Give two methods and when to use each.
What is fast modular exponentiation and why is it important?
Describe the Chinese Remainder Theorem and one practical application.
How would you precompute factorials and inverse factorials for nCr queries modulo a prime? What's the complexity?
Frequently Asked Questions
Modular arithmetic works within a bounded set [0, m-1]. Addition, subtraction, multiplication behave as expected but wrap around at m. Division is replaced by multiplication by modular inverse. The key advantage: you can keep numbers small even when working with huge exponents.
pow(a, -1, m) uses the Extended Euclidean Algorithm internally, which works for any modulus as long as gcd(a,m)=1. Fermat's theorem (pow(a, m-2, m)) only applies when m is prime because the derivation relies on the group of non-zero residues modulo a prime.
If modulus is not prime, you cannot use Fermat for inverse factorials directly. You can factor the modulus and use Chinese Remainder Theorem with Lucas theorem for each prime factor, then combine. Or use prime factorization of numerator/denominator to compute nCr exactly and then reduce mod m, but that may be slow for large n. Some problems use modulus that is a product of two primes; then you can use Garner's algorithm.
Lucas theorem allows computing nCr mod p for prime p even when n >= p. It expresses n and r in base p and computes the product of smaller binomial coefficients. You need it when the modulus is prime but n exceeds the modulus. Without it, the factorial precomputation method fails because factorials become zero modulo p when n >= p.
20+ years shipping performance-critical code where algorithms decide the bill. Everything here is grounded in real deployments.
That's Number Theory. Mark it forged?
11 min read · try the examples if you haven't