FLINT3.7.0-dev

Splitting integers into coefficients

Synopsis#include <flint/fft.h>
mp_size_t fft_split_limbs(mp_limb_t ** poly, mp_srcptr limbs, mp_size_t total_limbs, mp_size_t coeff_limbs, mp_size_t output_limbs)source
mp_size_t fft_split_bits(mp_limb_t ** poly, mp_srcptr limbs, mp_size_t total_limbs, flint_bitcnt_t bits, mp_size_t output_limbs)source

Split the integer (limbs, total_limbs) into consecutive chunks of coeff_limbs limbs, respectively bits bits, starting with the least significant, and write them to poly[0], poly[1], …, each zero-padded to \mathit{output\_limbs} + 1 limbs. Return the number of chunks written, \lceil \mathit{total\_limbs}/\mathit{coeff\_limbs} \rceil, respectively \lceil \mathtt{FLINT\_BITS} \cdot \mathit{total\_limbs}/\mathit{bits} \rceil; later entries of poly are not touched. Requires output_limbs to be at least the chunk size in limbs. May use threads.

void fft_combine_limbs(mp_limb_t * res, mp_limb_t ** poly, slong length, mp_size_t coeff_limbs, mp_size_t output_limbs, mp_size_t total_limbs)source
void fft_combine_bits(mp_limb_t * res, mp_limb_t ** poly, slong length, flint_bitcnt_t bits, mp_size_t output_limbs, mp_size_t total_limbs)source

Evaluate the polynomial with the length coefficients poly at B^{\mathit{coeff\_limbs}}, respectively 2^{\mathit{bits}}, and set (res, total_limbs) to the value, discarding the bits beyond total_limbs limbs. Each coefficient must be less than B^{\mathit{output\_limbs}} and have \mathit{output\_limbs} + 1 readable limbs. res must be zero on entry: the coefficients are added into it, but carries out of the window of each coefficient are lost; see Combining.

To multiply integers with a convolution, they are cut into chunks that become polynomial coefficients, and the product polynomial is evaluated at the chunk base to recover the product. These four functions do the cutting and the evaluation; mul_truncate_sqrt2, mul_mfa_truncate_sqrt2 and _fft_mulmod_2expp1 use them, and fmpz_mat_mul_fft uses them for matrix entries.

Splitting#

fft_split_bits(poly, x, total_limbs, bits, output_limbs) writes chunk i of x, that is \lfloor x / 2^{i \cdot \mathit{bits}} \rfloor \bmod 2^{\mathit{bits}}, to poly[i], zero-padded to \mathit{output\_limbs} + 1 limbs, for 0 \le i < \lceil \mathtt{FLINT\_BITS} \cdot \mathit{total\_limbs} / \mathit{bits} \rceil, and returns this count. The count depends only on total_limbs, so leading chunks may be zero. If bits is a multiple of \mathtt{FLINT\_BITS} it calls fft_split_limbs, which copies whole limbs. The chunks need \lceil \mathit{bits}/\mathtt{FLINT\_BITS} \rceil \le \mathit{output\_limbs} limbs. Entries of poly beyond the count are not touched, so callers zero them themselves when they pad the vector to the transform length. Both functions distribute the work over the threads of the global thread pool in blocks of 16 chunks.

Combining#

fft_combine_bits(res, poly, length, bits, output_limbs, total_limbs) adds \sum_{i < \mathit{length}} \mathit{poly}_i \cdot 2^{i \cdot \mathit{bits}} to (res, total_limbs) and discards everything beyond total_limbs limbs; fft_combine_limbs does the same with B^{\mathit{coeff\_limbs}}. The coefficients must be less than B^{\mathit{output\_limbs}}, which holds for reduced residues modulo 2^N + 1 with \mathit{output\_limbs} = N/\mathtt{FLINT\_BITS} unless a residue is 2^N, and they must have \mathit{output\_limbs} + 1 readable limbs, since fft_combine_bits shifts the top limb in when bits is not a multiple of \mathtt{FLINT\_BITS}.

Each coefficient is added into a window of \mathit{output\_limbs} + 1 limbs starting at the limb where it begins. When res starts at zero, the sum of the coefficients added so far is less than 2^{\mathit{output\_limbs} \cdot \mathtt{FLINT\_BITS} + i \cdot \mathit{bits} + 1} by a geometric series, which fits in the window of coefficient i, so no carry leaves the window and the result is exact.

For this reason res must be zero on entry, which builds configured with --enable-assert check. When res is not zero, carries out of a window are lost, so the functions do not add to res in general: with res = 2^{192} - 1 in four limbs and the constant polynomial 1, fft_combine_limbs(res, poly, 1, 1, 1, 4) would yield 2^{192} - 2^{128} instead of 2^{192}.

Example#

A 192-bit integer cut into 20-bit chunks and put together again. The top limb holds only 5 bits, but all ten chunks of the three limbs are written.

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

int main(void)
{
    mp_limb_t x[3] = { UWORD(0x0123456789abcdef), UWORD(0xfedcba9876543210), UWORD(0x1f) };
    mp_limb_t y[3] = { 0, 0, 0 };
    mp_limb_t mem[10 * 2], * poly[10];
    slong i, len;

    for (i = 0; i < 10; i++)
        poly[i] = mem + 2 * i;           /* output_limbs + 1 = 2 limbs each */

    /* 3 limbs = 192 bits -> 10 coefficients of 20 bits */
    len = fft_split_bits(poly, x, 3, 20, 1);
    flint_printf("%wd coefficients:", len);
    for (i = 0; i < len; i++)
        flint_printf(" %wx", poly[i][0]);
    flint_printf("\n");

    /* evaluate at 2^20 into a zeroed result */
    fft_combine_bits(y, poly, len, 20, 1, 3);
    flint_printf("round trip: %d\n", mpn_cmp(x, y, 3) == 0);
    return 0;
}
Output
10 coefficients: bcdef 6789a 12345 32100 87654 dcba9 1ffe 0 0 0
round trip: 1

Guides#

Cite this page

The FLINT team, “Splitting integers into coefficients (fft.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fft/split.html (generated 2026-10-20).