FLINT3.7.0-dev
Guide · fft.h

Multiplying integers and polynomials

The module offers three levels of functionality: integer multiplication with automatically chosen parameters (flint_mpn_mul_fft_main), convolutions of vectors of large integers with parameters chosen by the caller (fft_convolution and its variants), and the transforms themselves.

Choosing a function#

Memory layout#

A convolution of transform length 4n4n, n=2depthn = 2^{\mathit{depth}}, with coefficients modulo p=2N+1p = 2^N + 1, N=limbs⋅FLINT_BITSN = \mathit{limbs} \cdot \mathtt{FLINT\_BITS}, needs:

  1. For each operand an array of 4n4n pointers, each to its own block of limbs+1\mathit{limbs} + 1 limbs. One coefficient is a signed two's complement integer in this block.
  2. For each thread (flint_get_num_threads() of them), three blocks of limbs+1\mathit{limbs} + 1 limbs, collected in the pointer arrays t1, t2 and s1, and one block of 2(limbs+1)2(\mathit{limbs} + 1) limbs in the array tt.

The transforms swap pointers between the operand arrays and the scratch arrays, so all blocks should come from a single allocation, freed as a whole at the end, and the coefficients must always be accessed through the pointer array.

Choosing the parameters#

For a product with at most ℓ\ell coefficients whose absolute values are below 2N−12^{N - 1} (or nonnegative and below 2N2^N):

  1. Take depth=⌈log⁡2ℓ⌉−2\mathit{depth} = \lceil \log_2 \ell \rceil - 2 so that 2n<ℓ≤4n2n < \ell \le 4n, and pass trunc=ℓ\mathit{trunc} = \ell. Products of at most 2 coefficients are not supported.
  2. Round NN up to a multiple of nn and of FLINT_BITS\mathtt{FLINT\_BITS}, so that every point of the transform carries w=N/nw = N/n bits.
  3. If limbs=N/FLINT_BITS\mathit{limbs} = N/\mathtt{FLINT\_BITS} exceeds 128, replace it by fft_adjust_limbs(limbs), or by fft_adjust_limbs_sd_fft(limbs, n, &m) to allow the faster pointwise engine of fft_small.

The coefficient bound comes from the operands: with input coefficients below 2b12^{b_1} and 2b22^{b_2} in absolute value and the shorter operand of length ℓ2\ell_2, the product coefficients are below ℓ2 2b1+b2\ell_2 \, 2^{b_1 + b_2}. When the inputs are chunks of integers, as below, the chunk size bb is chosen instead so that 2b+depth+1≤N2b + \mathit{depth} + 1 \le N; this is the rule of mul_truncate_sqrt2.

A complete integer product#

The following program multiplies two integers of about 6300 bits with a convolution of length 256 modulo 2128+12^{128} + 1, which is what mul_truncate_sqrt2 does with depth=6\mathit{depth} = 6 and w=2w = 2. The chunks have b=⌊(128−7)/2⌋=60b = \lfloor (128 - 7)/2 \rfloor = 60 bits, so the product coefficients are below 105⋅2120<2128105 \cdot 2^{120} < 2^{128}.

Examplefft_pipeline.cchecked by make checkdoc
#include <gmp.h>
#include <flint/flint.h>
#include <flint/fft.h>
#include <flint/mpn_extras.h>

