Schönhage–Strassen multiplication
- Cost
- bit operations for -bit integers with full recursion; FLINT recurses one level, and its transforms cost bit operations.
Schönhage and Strassen [SS1971] multiply integers with a discrete Fourier transform over the ring . In this ring has order , so the roots of unity needed by the FFT can be chosen as powers of two, and multiplying by them is a shift with wraparound, costing bit operations instead of a multiplication. The pointwise products are products in the same kind of ring and can be computed with the same method, which gives bit operations for -bit integers. Fürer [Fur2009] and Harvey and van der Hoeven [HvdH2021] improved the asymptotic bound to , but the Schönhage–Strassen algorithm remains the standard exact integer FFT, as in GMP [GKZ2007] and in FLINT's fft module, written by William Hart.
The transforms of fft.h are truncated (Truncated FFTs) and, for large lengths, computed with the matrix Fourier algorithm (The matrix Fourier algorithm).
The ring#
Let , let , and let and . Put .
Lemma 1 (Roots of unity). has order exactly , and for every integer that is not a multiple of ,
Moreover, is a unit in .
Proof. , so and the order divides but not ; as is a power of two, the order is . For the sum, we may reduce modulo and assume . Write with odd, so , and let . Then . The indices fall into blocks of length , which divides , and within a block the terms and cancel, since . Finally, .
The lemma says that is a principal root of unity, which is what the inversion formula needs in a ring that is not a field [Pol1971]: for ,
and is invertible. Hence pointwise products of transforms are the transforms of cyclic convolutions of length , and the inverse transform recovers these after a division by , which is a shift.
Multiplication by is a shift by bits. Since , the bits shifted out at the top re-enter at the bottom with a negative sign. FLINT splits the shift into whole limbs, realised by reading or writing at an offset with the wrapped part subtracted, and a remaining shift by fewer than bits. Residues are kept in limbs in two's complement, and sums and differences are not reduced: the top limb absorbs the carries, and mpn_normmod_2expp1 reduces only where a reduced value is needed. A butterfly is thus a combined addition and subtraction (flint_mpn_sumdiff_n) followed by a shift: a few passes over bits. With these butterflies the transforms are radix-2 Cooley–Tukey FFTs (Radix-2 Cooley–Tukey).
The trick#
A transform of length with root needs a ring of bits. The following identity, also used by GMP [GKZ2007], gives a root of unity of order in the same ring.
Lemma 2. If , then satisfies . Hence has order and .
Proof. . Modulo we have and , so . Then and , so has order .
Even powers are shifts. For odd and odd , is odd and . Since , multiplying by costs a shift by bits, a shift by bits and a subtraction; this is fft_adjust_sqrt2 and fft_butterfly_sqrt2. For even , and nothing special is needed. In a transform of length by decimation in frequency, only the first layer uses odd powers of ; the two halves are transforms of length with root . The trick therefore doubles the transform length for a given ring, or halves the ring for a given number of points, at the cost of a slightly more expensive first layer. All _sqrt2 functions of fft.h use it, with a multiple of , hence of 4.
Integer multiplication#
To multiply and , FLINT cuts them into chunks of bits, and with (fft_split_bits). The product is the value at of the polynomial with , which is computed by a convolution of length modulo using the trick.
mul_truncate_sqrt2)Input: , ; parameters and with a multiple of , .
Output: .
- . Split and into and chunks of bits; require .
- , raised to if smaller, and rounded up to an even number.
- Compute the truncated sqrt2 transforms of length of both chunk vectors at the first points (
fft_truncate_sqrt2). - Reduce the transformed values and multiply them pairwise modulo .
- Apply the truncated inverse transform (
ifft_truncate_sqrt2), divide by and reduce; this gives . - Return (
fft_combine_bits).
Theorem 3. Algorithm 1 returns .
Proof. Each is a sum of at most products . From we get , so
using . Since the product polynomial has coefficients, it equals the cyclic product of length , and by Theorem 4 of Truncated FFTs the truncated transforms at points determine it. The residues in found in step 5 are therefore the integers , and step 6 evaluates the product polynomial at .
The bound means that almost half of each transformed coefficient carries data: . The product has about bits, so each transform handles about twice the size of the product. mul_mfa_truncate_sqrt2 performs the same steps with the matrix Fourier algorithm, and fft_convolution performs steps 3 to 5 on given coefficient vectors, also with signed coefficients, which are then read from their residues in the symmetric range . When only a window of the coefficients is wanted, as in a middle product, fft_convolution_choose_mid shortens the cyclic convolution so that the wrap-around misses the window (Partial products), as the small-prime FFT does (Windows and wrap-around).
Multiplication modulo #
The pointwise products in step 4 are products modulo . Up to FFT_MULMOD_2EXPP1_CUTOFF limbs (128 limbs, 8192 bits), flint_mpn_mulmod_2expp1_basecase computes the full product with flint_mpn_mul_n and subtracts its high half from its low half. Beyond that, fft_mulmod_2expp1 applies the method of Schönhage and Strassen once more, now with a negacyclic convolution, which reduces modulo for free.
Write with and cut and , both less than , into pieces , of bits. Since , the product modulo is the value at of modulo , whose coefficients are
so that . The negacyclic convolution is a cyclic convolution of the weighted vectors and , where is a root of unity of order , followed by multiplication of coefficient by [Nus1980; CF1994]. FLINT computes it modulo with and (fft_negacyclic, ifft_negacyclic). This ring is too small for the coefficients by bits; the missing information comes from a second, cheap convolution.
Lemma 4 (Recovering the coefficients). Let with , and let . Suppose , with and . Then , where is the integer in congruent to modulo .
Proof. Since divides , . Write with an integer . Reducing modulo gives . From and the bound on , , so , and is determined by its residue modulo .
The coefficients satisfy as long as , which holds for any feasible transform. The residues modulo are the negacyclic convolution of the lowest limbs of the pieces in wrapping word arithmetic, fft_naive_convolution_1, which costs word multiplications, negligible next to the rest. Without this trick the inner ring would need bits, which is not of the form with a multiple of . Finally, is assembled from the as in step 6 of Algorithm 1, with a correction for the negative ones.
fft_mulmod_2expp1)Input: reduced , modulo , with a value returned by fft_adjust_limbs.
Output: , reduced.
- If or , return the negation of the other.
- If , return
flint_mpn_mulmod_2expp1_basecase. - and with from the table
MULMOD_TAB; , , . - Compute the negacyclic convolution of the -bit pieces modulo with transforms of length , using
flint_mpn_mulmod_2expp1_basecasefor its pointwise products, and the negacyclic convolution of the lowest limbs modulo . - Recover the by Lemma 4 and return .
fft_adjust_limbs makes step 3 well defined: it rounds the limb count up so that is a multiple of , making an integer, and a multiple of . The pointwise products of step 4 are not split further, so fft_mulmod_2expp1 has one level of recursion; its inner products of bits are full products by flint_mpn_mul_n. In builds with fft_small, the convolutions of fmpz_poly and fmpzi use instead sd_fft_mpn_mulmod_2expp1, a negacyclic convolution modulo word-size primes in floating point, whenever fft_adjust_limbs_sd_fft finds a suitable ring; it is about twice as fast for . See The negacyclic engine of fft_small and, for the algorithm, Algorithm.
Choice of parameters#
flint_mpn_mul_fft_main chooses in two steps.
- It tries in the order and stops at the first pair for which chunks of bits fit, .
- If , it shortens the transform by the factor given by the table entry
FFT_TAB[depth - 6][w - 1]and multiplies by , which keeps the capacity roughly the same with times fewer, larger coefficients. It then lowers in steps of , keeping a multiple of 64, as long as the product still fits, and callsmul_truncate_sqrt2. Otherwise, if , it halves the transform length and triples , and then callsmul_mfa_truncate_sqrt2.
For small products, few large coefficients win because the pointwise products, by Toom–Cook and basecase multiplication, are faster per bit than additional transform layers; for large products, the transform must stay long so that the coefficients and the pointwise products remain small. The tables are tuned per processor with src/fft/tune/tune-fft.c. For , each with :
| target | FFT_TAB |
|---|---|
| generic, ARM64, Apple M1, Skylake, Zen 3 | |
| Ice Lake, Sapphire Rapids, Meteor Lake, Arrow Lake | |
| Raptor Lake | |
| Tiger Lake | |
| Broadwell | |
| Zen 4 |
With the generic table, balanced products of limbs each get the following parameters ("first fit" is the pair found in step 1, "truncate" and "MFA" stand for mul_truncate_sqrt2 and mul_mfa_truncate_sqrt2):
| first fit | used | function | points | pointwise products | ||||
|---|---|---|---|---|---|---|---|---|
| 100 | (6, 2) | (2, 416) | truncate | 16 | 1664 | 830 | 15 | basecase |
| 1000 | (8, 2) | (6, 16) | truncate | 256 | 1024 | 508 | 251 | basecase |
| 10000 | (10, 1) | (8, 10) | truncate | 1024 | 2560 | 1275 | 1003 | basecase |
| 32000 | (10, 2) | (9, 8) | truncate | 2048 | 4096 | 2043 | 2005 | basecase |
| (11, 2) | (11, 2) | MFA | 8192 | 4096 | 2042 | 6269 | basecase | |
| (12, 2) | (11, 6) | MFA | 8192 | 12288 | 6138 | 6257 | 16 pieces of 768 bits | |
| (13, 1) | (13, 1) | MFA | 32768 | 8192 | 4089 | 31303 | basecase | |
| (15, 1) | (14, 3) | MFA | 65536 | 49152 | 24568 | 52101 | 32 pieces of 1536 bits | |
| (16, 2) | (15, 6) | MFA | 131072 | 196608 | 98296 | 130219 | 128 pieces of 1536 bits |
The pointwise products of fft_mulmod_2expp1 use with from MULMOD_TAB, indexed by and clamped at the last entry. The values lie between 1 and 4; the generic table has 19 entries, . The pieces then have between about and bits, as in the last column above. In builds without fft_small, flint_mpn_mul switches from GMP to flint_mpn_mul_fft_main at FLINT_FFT_MUL_THRESHOLD limbs.
Complexity#
In this section is the bit size of the operands. A transform of length with -bit coefficients costs butterflies of bit operations. With , the transforms cost bit operations, and the pointwise products cost , where is the cost of a multiplication modulo . Choosing and applying the method recursively to the pointwise products gives the bound of [SS1971], since each of the levels costs . FLINT stops after one negacyclic level and uses the best available multiplication for the remaining products of a few thousand bits. In practice the cost per bit grows slowly with and is dominated by the memory traffic of the transforms, which the matrix Fourier algorithm keeps in cache. FFTs modulo word-size primes in floating point are faster on current processors (FFTs modulo word-size primes in floating point); FFTs in FLINT compares the methods.
Referenced from#
- Discrete Fourier transforms over a generic ring (algorithm)
- Multiplication with several primes and CRT (algorithm)
- Truncated FFTs (algorithm)
- Multiplying integers (overview)
- Kronecker substitution (algorithm)
- Multiplying polynomials over Z (overview)
- Multiplying polynomials over Z/nZ (overview)
- Convolutions of large integers
- fft.h (module)
flint_mpn_mul_fft_main- Multiplying large integers
- Multiplication modulo 2^N + 1
- Multiplication modulo 2^N + 1
_fmpz_poly_mul_SS,fmpz_poly_mul_SS,_fmpz_poly_mullow_SS,fmpz_poly_mullow_SS,_fmpz_poly_mulmid_SS,fmpz_poly_mulmid_SS
References#
- [SS1971] A. Schönhage and V. Strassen. Schnelle Multiplikation großer Zahlen. Computing 7 (1971), 281–292.
- [Fur2009] M. Fürer. Faster integer multiplication. SIAM Journal on Computing 39 (2009), 979–1005.
- [HvdH2021] D. Harvey and J. van der Hoeven. Integer multiplication in time . Annals of Mathematics 193 (2021), 563–617.
- [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.
- [Pol1971] J. M. Pollard. The fast Fourier transform in a finite field. Mathematics of Computation 25 (1971), 365–374.
- [Nus1980] H. J. Nussbaumer. Fast polynomial transform algorithms for digital convolution. IEEE Transactions on Acoustics, Speech and Signal Processing 28 (1980), 205–215.
- [CF1994] R. Crandall and B. Fagin. Discrete weighted transforms and large-integer arithmetic. Mathematics of Computation 62 (1994), 305–324.
The FLINT team, “Schönhage–Strassen multiplication,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/fft/schonhage-strassen.html (generated 2026-11-02).
@misc{flint-doc-algorithms-fft-schonhage-strassen,
author = {{The FLINT team}},
title = {{Schönhage–Strassen multiplication}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/algorithms/fft/schonhage-strassen.html},
note = {Generated 2026-11-02}
}