Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA)  Module 18 – Maths for DSA

Modular arithmetic and fast exponentiation

Reduce sums and products modulo m at every step, compute a^b mod m by repeated squaring, and find modular inverses with Fermat or extended Euclid.

  • Intermediate
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free core, Pro depth: 11 of 30 minutes free
Free: Reduce after every step · Fast exponentiation by repeated squaring · Dividing by finding an inverse · Exercise and self-check. With a pass: 3 in-depth parts and 3 interview answers, about 19 minutes.

What you will learn

  • Apply the rules for modular addition, subtraction and multiplication
  • Compute a^b mod m by repeated squaring in O(log b) multiplications
  • Find modular inverses with Fermat's little theorem and with extended Euclid
  • Normalise remainders of negative numbers in Python, Java, C++, Go and JavaScript

Before you start

Modular arithmetic works with remainders. Two numbers are congruent modulo m, written a ≡ b (mod m), when m divides a − b, so every whole number is equivalent to exactly one remainder from 0 to m − 1. Sums, differences and products can be reduced modulo m after every step without changing the final remainder, which keeps the numbers small; that is why so many problems ask for an answer modulo 10⁹ + 7. Division is different: dividing by a means multiplying by its inverse, which exists exactly when gcd(a, m) = 1. Fast exponentiation computes a^b mod m with O(log b) multiplications by squaring, one step per binary digit of b, and Python’s pow(a, b, m) does it built in.

Reduce after every step

Write a = q × m + r. Adding or multiplying the multiples of m only adds more multiples of m, so only the remainders matter: (a + b) mod m = ((a mod m) + (b mod m)) mod m, and the same holds for subtraction and multiplication. In modular arithmetic a long computation can therefore keep every intermediate value below m:

100,000! modulo 10⁹ + 7, exactly and step by step Python · rules/running_product.py
import math

P = 10**9 + 7
N = 100_000

exact = math.factorial(N)
running = 1
largest = 0
for i in range(1, N + 1):
    running = running * i % P  # reduce after every multiplication
    largest = max(largest, running)

print(f"{N:,}! has {math.floor(math.log10(exact)) + 1:,} decimal digits")
print(f"exact value mod P:        {exact % P}")
print(f"product reduced each step: {running}")
print(f"largest number kept while reducing: {largest:,} (always below P = {P:,})")
print(f"(a + b) mod P == ((a mod P) + (b mod P)) mod P for a = 3^500, b = 7^400: {(3**500 + 7**400) % P == (3**500 % P + 7**400 % P) % P}")

Output

100,000! has 456,574 decimal digits
exact value mod P:        457992974
product reduced each step: 457992974
largest number kept while reducing: 999,992,676 (always below P = 1,000,000,007)
(a + b) mod P == ((a mod P) + (b mod P)) mod P for a = 3^500, b = 7^400: True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 running_product.py

The exact factorial has 456,574 digits, but the running product, reduced after each multiplication, never held a number larger than 999,992,676 and gave the same remainder, 457,992,974. With m = 10⁹ + 7, the product of two remainders is below 10¹⁸ + 1.5 × 10¹⁰, inside the 64-bit range of about 9.22 × 10¹⁸, so compiled languages can multiply two remainders without overflow.

Negative numbers need care, because languages disagree about the sign of a remainder:

-7 % 3 and friends in five languages

Python · sign/remainder.py

# Python's % takes the sign of the divisor, and // rounds down
for a, m in [(-7, 3), (7, -3), (-7, -3), (7, 3)]:
    print(f"{a:>2} % {m:>2} = {a % m:>2}    {a:>2} // {m:>2} = {a // m:>2}")
print("((-7 % 3) + 3) % 3 =", ((-7 % 3) + 3) % 3)

Output

-7 %  3 =  2    -7 //  3 = -3
 7 % -3 = -2     7 // -3 = -3
-7 % -3 = -1    -7 // -3 =  2
 7 %  3 =  1     7 //  3 =  2
((-7 % 3) + 3) % 3 = 2

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 remainder.py

JavaScript · sign/remainder.mjs

