FLINT3.7.0-dev
Overview

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 n-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, B = 2^{\mathtt{FLINT\_BITS}} is the limb base, M(n) the cost of multiplying two n-limb integers, and the operands are nonnegative limb arrays x = \sum_{i < m} x_i B^i and y = \sum_{j < n} y_j B^j with m \ge n.

Overview#

AlgorithmCost for n \times n limbsWhere FLINT uses it (x86-64 with ADX, Skylake thresholds)
Schoolbookn^2 limb productsFLINT's assembly kernels up to 16 limbs; GMP's basecase inside its Toom–Cook code
Karatsuba (Toom-2)O(n^{\log_2 3}) = O(n^{1.585})flint_mpn_mul_toom22 up to 230 limbs
Toom-3, 4, 6.5, 8.5O(n^{1.465}), O(n^{1.404}), …GMP's mpn_mul, from about 230 to 500 limbs
Small-prime FFTO(n \log n) in practicefft_small from 500 limbs
Schönhage–StrassenO(n \log n \log \log n)the fft module in builds without fft_small

Asymptotically faster algorithms exist, culminating in the O(n \log n) 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 xy = \sum_{j} (x y_j) B^j as n passes of a multiply-and-add of x by one limb (mpn_addmul_1), mn 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 16 \times 16 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.

AlgorithmBasecase multiplication kernelsSchoolbook multiplication of short limb arrays by a table of straight-line routines, one for each pair of small sizes (assembly with two independent carry chains on x86-64 and ARM64, C elsewhere), and Karatsuba multiplication built on them.

Karatsuba multiplication#

Theorem 1 (Karatsuba's identity [KO1962]). Let x = x_1 \beta + x_0 and y = y_1 \beta + y_0. Then

xy = x_1 y_1 \beta^2 + \bigl(x_1 y_1 + x_0 y_0 - (x_1 - x_0)(y_1 - y_0)\bigr) \beta + x_0 y_0 .

Proof. Expanding, (x_1 - x_0)(y_1 - y_0) = x_1 y_1 - x_1 y_0 - x_0 y_1 + x_0 y_0, so the coefficient of \beta equals x_1 y_0 + x_0 y_1, which is the middle coefficient of (x_1 \beta + x_0)(y_1 \beta + y_0).

With \beta = B^{\lceil n/2 \rceil}, a product of n-limb numbers thus needs three products of half the size (the difference (x_1 - x_0)(y_1 - y_0) is formed from absolute values and a sign) and O(n) additions.

Theorem 2. Applied recursively down to a basecase of fixed size, Karatsuba's method multiplies n-limb integers in O(n^{\log_2 3}) limb operations.

Proof. The cost satisfies K(n) \le 3 K(\lceil n/2 \rceil) + c n. For n = 2^k this unrolls to K(n) \le 3^k K(1) + c n \sum_{i < k} (3/2)^i = O(3^k) = O(n^{\log_2 3}), and monotonicity extends the bound to all n.

FLINT's flint_mpn_mul_toom22 is GMP's mpn_toom22_mul (which evaluates at 0, -1 and \infty, 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, n \le m < 2n, splitting at \lceil m/2 \rceil. On x86-64 with ADX, flint_mpn_mul uses it for n \le 230 limbs when 5n \ge 4m; 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 k = 2 of a general scheme due to Toom [Too1963] and Cook [Coo1966]. Write x = X(\beta) and y = Y(\beta) for polynomials X and Y of degree k - 1 whose coefficients are blocks of about n/k limbs. Then xy = W(\beta) with W = XY of degree 2k - 2, and W is determined by its values at 2k - 1 points.

Algorithm 1: Toom–Cook k-way multiplication

Input: x, y of n limbs, an integer k \ge 2, and 2k - 1 distinct points v_1, \ldots, v_{2k-1} among small integers, simple fractions and \infty.

Output: xy.

  1. Split x and y into k blocks of \lceil n/k \rceil limbs, giving X and Y.
  2. Evaluate X(v_i) and Y(v_i) for each i (linear combinations of the blocks with small coefficients; at \infty, the leading block).
  3. Compute the 2k - 1 products W(v_i) = X(v_i) Y(v_i) recursively.
  4. Interpolate the coefficients of W from these values by a fixed sequence of additions, shifts and exact divisions by small constants.
  5. Return W(\beta), a sum of shifted coefficients with carries.

Theorem 3. Algorithm 1 is correct, and applied recursively with a fixed k it costs O(n^{\log(2k-1)/\log k}) limb operations.

Proof. W = XY has degree 2k - 2, so it is determined by 2k - 1 values, and the interpolation in step 4 solves the Vandermonde system at the chosen points exactly (the points are distinct, and the projective point \infty gives the leading coefficient). Since x = X(\beta) and y = Y(\beta), W(\beta) = xy. Steps 1, 2, 4 and 5 cost O(n) limb operations for fixed k, because the evaluation and interpolation matrices have constant entries. The recurrence T(n) = (2k - 1) T(\lceil n/k \rceil) + O(n) solves to O(n^{\log_k (2k-1)}) as in Theorem 2.

Larger k lowers the exponent (1.465 for k = 3, 1.404 for k = 4) but increases the linear overhead, so each k 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 k) and for unbalanced ones (toom32, toom42, toom43, toom53, toom54, toom63), in which x and y 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 x and y into N coefficients of b 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 N in O(N \log N) ring operations. FLINT has two implementations:

  • Small-prime FFT (fft_small). The convolution is computed modulo several primes p < 2^{50} 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_ctx multiplies integers this way. A fixed number of primes suffices for all sizes that fit in memory, so the cost behaves like O(n \log n).
  • Schönhage–Strassen (fft). The convolution is computed in \mathbb{Z}/(2^K + 1)\mathbb{Z}, where 2 is a root of unity, so that the transforms need only shifts and additions, and the pointwise products are recursive multiplications. This gives O(n \log n \log \log n) [SS1971]. GMP's mpn_mul also 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.

Squaring#

A square x^2 needs only about half the limb products of a general product in the schoolbook method, since x_i x_j = x_j x_i, 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).

