Multiplying integers
Multiplication is the operation on which all other integer arithmetic is built: division, square roots, GCDs, radix conversion and the evaluation of series all cost a constant or logarithmic factor times a multiplication. FLINT multiplies -limb integers with a sequence of algorithms of decreasing asymptotic cost and increasing overhead: the schoolbook method, Karatsuba's method, the Toom–Cook family, and fast Fourier transforms.
Throughout, is the limb base, the cost of multiplying two -limb integers, and the operands are nonnegative limb arrays and with .
Overview#
| Algorithm | Cost for limbs | Where FLINT uses it (x86-64 with ADX, Skylake thresholds) |
|---|---|---|
| Schoolbook | limb products | FLINT's assembly kernels up to 16 limbs; GMP's basecase inside its Toom–Cook code |
| Karatsuba (Toom-2) | flint_mpn_mul_toom22 up to 230 limbs | |
| Toom-3, 4, 6.5, 8.5 | , , … | GMP's mpn_mul, from about 230 to 500 limbs |
| Small-prime FFT | in practice | fft_small from 500 limbs |
| Schönhage–Strassen | the fft module in builds without fft_small |
Asymptotically faster algorithms exist, culminating in the bit-complexity algorithm of Harvey and van der Hoeven [HvdH2021], but none is competitive at sizes that fit in memory.
Schoolbook multiplication#
The schoolbook method computes as passes of a multiply-and-add of by one limb (mpn_addmul_1), limb products in all. Its cost per limb product is small, and for short operands every other algorithm loses to it. On x86-64 processors with the ADX and BMI2 extensions, FLINT has a separate assembly function for every pair of sizes up to limbs (flint_mpn_mul_func_tab), and squaring functions up to 14 limbs. Each is fully unrolled, keeps operands in registers where possible and uses the two independent carry chains of the adcx and adox instructions, and none has the call overhead and loop control of GMP's general basecase. ARM64 has FLINT assembly kernels too (up to 15 limbs for the shorter operand). Other targets use portable C kernels up to 7 limbs and GMP's mpn_mul_basecase up to 16 limbs.
Karatsuba multiplication#
Theorem 1 (Karatsuba's identity [KO1962]). Let and . Then
Proof. Expanding, , so the coefficient of equals , which is the middle coefficient of .
With , a product of -limb numbers thus needs three products of half the size (the difference is formed from absolute values and a sign) and additions.
Theorem 2. Applied recursively down to a basecase of fixed size, Karatsuba's method multiplies -limb integers in limb operations.
Proof. The cost satisfies . For this unrolls to , and monotonicity extends the bound to all .
FLINT's flint_mpn_mul_toom22 is GMP's mpn_toom22_mul (which evaluates at , and , the form given above) compiled so that its recursive calls reach FLINT's basecase kernels instead of GMP's. It also accepts slightly unbalanced operands, , splitting at . On x86-64 with ADX, flint_mpn_mul uses it for limbs when ; on ARM64 up to 483 limbs (a threshold measured on Apple M1). Without FLINT assembly it is not used, and GMP's mpn_mul takes over after the basecase.
Toom–Cook multiplication#
Karatsuba's method is the case of a general scheme due to Toom [Too1963] and Cook [Coo1966]. Write and for polynomials and of degree whose coefficients are blocks of about limbs. Then with of degree , and is determined by its values at points.
Input: of limbs, an integer , and distinct points among small integers, simple fractions and .
Output: .
- Split and into blocks of limbs, giving and .
- Evaluate and for each (linear combinations of the blocks with small coefficients; at , the leading block).
- Compute the products recursively.
- Interpolate the coefficients of from these values by a fixed sequence of additions, shifts and exact divisions by small constants.
- Return , a sum of shifted coefficients with carries.
Theorem 3. Algorithm 1 is correct, and applied recursively with a fixed it costs limb operations.
Proof. has degree , so it is determined by values, and the interpolation in step 4 solves the Vandermonde system at the chosen points exactly (the points are distinct, and the projective point gives the leading coefficient). Since and , . Steps 1, 2, 4 and 5 cost limb operations for fixed , because the evaluation and interpolation matrices have constant entries. The recurrence solves to as in Theorem 2.
Larger lowers the exponent ( for , for ) but increases the linear overhead, so each is fastest only in a range of sizes. Bodrato and Zanoni [BZ2007] found evaluation points and interpolation sequences of minimal cost, which GMP implements for balanced splittings (toom33, toom44, and toom6h and toom8h with half-integer ) and for unbalanced ones (toom32, toom42, toom43, toom53, toom54, toom63), in which and are split into different numbers of blocks. FLINT does not reimplement these: between the end of its Karatsuba range and the FFT threshold it calls GMP's mpn_mul, which chooses among them with its own tuning [GMP].
FFT multiplication#
For large operands the product is computed as a convolution. Splitting and into coefficients of bits gives polynomials whose product has as coefficients the convolution of the two coefficient sequences, and a fast Fourier transform computes a convolution of length in ring operations. FLINT has two implementations:
- Small-prime FFT (
fft_small). The convolution is computed modulo several primes with number-theoretic transforms whose butterflies run in double-precision floating point with vector FMA instructions, and the coefficients are recovered by the Chinese remainder theorem.mpn_mul_default_mpn_ctxmultiplies integers this way. A fixed number of primes suffices for all sizes that fit in memory, so the cost behaves like . - Schönhage–Strassen (
fft). The convolution is computed in , where is a root of unity, so that the transforms need only shifts and additions, and the pointwise products are recursive multiplications. This gives [SS1971]. GMP'smpn_mulalso ends in a Schönhage–Strassen FFT (mpn_mul_fft) for very large operands [GKZ2007].
FFTs in FLINT compares the transforms, and Where FLINT uses fft_small lists the thresholds at which each caller switches.
fft_small modulo primes , on integers stored in doubles: the order of the roots, radix-4 butterflies with lazy reduction, blocking and truncation, and proofs that every value stays in range.AlgorithmSchönhage–Strassen multiplicationMultiplies large integers with FFTs over , where the roots of unity are powers of 2 and every twiddle factor is a shift; FLINT adds the trick, truncation, negacyclic pointwise products and tuned parameters.Squaring#
A square needs only about half the limb products of a general product in the schoolbook method, since , and only one forward transform instead of two with FFTs. FLINT therefore detects squaring: flint_mpn_sqr has its own assembly basecases (up to 14 limbs on x86-64 with ADX, 9 on ARM64, none elsewhere), then calls GMP's mpn_sqr (with its Toom squaring variants), and fft_small from FLINT_FFT_SMALL_SQR_THRESHOLD limbs. fmpz_mul calls it when both arguments are the same object.
Short and middle products#
Newton iteration for division and square roots needs only parts of products: the high half (flint_mpn_mulhigh_n), the low half (flint_mpn_mullow_n) or a window (flint_mpn_mulmid). For small sizes these use assembly kernels that skip the unneeded limb products; above those, Mulders' short product [Mul2000], which combines a full product with two recursive short products [HZ2011]; and above FLINT_MPN_MULHIGH_FFT_SMALL_CUTOFF limbs (500 on recent x86-64), an FFT product of only the needed range (_mpn_ctx_mpn_mul_range), which costs about as much as a full product of the output length. A middle product is the transposed operation of a product, as explained in Middle products and the transposition principle; _mpn_ctx_mpn_mul_range computes a window shorter than the longer operand with transposed transforms, at the cost of a product of the shorter operand and the window (Middle products by transposed small-prime FFTs).
Use in FLINT#
flint_mpn_mul(r, x, m, y, n) (for , with not overlapping the inputs, see Multiplying limb arrays) is an inline function. If a basecase kernel exists for it calls it; otherwise _flint_mpn_mul chooses, with the thresholds of flint-mparam.h for the target (the values given are those of x86-64 processors from Skylake on):
- :
mpn_mul_1. - and :
flint_mpn_mul_toom22. - , unless and , where
FLINT_FFT_SMALL_MUL_THRESHOLD: GMP'smpn_mul. - Otherwise:
mpn_mul_default_mpn_ctxoffft_small.
The exception in step 3 sends unbalanced products to the FFT earlier, because GMP's cost grows like while that of the FFT depends on . Without fft_small, step 4 is replaced by flint_mpn_mul_fft_main of the fft module from FLINT_FFT_MUL_THRESHOLD limbs. flint_mpn_mul_n (balanced operands) and flint_mpn_sqr follow the same pattern, and flint_mpn_mul_shared computes two products and with a common factor, sharing the transform of when both are in the FFT range.
The combined effect, for balanced products measured on the machine that built this documentation (an x86-64 server with AVX-512, shared with other jobs, so the ratios vary by about 10%):
| (limbs) | 4 | 8–16 | 32 | 100 | 200–400 | 800 | 2000 | 10000–50000 |
|---|---|---|---|---|---|---|---|---|
time of GMP's mpn_mul_n / time of flint_mpn_mul_n | 2.2 | 1.2 | 1.5 | 1.2 | 1.0–1.1 | 1.5 | 2.6 | 2.1 |
Every product of large integers in FLINT reaches flint_mpn_mul:
fmpz_mul,fmpz_addmul,fmpz_pow_uiand the otherfmpzfunctions (Multiplying integers), and through themfmpqand the matrix and polynomial modules over and ;- Kronecker substitution in
fmpz_polyandnmod_poly, which packs polynomials into large integers (Kronecker substitution); - FLINT's own division, square root and radix conversion (Integer division and Newton iteration, Radix conversion);
- the floating-point and ball arithmetic of
arf,arbandmp_real, and binary splitting (Binary splitting and product trees).
mp_limb_t flint_mpn_mul(mp_ptr z, mp_srcptr x, mp_size_t xn, mp_srcptr y, mp_size_t yn)void flint_mpn_mul_n(mp_ptr z, mp_srcptr x, mp_srcptr y, mp_size_t n)mp_limb_t flint_mpn_sqr(mp_ptr z, mp_srcptr x, mp_size_t n)Referenced from#
- Basecase multiplication kernels (algorithm)
- Integer division and Newton iteration (algorithm)
- Integer arithmetic (overview)
- Getting started with fmpz
- fmpz.h (module)
fmpz_mul,fmpz_mul_ui,fmpz_mul_si- Multiplying integers
- Multiplying limb arrays
References#
- [HvdH2021] D. Harvey and J. van der Hoeven. Integer multiplication in time . Annals of Mathematics 193 (2021), 563–617.
- [KO1962] A. Karatsuba and Yu. Ofman, "Multiplication of multidigit numbers on automata", Doklady Akademii Nauk SSSR 145 (1962), 293–294; English translation in Soviet Physics Doklady 7 (1963), 595–596
- [Too1963] A. L. Toom. The complexity of a scheme of functional elements realizing the multiplication of integers. Soviet Mathematics Doklady 3 (1963), 714–716.
- [Coo1966] S. A. Cook. On the minimum computation time of functions. PhD thesis, Harvard University, 1966.
- [BZ2007] M. Bodrato and A. Zanoni. Integer and polynomial multiplication: towards optimal Toom-Cook matrices. ISSAC 2007, 17–24. https://doi.org/10.1145/1277548.1277552
- [GMP] T. Granlund and the GMP development team. GNU MP: The GNU Multiple Precision Arithmetic Library, version 6.3.0. Manual, 2023. https://gmplib.org/manual/
- [SS1971] A. Schönhage and V. Strassen. Schnelle Multiplikation großer Zahlen. Computing 7 (1971), 281–292.
- [GKZ2007] P. Gaudry, A. Kruppa and P. Zimmermann. A GMP-based implementation of Schönhage-Strassen's large integer multiplication algorithm. ISSAC 2007, 167–174.
- [Mul2000] Thom Mulders : On Short Multiplications and Divisions, AAECC vol. 11 (2000) 69–88
- [HZ2011] D. Harvey and P. Zimmermann, "Short division of long integers" (2011), Proceedings of the 20th Symposium on Computer Arithmetic (ARITH-20), July 25-27, 2011, pages 7-14. https://web.maths.unsw.edu.au/~davidharvey/research/shortdiv.pdf
The FLINT team, “Multiplying integers,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/integers/multiplication.html (generated 2026-11-02).
@misc{flint-doc-algorithms-integers-multiplication,
author = {{The FLINT team}},
title = {{Multiplying integers}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/algorithms/integers/multiplication.html},
note = {Generated 2026-11-02}
}