// JavaScript's % keeps the sign of the dividend; Math.trunc mirrors the division that goes with it
for (const [a, m] of [[-7, 3], [7, -3], [-7, -3], [7, 3]]) {
  console.log(`${String(a).padStart(2)} % ${String(m).padStart(2)} = ${String(a % m).padStart(2)}    trunc(${a} / ${m}) = ${Math.trunc(a / m)}`);
}
console.log('((-7 % 3) + 3) % 3 =', ((-7 % 3) + 3) % 3);

Output

-7 %  3 = -1    trunc(-7 / 3) = -2
 7 % -3 =  1    trunc(7 / -3) = -2
-7 % -3 = -1    trunc(-7 / -3) = 2
 7 %  3 =  1    trunc(7 / 3) = 2
((-7 % 3) + 3) % 3 = 2

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node remainder.mjs

Java · compiled/Remainder.java

public class Remainder {
    public static void main(String[] args) {
        // % takes the sign of the dividend and / rounds toward zero; Math.floorMod takes the sign of the divisor
        int[][] pairs = {{-7, 3}, {7, -3}, {-7, -3}, {7, 3}};
        for (int[] p : pairs) {
            System.out.printf("%2d %% %2d = %2d    %2d / %2d = %2d    floorMod = %2d%n", p[0], p[1], p[0] % p[1], p[0], p[1], p[0] / p[1], Math.floorMod(p[0], p[1]));
        }
    }
}

C++ · compiled/remainder.cpp

#include <cstdio>

int main() {
    // / rounds toward zero, so % takes the sign of the dividend
    const int pairs[][2] = {{-7, 3}, {7, -3}, {-7, -3}, {7, 3}};
    for (const auto& p : pairs) {
        std::printf("%2d %% %2d = %2d    %2d / %2d = %2d\n", p[0], p[1], p[0] % p[1], p[0], p[1], p[0] / p[1]);
    }
    std::printf("((-7 %% 3) + 3) %% 3 = %d\n", ((-7 % 3) + 3) % 3);
}

Go · compiled/remainder.go

package main

import "fmt"

func main() {
	// / truncates toward zero, and x % y has the sign of x
	pairs := [][2]int{{-7, 3}, {7, -3}, {-7, -3}, {7, 3}}
	for _, p := range pairs {
		fmt.Printf("%2d %% %2d = %2d    %2d / %2d = %2d\n", p[0], p[1], p[0]%p[1], p[0], p[1], p[0]/p[1])
	}
	fmt.Printf("((-7 %% 3) + 3) %% 3 = %d\n", ((-7%3)+3)%3)
}
Java, C++ and Go, built and run Python · compiled/remainder_session.py
from session import run, versions

