Introduction to Modular Arithmetic

In algorithmic competitions, an important part of number theory is modular arithmetic (modular arithmetic), that is, performing various integer operations modulo a fixed modulus. Besides basic arithmetic and exponentiation, one can also conveniently compute discrete logarithms, roots of various degrees, factorials, binomial coefficients, and more.

Modular arithmetic appears in all kinds of problems, not only in number theory. The actual answer to many problems may be very large, exceeding the storage range of common integer types. To avoid introducing big-integer arithmetic and printing long numbers, problems often ask for the answer modulo some number. This makes it important to master various modular-arithmetic techniques.

Integer Division and Remainder in C/C++

In C/C++, integer division and remainder do not follow the usual mathematical conventions for modular arithmetic and division.

For every standard version of C/C++, integer division is specified as follows:

  1. When the divisor is zero, the behavior is undefined.
  2. Otherwise, the result of (a / b) * b + a % b is equal to a.

In other words, the sign of the remainder depends on how division rounds, and the way division rounds is implementation-defined (determined by the compiler).

Starting with the C99 and C++11 standards, the quotient is specified to round toward zero (discarding the fractional part), so the remainder has the same sign as the dividend. Therefore, the following assertions are guaranteed to be true:

assert(5 % 3 == 2);
assert(5 % -3 == 2);
assert(-5 % 3 == -2);
assert(-5 % -3 == -2);

ModInt Class

Modular arithmetic can be viewed as performing operations on congruence classes modulo a fixed number. If a structure is used to represent a congruence class, and addition, subtraction, multiplication, and other operations between congruence classes are encapsulated as structure methods or overloaded operators, modular arithmetic can naturally be implemented as a ModInt class. The following is a simple example supporting addition, subtraction, multiplication, and fast exponentiation of M<230M < 2^{30}-bit signed integers for moduli 3232:

A Simple ModInt Class
// A simple ModInt implementation.
template <int M>
struct ModInt {
  struct skip_mod {};

  ModInt(int v, skip_mod) : v(v) {}

  int v;

  ModInt() : v(0) {}

  // Initialization: find remainder.
  // Equivalent to: v = int((x % M + M) % M)
  ModInt(long long x) {
    x %= M;
    if (x < 0) x += M;
    v = int(x);
  }

  // Addition.
  // Equivalent to: ModInt((l.v + r.v) % M)
  friend ModInt operator+(ModInt l, ModInt r) {
    int res = l.v + r.v;
    if (res >= M) res -= M;
    return ModInt(res, skip_mod{});
  }

  // Subtraction.
  // Equivalent to: ModInt((l.v - r.v + M) % M)
  friend ModInt operator-(ModInt l, ModInt r) {
    int res = l.v - r.v;
    if (res < 0) res += M;
    return ModInt(res, skip_mod{});
  }

  // Multiplication.
  friend ModInt operator*(ModInt l, ModInt r) {
    return ModInt(1LL * l.v * r.v % M, skip_mod{});
  }

  // Exponentiation.
  ModInt pow(long long b) const {
    ModInt res{1}, po{*this};
    for (; b; b >>= 1) {
      if (b & 1) res = res * po;
      po = po * po;
    }
    return res;
  }
};

This implementation deliberately reduces the number of remainder operations, because remainder operations are usually much slower than ordinary addition, subtraction, multiplication, or comparison. The code comments provide equivalent, more direct implementations. The main idea of these simple optimizations is that when two integers in [0,M)[0,M) are added or subtracted, the result always lies in (−M,2M)(-M,2M), so one addition or subtraction can bring it back into [0,M)[0,M). The exponentiation implementation uses the binary exponentiation technique.

In addition to these basic operations, the following operations can be performed under various moduli:

These operations are usually easier for prime moduli. For composite moduli, extended versions of the corresponding algorithms and the Chinese remainder theorem are often needed. Most modular operations can be viewed as solving some kind of congruence equation. For general methods of solving congruence equations, see the congruence equations page.

This section introduces several methods for optimizing remainder, multiplication, and fast-exponentiation operations modulo a number. For most problems, the simple implementation given above is already efficient enough. However, when a problem imposes strict requirements on constant factors, these optimizations can reduce unnecessary computation and remainder operations, further lowering the running time.