AlgorithmHigh, low and middle products of integersComputing only the low half, an approximation of the high half, or an arbitrary window of the limbs of an integer product, with hard-coded kernels, Mulders' recursive short products and proved error bounds.

Use in FLINT#

flint_mpn_mul(r, x, m, y, n) (for m \ge n \ge 1, with r not overlapping the inputs, see Multiplying limb arrays) is an inline function. If a basecase kernel exists for (m, n) 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):

  1. n = 1: mpn_mul_1.
  2. n \le 230 and 5n \ge 4m: flint_mpn_mul_toom22.
  3. n < T, unless m + n > 2T and 3n > T, where T = FLINT_FFT_SMALL_MUL_THRESHOLD = 500: GMP's mpn_mul.
  4. Otherwise: mpn_mul_default_mpn_ctx of fft_small.

The exception in step 3 sends unbalanced products to the FFT earlier, because GMP's cost grows like (m/n) M(n) while that of the FFT depends on m + n. Without fft_small, step 4 is replaced by flint_mpn_mul_fft_main of the fft module from FLINT_FFT_MUL_THRESHOLD = 32000 limbs. flint_mpn_mul_n (balanced operands) and flint_mpn_sqr follow the same pattern, and flint_mpn_mul_shared computes two products ax and bx with a common factor, sharing the transform of x when both are in the FFT range.

The combined effect, for balanced n \times n 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%):

n (limbs)48–1632100200–400800200010000–50000
time of GMP's mpn_mul_n / time of flint_mpn_mul_n2.21.21.51.21.0–1.11.52.62.1

Every product of large integers in FLINT reaches flint_mpn_mul:

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)
void fmpz_mul(fmpz_t f, const fmpz_t g, const fmpz_t h)

Referenced from#

References#

  • [HvdH2021] D. Harvey and J. van der Hoeven. Integer multiplication in time O(n \log n). 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
Cite this page

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-10-20).