Modular Inverse

Presentation: https://youtu.be/eEU1v1ZPHe0

Modular Inverse

Suppose we want to find the modular inverse x=a1x = a^{-1} of some element aa modulo pp, that is:

ax1modpax \equiv 1 \mod p

The inverse exists if and only if gcd(a,p)=1\gcd(a, p) = 1, i.e., when aa is coprime with pp.

When pp is a prime number, the field Fp\mathbb{F}_p forms a finite field, and every nonzero element has an inverse. In that case, Fermat’s Little Theorem can be used to compute the inverse efficiently:

xap2modpx \equiv a^{p-2} \mod p

Modular Inverse using Binary Extended Euclidean Algorithm

However, a more efficient method is to use the Extended Euclidean Algorithm, which solves the following equation:

ax+py=gcd(a,p)=1ax + py = \gcd(a, p) = 1

Taking both sides modulo pp yields:

ax1modpax \equiv 1 \mod p

Therefore, xx is the modular inverse of aa modulo pp.

Core Idea

Let pp be an odd prime, and let aa and pp be coprime. Starting with u=au = a and v=pv = p, we apply the Binary GCD rules and reduce until either uu or vv becomes 1. The case where both uu and vv are even has already been handled in the previous stage and is thus excluded here.

gcd(u,v)={1 if u=1v=1gcd(u2,v) if u0(mod2)gcd(u,v2) if v0(mod2)gcd(uv,v) if uvu1(mod2) v1(mod2)gcd(u,vu) if v>uu1(mod2) v1(mod2)\gcd(u, v) = \begin{cases} 1 & \text{ if } u = 1 \lor v = 1 \\ \gcd \left(\frac{u}{2}, v \right) & \text{ if } u \equiv 0 \pmod 2 \\ \gcd \left(u, \frac{v}{2} \right) & \text{ if } v \equiv 0 \pmod 2 \\ \gcd \left(u - v, v \right) & \text{ if } u \ge v \land u \equiv 1 \pmod 2 \land \ v \equiv 1 \pmod 2 \\ \gcd \left(u, v - u \right) & \text{ if } v > u \land u \equiv 1 \pmod 2 \land \ v \equiv 1 \pmod 2 \\ \end{cases}

Meanwhile, as in the Binary Extended Euclidean Algorithm, we initialize the variables as x1=1,y1=0,x2=0,y2=1x_1 = 1, y_1 = 0, x_2 = 0, y_2 = 1, and maintain the following two invariants:

ax1+py1=u,ax2+py2=vax_1 + p y_1 = u, \quad a x_2 + p y_2 = v

If we take both sides modulo pp, we obtain simpler invariants:

ax1umodp,ax2vmodpa x_1 \equiv u \mod p, \quad a x_2 \equiv v \mod p

At each step, we update the expressions while maintaining these invariants. Let’s examine just case 2 and case 4 as examples.

Case 2: uu is even

  • If x1x_1​ is even, we update as:

ax1umodpax12u2modpa x_1 \equiv u \mod p \quad \Leftrightarrow \quad a \cdot \frac{x_1}{2} \equiv \frac{u}{2} \mod p
  • If x1x_1​ is odd, we update as:

ax1umodpax1+p2u2modpa x_1 \equiv u \mod p \quad \Leftrightarrow \quad a \cdot \frac{x_1 + p}{2} \equiv \frac{u}{2} \mod p

Case 4: uvu \ge v and both uu and vv are odd

We update as:

ax1umodp,ax2vmodpa(x1x2)uvmodpa x_1 \equiv u \mod p, \quad a x_2 \equiv v \mod p \quad \Leftrightarrow \quad a(x_1 - x_2) \equiv u - v \mod p

Code

Here is the Python implementation of Algorithm 16: BEA for Inversion in 𝔽ₚ (Binary Extended Euclidean Algorithm for computing the modular inverse in a prime field) from here. Each step is annotated with comments to aid understanding.

def binary_inverse_fp(a: int, p: int) -> int:
    """
    Computes the modular inverse of a modulo prime p using Binary Extended Euclidean Algorithm.
    
    Parameters: a ∈ 𝔽ₚ, prime p greater than 2
    
    Returns: a⁻¹ mod p
    """

    u, v = a, p      # Step 1: Initialize u ← a, v ← p
    b, c = 1, 0      # Initialize Bézout coefficients: b for u, c for v

    while u != 1 and v != 1:  # Step 2
        # Step 3-10: Make u odd
        while u % 2 == 0:
            u //= 2
            if b % 2 == 0:
                b //= 2
            else:
                b = (b + p) // 2

        # Step 11-18: Make v odd
        while v % 2 == 0:
            v //= 2
            if c % 2 == 0:
                c //= 2
            else:
                c = (c + p) // 2

        # Step 19-23: Subtract the smaller from the larger and update coefficients
        if u >= v:
            u -= v
            b -= c
        else:
            v -= u
            c -= b

    # Step 25-29: Return the correct inverse mod p
    if u == 1:
        return b % p
    else:
        return c % p

For Montgomery Domain

A value aa is transformed to its Montgomery representation as follows:

toMont(a)=aR=a2n\mathsf{toMont}(a) = aR = a2^n

Therefore, if we naively compute the modular inverse of aRaR, we get:

ModInv(aR)=a1R1=a12n\mathsf{ModInv}(aR) = a^{-1}R^{-1} = a^{-1}2^{-n}

However, the form we actually want is:

toMont(a1)=a1R\mathsf{toMont}(a^{-1}) = a^{-1}R

To achieve this, instead of initializing with b=1b = 1, we can set b=R2modpb = R^2 \mod p, and then return the result of the inversion algorithm.

ModInv(aR)×R2=a1R\mathsf{ModInv}(aR) \times R^2 = a^{-1}R

Montgomery Inversion Algorithm

Another efficient algorithm for computing modular Inverse is Kaliski's Montgomery Inverse algorithm.

Kaliski’s Montgomery Inverse consists of two phases:

  • Almost Inverse phase

AlmostInv(a)=a12k\mathsf{AlmostInv}(a) = a^{-1} \cdot 2^k

where nk2nn \le k \le 2n

  • Correction phase

Correction(a12k)=a12n\mathsf{Correction}(a^{-1} \cdot 2^k) = a^{-1} \cdot 2^n

Core Idea

TODO(chokoble): Add reason why this code works.

Code

Here is the Python implementation of Algorithm 17: Montgomery Inversion in 𝔽ₚ from here.

def montgomery_inverse_fp(a: int, p: int, n: int) -> tuple[int, int]:
    """
    Computes Montgomery Inverse of a mod p.
    Returns r ≡ a⁻¹ · 2^k mod p, where n ≤ k ≤ 2n.
    
    Parameters:
        a (int): element in 𝔽ₚ
        p (int): prime modulus
        n (int): expected bit-length of p (e.g., n = p.bit_length())
    
    Returns:
        (r, k): Montgomery inverse r and the exponent k
    """
    
    # Step 1: Initialization
    u = p
    v = a
    r = 0
    s = 1
    k = 0

    # Step 2–11: Main loop
    while v > 0:
        if u % 2 == 0:
            u //= 2
            s *= 2
        elif u > v:
            u = (u - v) // 2
            r += s
            s *= 2
        else:  # v >= u
            v = (v - u) // 2
            s += r
            r *= 2
        k += 1

    # Step 12–14: Reduce r if needed (Almost Inverse)
    if r >= p:
        r -= p

    # Step 15–21: Final division loop to normalize r
    for _ in range(k - n):
        if r % 2 == 0:
            r //= 2
        else:
            r = (r + p) // 2

    # Step 22: Return Montgomery inverse and k
    return r, k

Written by ryan Kim from A41

Last updated