Fast Multiplication

In primality testing and integer factorization, one often encounters modular multiplication with moduli in the long long range. To avoid integer overflow during the computation, this section introduces a “fast multiplication” method that handles moduli in the long long range without using __int128, in O(1)O(1) time. This algorithm requires the judging system to represent long double with at least 8080-bit extended precision floating point1.

Assume 0≤a,b<m0 \le a, b < m. We want to compute ab mod mab\bmod m. Notice that:

ab mod m=ab−⌊abm⌋m.ab\bmod m=ab-\left\lfloor \dfrac{ab}m \right\rfloor m.

Using the natural overflow of unsigned long long:

ab mod m=ab−⌊abm⌋m=(ab−⌊abm⌋m) mod 264.ab\bmod m=ab-\left\lfloor \dfrac{ab}m \right\rfloor m=\left(ab-\left\lfloor \dfrac{ab}m \right\rfloor m\right)\bmod 2^{64}.

As long as the quotient ⌊abm⌋\left\lfloor\dfrac{ab}m\right\rfloor can be computed, the multiplication and subtraction in the rightmost expression can both be evaluated directly using unsigned long long.

It remains to consider how to compute ⌊abm⌋\left\lfloor\dfrac {ab}m\right\rfloor. The solution is to first use long double to compute am\dfrac am and then multiply by bb. Since long double is being used, there will inevitably be precision error. Suppose that long double is an 8080-bit extended-precision floating-point type (with a 11-bit sign, a 1515-bit exponent, and a 6464-bit significand). Then long double can represent at most 6464 significant bits exactly2. Therefore, am\dfrac am may first become wrong at bit 6565, with an error range3 of (−2−64,2−64)\left(-2^{-64},2^{-64}\right). After multiplying by the bb-bit signed integer 6464, the error range is (−0.5,0.5)(-0.5,0.5). To simplify the discussion, we can first add 0.50.5 and then round; the final error range is {0,1}\{0,1\}.

Finally, substituting into the expression above requires multiplication by −m-m, so the final error range is {0,−m}\{0,-m\}. Since mm is in the long long range, when the result lies in r∈[0,m)r\in[0,m), return rr directly; otherwise return r+mr+m.

The implementation is as follows:

Reference Implementation
long long mul(long long a, long long b, long long m) {
  long long c = (unsigned long long)a * b -
                (unsigned long long)((long double)a / m * b + 0.5L) * m;
  return c < 0 ? c + m : c;
}

Most judging systems now provide C/C++ compilers supporting the __int128 type4, so the multiplicand type can also be promoted to __int128 before taking the remainder:

Reference Implementation
long long mul(long long a, long long b, long long m) {
  return (__int128)a * b % m;
}

Of course, taking the remainder of __int128 values is not particularly fast. If further constant-factor optimization is needed, the methods in the next two sections can be considered.

Barrett Reduction

As mentioned above, division and remainder operations are usually slower than the other four arithmetic operations. To reduce the cost of remainder operations, some algorithms can obtain the same result without directly taking a remainder. The Barrett reduction algorithm introduced in this section is one of them.

Let mm be a fixed modulus, and suppose we need to compute a>0a > 0 for different positive values a mod ma\bmod m. By the division algorithm:

z=a mod m=a−⌊am⌋m.z = a\bmod m = a - \left\lfloor\dfrac{a}{m}\right\rfloor m.

The key is computing the quotient ⌊am⌋\left\lfloor\dfrac{a}{m}\right\rfloor. Let RR be a constant. Then5

⌊am⌋=⌊aRm/R⌋≈⌊a⌊Rm⌋/R⌋.\left\lfloor\dfrac{a}{m}\right\rfloor = \left\lfloor a\dfrac{R}{m} / R\right\rfloor \approx \left\lfloor a\left\lfloor\dfrac{R}{m}\right\rfloor/R\right\rfloor.