# Remainder.java runs as a single source file, remainder.cpp is built with the sanitizers on, go run builds Go.
versions("java -version", "clang++ --version", "go version")
run("""
java -XX:-UsePerfData Remainder.java
clang++ -std=c++23 -O2 -Wall -Wextra -fsanitize=address,undefined remainder.cpp -o remainder && ./remainder
go run remainder.go
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
# go version go1.27.1 darwin/arm64
$ java -XX:-UsePerfData Remainder.java
-7 %  3 = -1    -7 /  3 = -2    floorMod =  2
 7 % -3 =  1     7 / -3 = -2    floorMod = -2
-7 % -3 = -1    -7 / -3 =  2    floorMod = -1
 7 %  3 =  1     7 /  3 =  2    floorMod =  1
$ clang++ -std=c++23 -O2 -Wall -Wextra -fsanitize=address,undefined remainder.cpp -o remainder && ./remainder
-7 %  3 = -1    -7 /  3 = -2
 7 % -3 =  1     7 / -3 = -2
-7 % -3 = -1    -7 / -3 =  2
 7 %  3 =  1     7 /  3 =  2
((-7 % 3) + 3) % 3 = 2
$ go run remainder.go
-7 %  3 = -1    -7 /  3 = -2
 7 % -3 =  1     7 / -3 = -2
-7 % -3 = -1    -7 / -3 =  2
 7 %  3 =  1     7 /  3 =  2
((-7 % 3) + 3) % 3 = 2

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 remainder_session.py

Python’s % takes the sign of the divisor, so −7 % 3 is 2, the remainder of modular arithmetic. Java, C++, Go and JavaScript round the quotient toward zero and give −1, the sign of the dividend. ((a % m) + m) % m turns either answer into the range 0 to m − 1, and Java offers Math.floorMod.

Fast exponentiation by repeated squaring

Multiplying by a, b times, takes b − 1 multiplications, hopeless for b = 10¹⁸. Fast exponentiation uses the binary digits of b instead: 45 = 32 + 8 + 4 + 1, so a⁴⁵ = a³² × a⁸ × a⁴ × a. Square a again and again to get a, a², a⁴, a⁸ and so on, and multiply the result by each square whose digit is 1, reducing modulo m every time:

7⁴⁵ mod 1000 by squaring: the squares 7, 49, 401, 801, 601 and 201, the binary digits of 45, and the running result ending at 807.squaredigitresult7¹7177²49077⁴40118077⁸80114077¹⁶60104077³²2011807

Repeated squaring for 7⁴⁵ mod 1000, one column per binary digit of 45

Text description of the diagram

The picture has six columns, one for each binary digit of 45 = 101101 in binary, read from the lowest digit on the left to the highest on the right.

  • The top row holds the squares of 7 modulo 1000, each the square of the one before it: 7¹ = 7, 7² = 49, 7⁴ = 2401 mod 1000 = 401, 7⁸ = 801, 7¹⁶ = 601 and 7³² = 201.
  • The middle row holds the digits of 45 from the lowest: 1, 0, 1, 1, 0, 1. The columns with a 1 are shaded, and their squares have a bold border: 7¹, 7⁴, 7⁸ and 7³², because 45 = 1 + 4 + 8 + 32.
  • The bottom row holds the running result after each column, starting from 1: it becomes 7 after the first column, stays 7 after the second, becomes 7 × 401 mod 1000 = 807, then 807 × 801 mod 1000 = 407, stays 407, and ends at 407 × 201 mod 1000 = 807.

So 7⁴⁵ mod 1000 = 807, found with 5 squarings and 4 multiplications instead of 44 multiplications, and every number stays below 1000 × 1000.

The squaring loop, with its multiplications counted Python · power/fast_pow.py
def fast_pow(a, b, m):
    """a^b mod m by repeated squaring; also returns how many multiplications it made."""
    result, multiplications = 1 % m, 0
    a %= m
    while b > 0:
        if b & 1:  # this binary digit of b is 1: multiply the current square in
            result = result * a % m
            multiplications += 1
        b >>= 1
        if b > 0:  # square for the next binary digit
            a = a * a % m
            multiplications += 1
    return result, multiplications


P = 10**9 + 7
print(f"7^45 mod 1000 = {fast_pow(7, 45, 1000)[0]}, Python's pow agrees: {fast_pow(7, 45, 1000)[0] == pow(7, 45, 1000)}")
print("exponent b                 binary digits   ones   multiplications   2^b mod P")
for b in (45, 10**9, 10**18):
    value, count = fast_pow(2, b, P)
    assert value == pow(2, b, P)
    print(f"{b:<25,}  {b.bit_length():>13}  {bin(b).count('1'):>5}  {count:>16}   {value}")

Output

7^45 mod 1000 = 807, Python's pow agrees: True
exponent b                 binary digits   ones   multiplications   2^b mod P
45                                     6      4                 9   371842544
1,000,000,000                         30     13                42   140625001
1,000,000,000,000,000,000             60     24                83   719476260

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 fast_pow.py

The loop makes one squaring per binary digit and one more multiplication per digit that is 1. For b = 10¹⁸, with 60 binary digits of which 24 are 1, that is 83 multiplications, and the result matches pow(2, 10**18, P). Fast exponentiation therefore costs O(log b) multiplications, and with reduction every number in it stays below m².

Dividing by finding an inverse

The inverse of a modulo m is the residue x for which a × x leaves remainder 1, so multiplying by x undoes a multiplication by a. The lesson on Euclid’s algorithm showed when it exists: the extended algorithm gives a·x + m·y = gcd(a, m), so an inverse exists exactly when gcd(a, m) = 1. For a prime m there is a shortcut, Fermat’s little theorem: a^(m − 1) ≡ 1, so a^(m − 2) is the inverse, one fast exponentiation away.

The inverse of 7 modulo 10⁹ + 7, three ways, and two failures Python · inverse/inverses.py
P = 10**9 + 7  # a prime


def extended_gcd(a, b):
    old_r, r, old_s, s = a, b, 1, 0
    while r != 0:
        q = old_r // r
        old_r, r = r, old_r - q * r
        old_s, s = s, old_s - q * s
    return old_r, old_s  # gcd(a, b) and x with a·x + b·y = gcd(a, b)


g, x = extended_gcd(7, P)
print("inverse of 7 modulo P:")
print(f"  by Fermat, 7^(P - 2) mod P   = {pow(7, P - 2, P)}")
print(f"  by extended Euclid           = {x % P}")
print(f"  by pow(7, -1, P)             = {pow(7, -1, P)}")
print(f"  check: 7 × {pow(7, -1, P)} mod P = {7 * pow(7, -1, P) % P}")

values = [3, 4, 4]  # their average is 11/3, not a whole number
residue = sum(values) % P * pow(len(values), -1, P) % P
print(f"average of {values} as a residue: {residue}; times 3 it gives back {residue * 3 % P} = 11")

for a, m in [(2, 9), (2, 10)]:
    try:
        print(f"pow({a}, -1, {m}) = {pow(a, -1, m)}")
    except ValueError as error:
        print(f"pow({a}, -1, {m}) raises ValueError: {error}")

Output

inverse of 7 modulo P:
  by Fermat, 7^(P - 2) mod P   = 142857144
  by extended Euclid           = 142857144
  by pow(7, -1, P)             = 142857144
  check: 7 × 142857144 mod P = 1
average of [3, 4, 4] as a residue: 666666675; times 3 it gives back 11 = 11
pow(2, -1, 9) = 5
pow(2, -1, 10) raises ValueError: base is not invertible for the given modulus

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 inverses.py

All three methods give 142,857,144, and 7 × 142,857,144 = 1,000,000,008 leaves remainder 1. A division such as an average becomes a multiplication by an inverse: 11 / 3 is the residue 666,666,675, and 3 times it gives back 11. pow(a, -1, m), available since Python 3.8, finds the inverse modulo any m, and 2 has none modulo 10.

Operation Rule modulo m Watch for
a + b, a − b Reduce each result A negative difference in Java, C++, Go and JavaScript
a × b Reduce each product Products above 2⁶³ when m is above about 3 × 10⁹
a^b Repeated squaring, O(log b) Reduce after every multiplication
a / b Multiply by the inverse of b It exists only when gcd(b, m) = 1

Three more tools build on these rules. Matrix exponentiation applies repeated squaring to a matrix and computes a term of a linear recurrence, such as Fibonacci(10¹⁸), in O(log n) matrix products. 128-bit products keep a × b mod m exact when m is near 2⁶³. A table of inverses finds every inverse from 1 to n in one pass.

Exercise and self-check

Write the squaring loop and a modular inverse that also works when the modulus is not prime.

Exercise · Medium · Python, JavaScript

Fast power and modular inverses

Write two functions.

  • pow_mod(a, b, m) (JavaScript: powMod) returns a to the power b, modulo m, for whole numbers a >= 0, 0 <= b <= 10¹⁸ and 1 <= m <= 10¹⁸, with repeated squaring. Count 0⁰ as 1, as Python's pow does, so the answer for b = 0 is 1 % m.
  • inverse_mod(a, m) (JavaScript: inverseMod) returns the x with 0 <= x < m and a * x leaving remainder 1 modulo m, for a >= 0 and 2 <= m <= 10¹⁸, or None (JavaScript: null) when there is no such x. The modulus need not be prime.

In JavaScript every argument and result is a BigInt (7n, 1000n), because products of numbers near 10¹⁸ are far past the exact integers of a Number. The sample tests check small cases, exponents near 10¹⁸, a few hundred random cases against Python's pow (or exact BigInt arithmetic), inverses for every small modulus, and moduli that are not prime.

Python · Starter code · modpow.py

def pow_mod(a, b, m):
    """a to the power b, modulo m, by repeated squaring (0 to the power 0 counts as 1)."""
    raise NotImplementedError("write pow_mod")


def inverse_mod(a, m):
    """The x with 0 <= x < m and a * x % m == 1, or None when there is none."""
    raise NotImplementedError("write inverse_mod")
The sample tests · test_modpow.py
import math
import random

from modpow import inverse_mod, pow_mod

P = 10**9 + 7


def test_small_powers():
    """small cases, including a base larger than the modulus and exponent 0"""
    assert pow_mod(7, 45, 1000) == 807
    assert pow_mod(2, 10, 1000) == 24
    assert pow_mod(10, 3, 7) == 6
    assert pow_mod(5, 0, 7) == 1
    assert pow_mod(0, 0, 7) == 1
    assert pow_mod(5, 0, 1) == 0
    assert pow_mod(0, 5, 7) == 0


def test_exponents_near_ten_to_the_eighteen():
    """exponents up to 10^18, a prime modulus and a modulus near 10^18"""
    assert pow_mod(2, 10**18, P) == 719476260
    assert pow_mod(3, 10**18 - 1, 10**18 + 9) == pow(3, 10**18 - 1, 10**18 + 9)
    assert pow_mod(10**18 + 8, 2, 10**18 + 9) == 1


def test_random_powers():
    """500 random cases compared with Python's pow"""
    rng = random.Random(21)
    for _ in range(500):
        a, b, m = rng.randrange(0, 10**18), rng.randrange(0, 10**18), rng.randrange(1, 10**18)
        assert pow_mod(a, b, m) == pow(a, b, m), (a, b, m)


def test_small_inverses():
    """inverses that exist, and two that do not"""
    assert inverse_mod(3, 7) == 5
    assert inverse_mod(10, 7) == 5
    assert inverse_mod(7, P) == 142857144
    assert inverse_mod(2, 9) == 5
    assert inverse_mod(1, 2) == 1
    assert inverse_mod(2, 10) is None
    assert inverse_mod(6, 9) is None
    assert inverse_mod(0, 5) is None


def test_every_small_modulus():
    """every a below m for every m from 2 to 60, against a search"""
    for m in range(2, 61):
        for a in range(m):
            found = next((x for x in range(m) if a * x % m == 1), None)
            assert inverse_mod(a, m) == found, (a, m)


def test_moduli_that_are_not_prime():
    """Fermat's a^(m - 2) fails here; the extended algorithm does not"""
    m = 10**18
    for a in (999_999_999_999_999_999, 123_456_789_012_345_677, 3):
        x = inverse_mod(a, m)
        assert x is not None and 0 <= x < m and a * x % m == 1
    assert inverse_mod(10**9, m) is None
    rng = random.Random(8)
    for _ in range(300):
        m = rng.randrange(2, 10**12)
        a = rng.randrange(0, m)
        x = inverse_mod(a, m)
        if math.gcd(a, m) == 1:
            assert x == pow(a, -1, m), (a, m)
        else:
            assert x is None, (a, m)

JavaScript · Starter code · modpow.mjs

/** a to the power b, modulo m, by repeated squaring (0 to the power 0 counts as 1). All BigInts. */
export function powMod(a, b, m) {
  throw new Error('write powMod');
}

/** The x with 0 <= x < m and a * x % m === 1, or null when there is none. All BigInts. */
export function inverseMod(a, m) {
  throw new Error('write inverseMod');
}
The sample tests · modpow.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { inverseMod, powMod } from './modpow.mjs';

const P = 1000000007n;

/** Exact a^b mod m with BigInt, by plain repeated squaring of a different shape (right to left over a bit string). */
function referencePow(a, b, m) {
  let result = 1n % m;
  const bits = b.toString(2);
  for (const bit of bits) {
    result = (result * result) % m;
    if (bit === '1') result = (result * a) % m;
  }
  return result;
}

/** A small seeded generator of BigInts below limit (a 64-bit linear congruential generator). */
function generator(seed) {
  let state = BigInt(seed);
  return (limit) => {
    state = (state * 6364136223846793005n + 1442695040888963407n) % (1n << 64n);
    return state % limit;
  };
}

function gcd(a, b) {
  while (b) [a, b] = [b, a % b];
  return a;
}

test('small cases, including a base larger than the modulus and exponent 0', () => {
  assert.equal(powMod(7n, 45n, 1000n), 807n);
  assert.equal(powMod(2n, 10n, 1000n), 24n);
  assert.equal(powMod(10n, 3n, 7n), 6n);
  assert.equal(powMod(5n, 0n, 7n), 1n);
  assert.equal(powMod(0n, 0n, 7n), 1n);
  assert.equal(powMod(5n, 0n, 1n), 0n);
  assert.equal(powMod(0n, 5n, 7n), 0n);
});

test('exponents up to 10^18, a prime modulus and a modulus near 10^18', () => {
  assert.equal(powMod(2n, 10n ** 18n, P), 719476260n);
  assert.equal(powMod(10n ** 18n + 8n, 2n, 10n ** 18n + 9n), 1n);
});

test('300 random cases compared with exact BigInt arithmetic', () => {
  const next = generator(21);
  for (let i = 0; i < 300; i++) {
    const a = next(10n ** 18n);
    const b = next(10n ** 18n);
    const m = next(10n ** 18n - 1n) + 1n;
    assert.equal(powMod(a, b, m), referencePow(a % m, b, m), `${a} ${b} ${m}`);
  }
});

test('inverses that exist, and two that do not', () => {
  assert.equal(inverseMod(3n, 7n), 5n);
  assert.equal(inverseMod(10n, 7n), 5n);
  assert.equal(inverseMod(7n, P), 142857144n);
  assert.equal(inverseMod(2n, 9n), 5n);
  assert.equal(inverseMod(1n, 2n), 1n);
  assert.equal(inverseMod(2n, 10n), null);
  assert.equal(inverseMod(6n, 9n), null);
  assert.equal(inverseMod(0n, 5n), null);
});

test('every a below m for every m from 2 to 60, against a search', () => {
  for (let m = 2n; m <= 60n; m++) {
    for (let a = 0n; a < m; a++) {
      let found = null;
      for (let x = 0n; x < m; x++) if ((a * x) % m === 1n) { found = x; break; }
      assert.equal(inverseMod(a, m), found, `${a} ${m}`);
    }
  }
});

test("Fermat's a^(m - 2) fails here; the extended algorithm does not", () => {
  const m = 10n ** 18n;
  for (const a of [999999999999999999n, 123456789012345677n, 3n]) {
    const x = inverseMod(a, m);
    assert.ok(x !== null && x >= 0n && x < m && (a * x) % m === 1n, `${a}`);
  }
  assert.equal(inverseMod(10n ** 9n, m), null);
  const next = generator(8);
  for (let i = 0; i < 200; i++) {
    const mod = next(10n ** 12n - 2n) + 2n;
    const a = next(mod);
    const x = inverseMod(a, mod);
    if (gcd(a, mod) === 1n) assert.ok(x !== null && (a * x) % mod === 1n && x < mod, `${a} ${mod}`);
    else assert.equal(x, null, `${a} ${mod}`);
  }
});
A hint

For pow_mod, keep result and the current square of a; at every binary digit of b, multiply the square into result when the digit is 1, then square it, and reduce modulo m after each multiplication. For inverse_mod, run the extended Euclidean algorithm on a and m: the inverse exists exactly when their GCD is 1, and then it is the coefficient of a, reduced modulo m.

The sample tests run on this device, in your browser (Pyodide, QuickJS): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 5 What does Python print for -17 % 5?

    Type a number.

    Show the answer to question 1

    Answer: 3

    Python's % takes the sign of the divisor and pairs with // rounding down: -17 // 5 is -4, and -17 - (-4 × 5) = 3. The remainder is always between 0 and 4 for a positive modulus 5.

  2. Question 2 of 5 In Java, C++, Go and JavaScript, what is -17 % 5?

    Choose one answer.

    Show the answer to question 2

    Answer: -2

    These languages round the quotient toward zero, to -3, so the remainder is -17 - (-3 × 5) = -2, with the sign of the dividend. ((-17 % 5) + 5) % 5 gives 3, as does Java's Math.floorMod(-17, 5).

  3. Question 3 of 5 How many multiplications does the lesson's fast_pow make for an exponent of 13, counting squarings and multiplications into the result?

    Type a number.

    Show the answer to question 3

    Answer: 6 multiplications

    13 is 1101 in binary: four digits, three of them 1. The loop multiplies the result once for each 1 (3 times) and squares once between consecutive digits (3 times): 6 in all, against 12 for multiplying by a twelve times.

  4. Question 4 of 5 Which of these numbers has no inverse modulo 12?

    Choose one answer.

    Show the answer to question 4

    Answer: 9

    An inverse exists exactly when gcd(a, 12) = 1. gcd(9, 12) = 3, so every multiple of 9 modulo 12 is a multiple of 3 and never 1. The others are coprime to 12: 5 × 5 = 25, 7 × 7 = 49 and 11 × 11 = 121 all leave remainder 1.

  5. Question 5 of 5 When is a^(m − 2) mod m the inverse of a modulo m?

    Choose one answer.

    Show the answer to question 5

    Answer: When m is prime and does not divide a

    Fermat's little theorem gives a^(m − 1) ≡ 1 for a prime m that does not divide a, so a × a^(m − 2) ≡ 1. For a composite m the theorem does not hold: modulo 9, 2⁷ mod 9 = 2, and 2 × 2 = 4, while the true inverse is 5. Use the extended Euclidean algorithm for any modulus.

Why the rules hold: the squaring loop and Fermat's theorem proved Pro: opens with a Pro, Premium or Ultimate pass

Every rule above has a short proof, and each proof also says where the rule stops working. This part proves the reduction rules, gives the invariant behind the squaring loop with a dry run, proves Fermat’s little theorem, and builds every inverse from 1 to 10⁶ in one pass.

In this part:

  • Reduction never changes the remainder: the proof
  • The invariant of the squaring loop, with a dry run
  • Fermat’s little theorem, proved by rearranging remainders
  • Every inverse from 1 to n in one pass

1 runnable example (Python) · about 2 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Products near 2⁶³: the overflow reproduced and fixed in five languages Pro: opens with a Pro, Premium or Ultimate pass

With a modulus near 10¹⁸, the product of two remainders needs about 120 bits, and every fixed-width language gets it wrong in its own way. This part reproduces the failure with one product whose answer is known, and fixes it with the right tool in each language.

In this part:

  • One product, five wrong or right answers
  • What each fix costs
  • Tests that catch a wrong product

2 runnable examples (C++, Go, Java, JavaScript, Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Matrix exponentiation for linear recurrences, with a practice set Pro: opens with a Pro, Premium or Ultimate pass

Repeated squaring works for anything that can be multiplied associatively, matrices included. This part computes Fibonacci(10¹⁸) modulo 10⁹ + 7 with 2 × 2 matrices, checks it with fast doubling, solves a three-term recurrence and measures when the k³ factor of matrix exponentiation becomes too large.

In this part:

  • Fibonacci(10¹⁸) with 2 × 2 matrices, checked by fast doubling
  • A three-term recurrence: climbing stairs 1, 2 or 3 at a time
  • When k³ log n is too much, with numbers
  • Practice set

1 runnable example (Python) · about 2 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher · Warm-up

    Why do so many programming problems ask for the answer modulo 10⁹ + 7?

    The exact answers, such as counts of paths or arrangements, often have thousands of digits, while a remainder modulo a fixed number is small and can still be checked exactly. 10⁹ + 7 suits the job for three reasons. It is prime, so every number from 1 to 10⁹ + 6 has an inverse and division works through Fermat's little theorem. Every remainder, and the sum of two of them, fits in a signed 32-bit integer. And the product of two remainders stays below about 1.00000001 × 10¹⁸, inside the 64-bit range of about 9.22 × 10¹⁸, so multiplying and then reducing cannot overflow in Java, C++ or Go. 998,244,353 is another common choice: it is prime too, and one less than it is 119 × 2²³, a shape that number-theoretic transforms need.

  2. Mid

    A Java service computes (a * b) % m on long values with m = 10¹² + 39. Some results are negative, and others are wrong but look normal. Explain both, and give two fixes with their costs.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Fresher to mid

    A problem asks for the average of n values as a residue modulo 10⁹ + 7, that is, the sum times the inverse of n. How do you compute it, what goes wrong if n is a multiple of the modulus, and what changes if the modulus is 10⁹?

    Model answer, Pro. First step and follow-ups

  4. Senior

    Compute the n-th term of a linear recurrence of order k modulo a prime, for n up to 10¹⁸. What does matrix exponentiation cost, when does it become too slow, and what do you use then?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Reduce after every addition, subtraction and multiplication; the remainder at the end is the same.
  • Python’s % takes the sign of the divisor; Java, C++, Go and JavaScript take the sign of the dividend, so normalise with ((a % m) + m) % m or Math.floorMod.
  • Fast exponentiation squares once per binary digit of b: 83 multiplications for b = 10¹⁸.
  • An inverse modulo m exists exactly when gcd(a, m) = 1; for a prime m it is a^(m − 2), and pow(a, -1, m) finds it for any m.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.