Multiplying large integers
#include <flint/fft_small.h>void mpn_ctx_mpn_mul(mpn_ctx_t R, ulong * z, const ulong * a, ulong an, const ulong * b, ulong bn)sourceSets to the product of and , using R or the default context. Requires , in either order. If a == b and , a square is computed.
Operands with (FLINT_MPN_MUL_NP2_MAX_AN) use the single-threaded two-prime pipeline. Larger ones use a profile chosen for the sizes and the number of threads available, and up to eight threads. The number of products accumulating in one coefficient is bounded using , so for very unbalanced operands it is slightly better to pass the longer one first. See Multiplying large integers.
void _mpn_ctx_mpn_mul_range(mpn_ctx_t R, ulong * z, ulong lo, ulong hi, const ulong * a, ulong an, const ulong * b, ulong bn)sourceWrites to the limbs of the product of and . The result is exact for ; otherwise carries from below limb may be lost, making it smaller than the exact window by a few units. Limbs at or above are zero.
Requires ; nothing is written if . Precisely, the result is congruent modulo to with . Only the coefficients of the chunk product that reach the window are reconstructed. A window shorter than the longer operand is computed as a transposed product of the shorter operand and the window when that transform is strictly shorter (not for squarings), with the same result; otherwise, above 40000 limbs, the transform is cyclic of half the length when the window allows it. See Algorithm.
These functions multiply nonnegative integers given as limb arrays, like GMP's mpn_mul. flint_mpn_mul calls mpn_mul_default_mpn_ctx from a few hundred limbs on, so a direct call is only needed to bypass the dispatch or to use a context of one's own; _mpn_ctx_mpn_mul_range computes only a window of limbs, as flint_mpn_mulhigh_n, flint_mpn_mullow_n and flint_mpn_mulmid do above their cutoffs.
Contract#
- ; the operands may come in either order, and the output must not overlap them. If
a == band , the square is computed, saving one forward transform per prime. mpn_ctx_mpn_mulwrites all limbs, the top one possibly zero._mpn_ctx_mpn_mul_rangewrites limbs, or nothing if . Limbs from on are zero. For it returns the exact window minus a small , modulo (so a deficit can borrow through limbs that are zero in the exact result), with . In practice is 0, 1 or 2. This is the usual contract of FLINT's high and middle products, whose callers allow for a few units of error in the lowest limb.- The context is used for scratch memory and must not be in use on another thread. The default context is local to the calling thread.
Algorithm#
The integers are cut into chunks of bits, and , and the chunk sequences are multiplied as polynomials. A coefficient of the product is a sum of at most products of chunks, so it is less than ; if the product of the primes exceeds this, the coefficients are recovered exactly from their residues. The product is , assembled with carries.
| Operands | Primes | Chunk size | Threads |
|---|---|---|---|
| 2 | 40 to 45 bits | 1 | |
| larger | 4 to 8 | 84 to 192 bits | up to 8 |
Up to 40000 limbs (FLINT_MPN_MUL_NP2_MAX_AN) the two-prime pipeline is used: the chunks are smaller than both primes, so the conversion into the transform is bit slicing with no reductions, and the reconstruction is a single vectorised Garner step. It chooses the shortest transform and then the largest chunk size in that satisfies the bound. For larger operands a profile is taken from a fixed table of fifteen, with primes and chunks of bits, reduced modulo all primes at once by vectorised code. Among the profiles whose bound allows , the one with the largest chunks for each competes, and the one with the smallest estimated cost wins, where is the transform length, the truncated length and ; with several threads only profiles whose is a multiple of the thread count qualify. The resulting choices for balanced products are, for example:
| (limbs) | 1000 | 40000 | 100000 | ||
|---|---|---|---|---|---|
| primes, chunk bits (one thread) | 2, 44 | 2, 41 | 5, 116 | 6, 136 | 7, 160 |
| transform length |
The transforms are truncated to the product length, rounded up to a multiple of 256. For a window, only the chunk coefficients that reach the limbs are reconstructed. Above 40000 limbs, if a power of two is at least both chunk counts and the highest needed coefficient, while the coefficients beyond fold onto coefficients that lie entirely below limb , the transform is cyclic of length , which can halve it for a high product.
A window of chunk coefficients that is shorter than the longer operand (in chunks) is computed, on both paths, as a transposed product when its transform is strictly shorter: the coefficients , , with the chunks of the shorter operand reversed and the chunks of the longer one from index on. This is the transpose of a product of lengths and , computed with the transposed transforms (sd_ifft_trunc_tr on , the forward transform of , a pointwise product and sd_fft_trunc_tr), so its transform has length about instead of the length of the full product: for the middle product of by limbs that a Newton iteration needs, about instead of limbs' worth of chunks. and are rounded so that starts on a limb boundary and the reconstruction sees the same coefficients at the same bit positions, so the result is the same, bit for bit, as that of the window of the full product. Squarings do not use it. The details, including the proofs of the bounds, are on the algorithm pages; for the transposition principle see Transposed FFT.
fft_small multiplies integers and polynomials whose product coefficients exceed one prime: chunk sizes and the number of primes, the two-prime and multi-prime pipelines, Chinese remaindering with the normalisation folded in, windows computed with wrap-around, sums of products in transform space, precomputed and shared operands, and transposed products for middle products.AlgorithmFFTs modulo word-size primes in floating pointThe number-theoretic transforms of 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.Memory and threads#
The transforms of both operands are kept for all primes: arrays of doubles, taken from the context's scratch buffer, which is kept for the next call. For two operands of 40000 limbs this is 4 MiB, about six times the size of the product. Above 40000 limbs the work is spread over up to eight threads of FLINT's thread pool, by prime for the transforms and by ranges of coefficients for the reconstruction.
Example#
Multiplying by , and computing the top four limbs of the product alone:
#include <gmp.h>
#include <flint/flint.h>
#include <flint/mpn_extras.h>
#include <flint/fft_small.h>
int main(void)
{
mpz_t a, b, c;
nn_ptr z, w;
slong an, bn, cn, lo, hi;
mpz_init(a);
mpz_init(b);
mpz_init(c);
mpz_ui_pow_ui(a, 3, 300000);
mpz_ui_pow_ui(b, 7, 100000);
an = mpz_size(a);
bn = mpz_size(b);
/* full product with the default context of this thread */
z = flint_malloc((an + bn) * sizeof(ulong));
mpn_mul_default_mpn_ctx(z, mpz_limbs_read(a), an, mpz_limbs_read(b), bn);
mpz_mul(c, a, b);
cn = mpz_size(c);
flint_printf("%wd x %wd limbs: %s\n", an, bn,
(mpn_cmp(z, mpz_limbs_read(c), cn) == 0
&& flint_mpn_zero_p(z + cn, an + bn - cn)) ? "equal to mpz_mul" : "WRONG");
/* the top four limbs alone */
lo = an + bn - 4;
hi = an + bn;
w = flint_malloc((hi - lo) * sizeof(ulong));
_mpn_ctx_mpn_mul_range(get_default_mpn_ctx(), w, lo, hi,
mpz_limbs_read(a), an, mpz_limbs_read(b), bn);
flint_printf("limbs [%wd, %wd): %s\n", lo, hi,
mpn_cmp(w, z + lo, hi - lo) == 0 ? "exact" : "low by a few units");
flint_free(z);
flint_free(w);
mpz_clear(a);
mpz_clear(b);
mpz_clear(c);
flint_cleanup_master();
return 0;
}7430 x 4387 limbs: equal to mpz_mul limbs [11813, 11817): exact
Guides#
The FLINT team, “Multiplying large integers (fft_small.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fft_small/mpn_mul.html (generated 2026-11-02).
@misc{flint-doc-fft-small-mpn-mul,
author = {{The FLINT team}},
title = {{Multiplying large integers (fft\_small.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/fft_small/mpn_mul.html},
note = {Generated 2026-11-02}
}