If we choose R=2kR = 2^k, then in the right-hand side, ⌊Rm⌋\left\lfloor\dfrac{R}{m}\right\rfloor can be precomputed, and division by RR can be performed by a shift operation. Thus, computing the quotient with the right-hand side requires only one multiplication and one shift. Substituting it into the expression for a mod ma\bmod m gives the desired estimate z′z' of the remainder.

Now analyze the error. The floor function has the property that for all x>y>0x > y > 0, we have ⌊x⌋−⌊y⌋≤⌈x−y⌉\lfloor x\rfloor - \lfloor y\rfloor \le \lceil x - y\rceil. Therefore, the error is

Δ=∣z′−z∣=m∣⌊am⌋−⌊a⌊Rm⌋/R⌋∣≤m⌈a(Rm−⌊Rm⌋)/R⌉≤m⌈aR⌉.\begin{aligned} \Delta &= |z' - z| = m\left|\left\lfloor\dfrac{a}{m}\right\rfloor - \left\lfloor a\left\lfloor\dfrac{R}{m}\right\rfloor/R\right\rfloor\right|\\ &\le m\left\lceil a\left(\dfrac{R}{m} - \left\lfloor\dfrac{R}{m}\right\rfloor\right) /R\right\rceil \le m\left\lceil\dfrac{a}{R}\right\rceil. \end{aligned}

As long as a≤Ra \le R, the error Δ\Delta does not exceed mm. Since z′≥zz' \ge z, the estimate z′z' can only be zz or z+mz + m. After obtaining the estimate, it is sufficient to subtract an extra z′≥mz' \ge m when mm to ensure the answer is correct.

The Barrett reduction computation uses only two multiplications, one shift, and at most two subtractions to perform an integer remainder. This efficiency improvement is not free: the intermediate variables involved in Barrett reduction are often longer than the input variables. It is easy to see that the longest intermediate variable in Barrett reduction is a⌊Rm⌋a\left\lfloor\dfrac{R}{m}\right\rfloor. Let ℓ(x)\ell(x) be the bit length of the binary representation of integer xx. Then:

ℓ(a⌊Rm⌋)≈ℓ(a)+ℓ(R)−ℓ(m).\ell\left(a\left\lfloor\dfrac{R}{m}\right\rfloor\right) \approx \ell(a) + \ell(R) - \ell(m).

Because the choice of RR must satisfy a<Ra < R, this length is at least 2ℓ(a)−ℓ(m)2\ell(a) - \ell(m). However, when a remainder is needed, we usually have ℓ(m)≤ℓ(a)\ell(m)\le\ell(a), so this intermediate variable may be longer than the input length ℓ(a)\ell(a). For example, if a 6464-bit integer is reduced modulo a 3232-bit integer, the intermediate variable actually needs 64×2−32=9664 \times 2 - 32 = 96 bits.

One application of Barrett reduction is computing the remainder of the product ab mod mab\bmod m. If one multiplicand is fixed, for example when bb is fixed, we can use:

ab mod m=ab−⌊a⌊bRm⌋/R⌋mab\bmod m = ab - \left\lfloor a\left\lfloor\dfrac{bR}{m}\right\rfloor/R\right\rfloor m

to make a similar estimate; it is only necessary to precompute the value of ⌊bRm⌋\left\lfloor\dfrac{bR}{m}\right\rfloor. This fixed-bb case is sometimes called Shoup modular multiplication6.

The more common case is that a,ba, b are both variable. First compute abab, then use Barrett reduction to obtain ab mod mab\bmod m. For example, when implementing modular multiplication, compute 0≤a,b<m0 \le a,b < m for ab mod mab\bmod m. In this case, the chosen rr must satisfy ab<Rab < R. By the analysis above, the longest intermediate variable has length 2ℓ(ab)−ℓ(m)2\ell(ab)-\ell(m). When ℓ(a)≈ℓ(b)≈ℓ(m)\ell(a)\approx\ell(b)\approx\ell(m), this length is 3ℓ(m)3\ell(m). In other words, to implement modular multiplication for 3232-bit integers using Barrett reduction, the intermediate variable needs 9696 bits. This is one limitation of Barrett reduction in practical algorithm-contest applications.