int main(void)
{
    /* transform length 4n = 256, coefficients modulo 2^128 + 1 */
    slong depth = 6, n = WORD(1) << depth, limbs = 2, size = limbs + 1;
    flint_bitcnt_t bits = (limbs * FLINT_BITS - depth - 1) / 2;   /* 60 */
    slong nt = flint_get_num_threads(), i, j1, j2, trunc;
    mp_limb_t ** ii, ** jj, ** t1, ** t2, ** s1, ** tt, * mem, * r;
    mp_size_t n1, n2;
    mpz_t a, b, c;

    mpz_init(a); mpz_init(b); mpz_init(c);
    mpz_ui_pow_ui(a, 3, 4000);
    mpz_ui_pow_ui(b, 5, 2700);
    n1 = mpz_size(a);
    n2 = mpz_size(b);

    /* 2 x 4n coefficients, plus per-thread scratch: t1, t2, s1 of
       size limbs + 1 and tt of size 2 (limbs + 1) */
    ii = flint_malloc(8 * n * sizeof(mp_limb_t *));
    jj = ii + 4 * n;
    t1 = flint_malloc(4 * nt * sizeof(mp_limb_t *));
    t2 = t1 + nt; s1 = t2 + nt; tt = s1 + nt;
    mem = flint_malloc((8 * n + 5 * nt) * size * sizeof(mp_limb_t));
    for (i = 0; i < 8 * n; i++)
        ii[i] = mem + i * size;
    for (i = 0; i < nt; i++)
    {
        t1[i] = mem + (8 * n + i) * size;
        t2[i] = t1[i] + nt * size;
        s1[i] = t2[i] + nt * size;
        tt[i] = mem + (8 * n + 3 * nt + 2 * i) * size;
    }

    /* split into 60-bit coefficients and pad with zeros */
    j1 = fft_split_bits(ii, mpz_limbs_read(a), n1, bits, limbs);
    j2 = fft_split_bits(jj, mpz_limbs_read(b), n2, bits, limbs);
    for (i = j1; i < 4 * n; i++) flint_mpn_zero(ii[i], size);
    for (i = j2; i < 4 * n; i++) flint_mpn_zero(jj[i], size);

    trunc = j1 + j2 - 1;            /* must satisfy 2n < trunc <= 4n */
    flint_printf("j1 = %wd, j2 = %wd, trunc = %wd, 4n = %wd\n", j1, j2, trunc, 4 * n);

    fft_convolution(ii, jj, depth, limbs, trunc, t1, t2, s1, tt);

    /* evaluate the product polynomial at 2^bits */
    r = flint_calloc(n1 + n2, sizeof(mp_limb_t));
    fft_combine_bits(r, ii, trunc, bits, limbs, n1 + n2);

    mpz_mul(c, a, b);
    flint_printf("equal to mpz_mul: %d\n",
        mpn_cmp(r, mpz_limbs_read(c), mpz_size(c)) == 0 &&
        (mpz_size(c) == (size_t) (n1 + n2) || r[n1 + n2 - 1] == 0));

    flint_free(r); flint_free(mem); flint_free(t1); flint_free(ii);
    mpz_clear(a); mpz_clear(b); mpz_clear(c);
    return 0;
}
Output
j1 = 107, j2 = 105, trunc = 211, 4n = 256
equal to mpz_mul: 1

The same program works for larger transforms, where fft_convolution switches to the threaded matrix Fourier algorithm; with depth=9\mathit{depth} = 9 and eight limbs it multiplies integers of half a million bits. The scratch arrays must then really have one entry per thread: the number of threads must not be increased between allocating them and the call.

Polynomials with integer coefficients#

For polynomials, each coefficient goes into its own block instead of being cut from an integer. Negative coefficients are stored in two's complement over all limbs+1\mathit{limbs} + 1 limbs, and a coefficient of the result is negative if its residue exceeds p/2p/2; _fmpz_vec_get_fft and _fmpz_vec_set_fft convert between fmpz vectors and this format. The example in Examples multiplies two polynomials with signed coefficients this way and compares the result with fmpz_poly_mul; the second example there uses the pointwise engine of fft_small. When only some coefficients of the product are wanted, fft_convolution_choose_mid can select a shorter cyclic transform.

Threads#

fft_convolution with depth>6\mathit{depth} > 6, fft_convolution_sd_fft, mul_mfa_truncate_sqrt2 (and so flint_mpn_mul_fft_main for large operands) and the splitting functions run in parallel on the threads of FLINT's global thread pool, as set by flint_set_num_threads. The precached convolutions without the fft_small engine and, apart from the splitting, mul_truncate_sqrt2 run in the calling thread.

Cite this page

The FLINT team, “Multiplying integers and polynomials (fft.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fft/using.html (generated 2026-11-02).