FLINT3.7.0-dev

Multiplication with a precomputed inverse

Synopsis#include <flint/ulong_extras.h>
ulong n_mulmod2_preinv(ulong a, ulong b, ulong n, ulong ninv)source

Returns ab mod nab \bmod n. No restrictions on aa, bb or nn.

ulong n_mulmod2(ulong a, ulong b, ulong n)source

Returns ab mod nab \bmod n, computing the inverse of nn on the fly. Use n_mulmod2_preinv when nn is reused.

ulong n_mulmod_preinv(ulong a, ulong b, ulong n, ulong ninv, ulong norm)source

Returns ⌊a/2norm⌋ b mod n\lfloor a / 2^{\mathit{norm}} \rfloor \, b \bmod n for normalised nn and reduced a,ba, b. If aa, bb and nn are the operands and modulus shifted left by norm bits, this is their product modulo the unshifted modulus, shifted left by norm bits.

These functions compute ab mod nab \bmod n for a word-size modulus nn. They form the inner loop of most modular arithmetic in FLINT; the nmod module wraps them together with the modulus and its inverse in an nmod_t.

Choosing a function#

  • n_mulmod2_preinv is the general-purpose function. Compute ninv = n_preinvert_limb(n) once per modulus.
  • n_mulmod2 computes ninv itself and is only worth using for a single multiplication.
  • n_mulmod_preinv saves a few operations by requiring the modulus to be normalised (top bit set) and the operands reduced. To use it with any nn, work with n2sn 2^s and operands shifted left by ss bits, where ss is the number of leading zero bits of nn, and pass ss as norm. Results then also come out shifted by ss bits.

n_mulmod_precomp uses a double inverse instead and requires n<253n < 2^{53}. When one operand is fixed, n_mulmod_shoup is faster still; see Choosing a method for modular arithmetic for all options.

Algorithm#

The product abab is computed as a two-word integer and reduced with one step of Möller–Granlund division: one full and one low multiplication by the precomputed inverse, followed by at most two corrections. n_mulmod_preinv performs exactly this step, since its operands are already shifted. n_mulmod2_preinv passes the product to n_ll_mod_preinv, which shifts it to normalise the modulus; if the operands are not reduced, the high word of the product may be at least nn, and a second division step reduces it first.

AlgorithmDivision by an invariant wordDivides a two-word number by a fixed word using one multiplication by a precomputed reciprocal, one low multiplication and two cheap corrections, instead of a hardware division.

Examples#

The most basic use:

Examplemulmod_basic.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/ulong_extras.h>

int main(void)
{
    ulong a = 12345678, b = 87654321, n = 111111111;
    ulong ninv = n_preinvert_limb(n);

    flint_printf("%wu * %wu mod %wu = %wu\n", a, b, n,
                 n_mulmod2_preinv(a, b, n, ninv));
    return 0;
}
Output
12345678 * 87654321 mod 111111111 = 23456790

The inverse pays off when the modulus is reused. This computes 1000!1000! modulo a prime with 999 multiplications and checks the result against n_factorial_mod2_preinv:

Examplemulmod_factorial.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/ulong_extras.h>

int main(void)
{
    ulong p = 4294967291;               /* the largest prime below 2^32 */
    ulong pinv = n_preinvert_limb(p);   /* computed once, used 999 times */
    ulong f = 1, k;

    for (k = 2; k <= 1000; k++)
        f = n_mulmod2_preinv(f, k, p, pinv);

    flint_printf("1000! mod p = %wu\n", f);
    flint_printf("check       = %wu\n", n_factorial_mod2_preinv(1000, p, pinv));
    return 0;
}
Output
1000! mod p = 444887038
check       = 444887038

Guides#

Cite this page

The FLINT team, “Multiplication with a precomputed inverse (ulong_extras.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/ulong_extras/mulmod.html (generated 2026-11-02).