As an example, a reference implementation of modular multiplication for signed 32-bit integers using Barrett reduction is:

Reference Implementation
// Modular multiplication of int32_t using Barrett reduction.
class Barrett {
  int32_t m;
  uint64_t r;

 public:
  Barrett(int32_t m) : m(m), r((uint64_t)(-m) / m + 1) {}

  // Barrett reduction: a % m.
  int32_t reduce(int64_t a) const {
    int64_t q = (__int128)a * r >> 64;
    a -= q * m;
    return a >= m ? a - m : a;
  }

  // Modular multiplication: (a * b) % m;
  // Assume that 0 <= a, b < m.
  int32_t mul(int32_t a, int32_t b) const { return reduce((int64_t)a * b); }
};

The implementation uses 128-bit integers4.

Montgomery Modular Multiplication

Montgomery modular multiplication serves a purpose very similar to Barrett reduction: it also reduces the cost of remainder operations during modular arithmetic. Unlike the previous two algorithms, which approximate the quotient, Montgomery multiplication maps all integers into Montgomery space, where operations are relatively easy, thereby reducing the overall computational cost.

Let the modulus mm be odd, and choose R=2k>mR = 2^k > m. Then the Montgomery form corresponding to the congruence class a mod ma \bmod m is

aR mod m.aR\bmod m.

Because R⊥mR\perp m, there is a bijection between the congruence class a mod ma \bmod m and its Montgomery form aR mod maR\bmod m. Therefore, integers can be converted to Montgomery form, several operations modulo mm can be performed, and the resulting Montgomery form can then be converted back to an integer; the result is always correct.

Montgomery form makes many modular operations convenient. As noted above, whether two congruence classes are equal can be checked by comparing their Montgomery forms. Also,

(a+b)R mod m=((aR mod m)±(bR mod m)) mod m,(a+b)R\bmod m = ((aR\bmod m)\pm(bR\bmod m)) \bmod{m},

so addition and subtraction of congruence classes correspond to addition and subtraction of their Montgomery forms. However, multiplication of congruence classes cannot be performed by directly multiplying the two Montgomery forms. Indeed,

(ab)R mod m=((aR mod m)(bR mod m)R−1) mod m,(ab)R\bmod m = ((aR\bmod m)(bR\bmod m)R^{-1}) \bmod{m},

Thus, when multiplying two Montgomery forms, we need to apply the following Montgomery reduction operation to their product xx:

REDC⁡:x↦xR−1 mod m.\operatorname{REDC}: x \mapsto xR^{-1}\bmod m.

Using this operation, the Montgomery form of the product abab is REDC⁡((aR mod m)(bR mod m))\operatorname{REDC}((aR\bmod m)(bR\bmod m)). Montgomery reduction is the core operation of Montgomery modular multiplication:

  • Converting aa to its Montgomery form is REDC⁡((a mod m)(R2 mod m))\operatorname{REDC}((a\bmod m)(R^2\bmod m)).
  • Converting the Montgomery form of aa back to a mod ma\bmod m is REDC⁡(aR mod m)\operatorname{REDC}(aR\bmod m).
  • The Montgomery form of the modular inverse a−1 mod ma^{-1}\bmod m is REDC⁡((aR mod m)−1(R3 mod m))\operatorname{REDC}((aR\bmod m)^{-1}(R^3\bmod m)).

Now discuss how to implement the Montgomery reduction operation REDC⁡\operatorname{REDC}. When computing REDC⁡(x)\operatorname{REDC}(x), we always assume 0≤x<m20 \le x < m^2, which holds in all the cases above. Since R⊥mR\perp m, Bézout’s identity gives integers R−1,m′R^{-1},m' such that

RR−1+mm′=1.RR^{-1} + mm' = 1.

Therefore, let q=⌊xm′/R⌋q=\lfloor xm' / R\rfloor. Then

xR−1=x1−mm′R≡x−xmm′+qmRR=x−m(xm′ mod R)R(modm).\begin{aligned} xR^{-1} &= x\dfrac{1 - mm'}{R} \equiv \dfrac{x-xmm' + qmR}{R} = \dfrac{x - m(xm'\bmod R)}{R} \pmod{m}. \end{aligned}

Since 0≤x<m2<mR0 \le x < m^2 < mR and 0≤xm′ mod R<R0 \le xm'\bmod R < R,

−m<x−m(xm′ mod R)R<m.-m < \dfrac{x - m(xm'\bmod R)}{R} < m.

That is, this quotient differs from xR−1 mod mxR^{-1}\bmod m by at most one mm. Adding mm when the quotient is negative gives REDC⁡(x)\operatorname{REDC}(x). Computing this quotient requires only two integer multiplications, one integer subtraction, and two bit operations (taking the remainder modulo and dividing by R=2kR=2^k). Thus, Montgomery reduction can be performed efficiently.

To perform Montgomery modular multiplication, a series of constants must be precomputed. First, Montgomery reduction uses m′=m−1 mod Rm' = m^{-1}\bmod R, which can be computed with the Newton–Hensel method introduced below. In addition, converting different operations into Montgomery reductions involves constants such as R2 mod mR^2\bmod m. To obtain it, compute R mod mR\bmod m once, then add it to itself to obtain 2R mod m2R\bmod m. Next, regard it as the Montgomery form of 22 and perform fast exponentiation directly to obtain 2kR mod m=R2 mod m2^kR\bmod m = R^2\bmod m.

As an example, Montgomery modular multiplication for signed 3232-bit integers is implemented as follows:

Reference Implementation
// Montgomery modular multiplication.
// The modulus m must be odd. The constant r is 2^32.
class Montgomery {
  int32_t m;
  uint32_t mm, r2;

 public:
  Montgomery(int32_t m) : m(m), mm(1), r2(-m) {
    // Compute mm as inv(m) mod r.
    for (int i = 0; i < 5; ++i) {
      mm *= 2 - mm * m;
    }
    // Compute r2 as r * r mod m.
    // If allowed to use modular operation for uint64_t, simply use:
    //   r2 = (uint64_t)(-m) % m;
    r2 %= m;
    r2 <<= 1;
    if (r2 >= (uint32_t)m) r2 -= m;
    for (int i = 0; i < 5; ++i) {
      r2 = mul(r2, r2);
    }
  }

  // Montgomery reduction: x * inv(r) % m.
  // Also used to transform x from Montgomery space to the normal space.
  int32_t reduce(int64_t x) {
    uint32_t u = (uint32_t)x * mm;
    int32_t ans = (x - (int64_t)m * u) >> 32;
    return ans < 0 ? ans + m : ans;
  }

  // Multiplication in Montgomery space: x * y * inv(r) % m.
  int32_t mul(int32_t x, int32_t y) { return reduce((int64_t)x * y); }

  // Transform x from the normal space to Montgomery space.
  int32_t init(int32_t x) { return mul(x, r2); }
};

Compared with implementing modular multiplication using Barrett reduction, Montgomery multiplication involves several steps, including conversion, multiplication in Montgomery form, and inverse conversion. Therefore, conversion and inverse-conversion costs can be amortized only when enough modular operations are performed between them, leading to high overall efficiency. However, because the Montgomery implementation uses only intermediate variables of length 2ℓ(m)2\ell(m), it is more flexible. For example, multiplication of 3232-bit integers requires only an intermediate variable of 6464 bits. Thus, if a modular-integer class is needed for various number-theoretic computations, Montgomery multiplication is more suitable.

Integer Classes Modulo Powers of 2

This section discusses implementing a modular-integer class when the modulus is a power of 22. In this special case, division and remainder operations can be implemented with bit operations, making computation very efficient. Barrett reduction and Montgomery multiplication both exploit this property when 2e2^e is used as divisor and modulus. In particular, when the modulus is exactly 2322^{32} or 2642^{64}, an unsigned integer of the corresponding bit width combined with natural overflow can implement a modular-integer class without any explicit remainder operations. Even when the modulus is not exactly one of these values, it can be converted to one of these cases. For example, when the modulus is 2582^{58}, intermediate computations can be performed modulo 2642^{64}, and the result can finally be reduced modulo 2582^{58}. Besides the convenience of taking remainders, modular-integer classes modulo 2e2^e have many other special implementations. This section focuses on implementing inverses and exponentiation.

First, consider inversion: given odd aa and modulus m=2e (e>2)m=2^e~(e > 2), compute a−1 mod ma^{-1}\bmod m. Common methods include the extended Euclidean algorithm and fast exponentiation. The extended Euclidean algorithm involves taking remainders modulo a general modulus; ordinary fast exponentiation requires computing aφ(m)−1 mod ma^{\varphi(m)-1}\bmod{m}, which takes Θ(e)\Theta(e) integer multiplications. A more efficient method is the Newton–Hensel method. Specifically, apply the following fact:7

mx≡1(mod2e)  ⟹  mx(2−mx)≡1(mod22e).mx \equiv 1 \pmod{2^e} \implies mx(2 - mx) \equiv 1\pmod{2^{2e}}.

Thus, starting from x=1x = 1 and repeatedly applying x←x(2−mx)x \gets x(2-mx), we obtain ⌈log⁡2e⌉\lceil\log_2 e\rceil after m−1 mod Rm^{-1}\bmod R iterations.

As an example, a reference implementation for inversion modulo 2322^{32} is:

Reference Implementation
// Compute 1/v mod 2^32 for odd v.
uint32_t inv_mod_2_32(uint32_t v) {
  uint32_t x = 1;
  for (int i = 0; i != 5; ++i) {
    x *= 2 - v * x;
  }
  return x;
}

Next, consider exponentiation: given x,a,bx,a,b and modulus m=2e (e>2)m=2^e~(e > 2), compute xab mod mxa^b\bmod m, where aa is odd. From the analysis of the multiplicative structure modulo 2e2^e, we know that aa can always be written in the form ±gℓ\pm g^{\ell}8, and the minus sign appears exactly when a≡3(mod4)a\equiv 3\pmod 4. In this case, replace aa with −a-a and multiply the final result by (−1)b(-1)^b. Thus, assume from now on that a≡1(mod4)a\equiv 1\pmod 4. The core idea of the algorithm is to write aa as gL(a) mod mg^{L(a)}\bmod m, then compute the desired power as xgbL(a) mod mxg^{bL(a)}\bmod m.

Computing L(a)L(a) means computing the discrete logarithm ind⁡ga\operatorname{ind}_ga. Note that as long as a≡1(mod4)a\equiv 1\pmod 4, aa can always be written in the following form:

a≡(2e1+1)(2e2+1)⋯(2es+1)(modm),a \equiv (2^{e_1}+1)(2^{e_2}+1)\cdots(2^{e_s}+1) \pmod{m},

where 1<e1<e2<⋯<es<e1 < e_1 < e_2 < \cdots < e_s < e. This is because expanding the product directly shows that the binary representation of aa has 11 as its second-lowest set bit, at position e1e_1 (with bit indices starting from 00), so this representation can be found recursively. By the properties of discrete logarithms,

4L(a)≡4L(2e1+1)+4L(2e2+1)+⋯+4L(2es+1)(modm).4L(a) \equiv 4L(2^{e_1}+1) + 4L(2^{e_2}+1) + \cdots + 4L(2^{e_s}+1) \pmod{m}.

Because the modulus of the discrete logarithm is the order δm(g)=2e−2=m/4\delta_m(g)=2^{e-2}=m/4, multiply the entire congruence by 44 here so that the computation can be carried out in the residue classes modulo mm. Therefore, precomputing, for every 1<d<e1 < d < e, the quantity 4L(2d+1)4L(2^d+1) makes it possible to compute 4L(a)4L(a) quickly.

Conversely, it is also easy to obtain the value of L(a)L(a) from ga mod mg^a\bmod{m}. By the binomial theorem, for every 1<d<e1 < d < e,

(2d+1)2e−d≡1(modm),(2d+1)2e−d−1≡1+2e−1(modm),\begin{aligned} (2^d+1)^{2^{e-d}} \equiv 1 \pmod{m},\quad (2^d+1)^{2^{e-d-1}} \equiv 1 + 2^{e-1} \pmod{m}, \end{aligned}

Therefore, δm(2d+1)=2e−d\delta_m(2^d+1) = 2^{e-d}. By the properties of orders,

δm(2d+1)=δm(g)gcd⁡(δm(g),ind⁡g(2d+1)).\delta_m(2^d+1) = \dfrac{\delta_m(g)}{\gcd(\delta_m(g), \operatorname{ind}_g(2^d+1))}.

Thus, gcd⁡(δm(g),ind⁡g(2d+1))=2d−2\gcd(\delta_m(g), \operatorname{ind}_g(2^d+1)) = 2^{d-2}. This shows that L(2d+1)=ind⁡g(2d+1)=2d−2rL(2^d+1) = \operatorname{ind}_g(2^d+1) = 2^{d-2}r, where 2∤r2\nmid r. Therefore, the least significant bit equal to 4L(2d+1)4L(2^d+1) in the binary representation of 11 is exactly bit dd (with bit indices starting from 00). Hence, the same binary-representation recursion can decompose 4L(a)4L(a) into a sum of terms of the form 4L(2d+1)4L(2^d+1). This gives the value of aa.

There are several further optimizations in the concrete implementation. First, division is still needed when decomposing aa into a product. It is more convenient to compute the decomposition of a−1a^{-1}, that is, find 1<e1<e2<⋯<es<e1 < e_1 < e_2 < \cdots < e_s < e such that

a(2e1+1)(2e2+1)⋯(2es+1)≡1(modm)a(2^{e_1}+1)(2^{e_2}+1)\cdots(2^{e_s}+1) \equiv 1 \pmod{m}

holds. Again, determine 11 by finding the second-lowest bit equal to e1e_1, but to remove the factor a−1a^{-1} from 2e1+12^{e_1}+1, it is enough to multiply aa by 2e1+12^{e_1}+1, which can be done with bit operations. Since 4L(a−1)=−4L(a)4L(a^{-1})=-4L(a), subtraction must replace addition when accumulating 4L(a)4L(a). Second, for a specially chosen base gg, iteration need not continue to d=e−1d = e-1; it only needs to reach d=⌈e/2⌉−1d = \lceil e/2\rceil - 1. For this, choose gg such that

4L(2⌈e/2⌉+1)=2⌈e/2⌉.4L(2^{\lceil e/2\rceil} + 1) = 2^{\lceil e/2\rceil}.

For d≥e/2d \ge e / 2, we have

(2d+1)2=22d+2d+1+1≡2d+1+1(modm).(2^d+1)^2 = 2^{2d} + 2^{d+1} + 1 \equiv 2^{d+1} + 1 \pmod{m}.

Therefore, induction starting from d=⌈e/2⌉d = \lceil e/2\rceil shows that L(2d+1)=2dL(2^d+1)=2^d for every d≥e/2d \ge e/2. Consequently, as long as e/2≤e1<e2<⋯<es<ee/2 \le e_1 < e_2 < \cdots < e_s < e, we have

(2e1+1)(2e2+1)⋯(2es+1)≡1+2e1+2e2+⋯+2es(modm)(2^{e_1}+1)(2^{e_2}+1)\cdots(2^{e_s}+1) \equiv 1 + 2^{e_1} + 2^{e_2} + \cdots + 2^{e_s} \pmod{m}

and

4L((2e1+1)(2e2+1)⋯(2es+1))=2e1+2e2+⋯+2es.4L((2^{e_1}+1)(2^{e_2}+1)\cdots(2^{e_s}+1)) = 2^{e_1} + 2^{e_2} + \cdots + 2^{e_s}.

Therefore, after processing all binary bits with d<e/2d < e/2, the remaining discrete logarithm can be obtained directly without computing bit by bit. With the first optimization, the entire exponentiation requires O(e)O(e) additions/subtractions and bit operations and 11 multiplication; with the second optimization, about half of the additions/subtractions and bit operations can be omitted, at the cost of an additional 11 multiplication.

As an example, exponentiation modulo 2322^{32} is implemented as follows:

Reference Implementation
// Store 4L(a) for a = 2^d + 1, where L(a) is disc. log. base 388251981.
// The first two values are never used and thus set to zero.
// The base is chosen such that 4L(2^16+1) = 2^16.
constexpr uint32_t log_table[16] = {
    0x00000000, 0x00000000, 0xbba0267c, 0x49b9d1e8, 0xf0026f90, 0xd6e17e20,
    0xe78bf840, 0x039fe080, 0xaf7f8100, 0x60fe0200, 0xd1f80400, 0x23e00800,
    0x47801000, 0x8e002000, 0x18004000, 0x20008000,
};

// Compute 4L(v).
uint32_t log_mod_2_32(uint32_t x, uint32_t v) {
  for (int i = 2; i != 16; ++i) {
    if ((v >> i) & 1) {
      v += v << i;
      x -= log_table[i];
    }
  }
  x += v ^ 1;
  return x;
}

// Compute x*a for 4L(a) = v.
uint32_t exp_mod_2_32(uint32_t x, uint32_t v) {
  for (int i = 2; i != 16; ++i) {
    if ((v >> i) & 1) {
      x += x << i;
      v -= log_table[i];
    }
  }
  x *= v ^ 1;
  return x;
}

// Compute x*a^b for odd a.
uint32_t pow_odd_mod_2_32(uint32_t a, uint32_t b, uint32_t x) {
  if (a & 2) {
    a = -a;
    if (b & 1) {
      x = -x;
    }
  }
  return exp_mod_2_32(x, log_mod_2_32(0, a) * b);
}

// Compute x*a^b mod 2^32.
uint32_t pow_mod_2_32(uint32_t a, uint32_t b, uint32_t x = 1) {
  if (!a) return b == 0 ? x : 0;
  auto d = __builtin_ctz(a);
  if ((uint64_t)d * b >= 32) return 0;
  return pow_odd_mod_2_32(a >> d, b, x) << (d * b);
}

The discrete-logarithm preprocessing can be done with the Pohlig–Hellman algorithm. The base gg can be chosen as

5ind⁡5(2⌈e/2⌉)/2⌈e/2⌉−2 mod 2e.5^{\operatorname{ind}_5(2^{\lceil e/2\rceil})/2^{\lceil e/2\rceil - 2}}\bmod{2^e}.

References and Notes

Footnotes

  1. This applies to GCC or Clang compilers on most 64-bit systems. ↩

  2. See Double-precision floating-point format - Wikipedia. ↩

  3. Here we use the condition a<ma < m, that is, a/m∈[0,1)a / m \in [0,1). ↩

  4. In current mainstream compilation environments, only MSVC on Windows does not support the __int128 type. To write code compatible with multiple platforms, use the macro _MSC_VER to detect the MSVC compilation environment, and include the <intrin.h> header under that condition, using its provided intrinsic functions (such as _umul128 and others) to implement 128-bit integer arithmetic indirectly (available only on 64-bit platforms). ↩ ↩2

  5. Here ⌊rm⌋\left\lfloor\dfrac{r}{m}\right\rfloor can also be replaced with other integer estimates of rm\dfrac{r}{m}, such as the ceiling function ⌈rm⌉\left\lceil\dfrac{r}{m}\right\rceil and the nearest-integer rounding function ⌊rm⌉\left\lfloor\dfrac{r}{m}\right\rceil, as long as the corresponding error correction step is adjusted. ↩

  6. Shoup implemented this extension of Barrett reduction in his number-theoretic computation library NTL, hence the name. ↩

  7. Direct verification: from mx≡1(mod2e)mx \equiv 1 \pmod{2^e}, write mx=1+λ2emx = 1 + \lambda 2^e. Then mx(2−mx)=(1+λ2e)(1−λ2e)=1−λ222e≡1(mod22e)mx(2-mx) = (1+\lambda 2^e)(1-\lambda 2^e) = 1 - \lambda^2 2^{2e} \equiv 1\pmod{2^{2e}}. ↩

  8. The page cited in the text only proves that gg can be chosen as 55. In fact, repeating that proof completely shows that gg can be any integer congruent to 88 modulo 55. The method for choosing gg is discussed below. ↩

This content comes from OI Wiki: Introduction to Modular Arithmetic. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.