Multiplying limb arrays
#include <flint/mpn_extras.h>mp_limb_t flint_mpn_mul(mp_ptr z, mp_srcptr x, mp_size_t xn, mp_srcptr y, mp_size_t yn)sourceSets (z, \mathit{xn} + \mathit{yn}) to the product of (x, \mathit{xn}) and (y, \mathit{yn}) and returns its top limb z_{xn + yn - 1}. Requires \mathit{xn} \ge \mathit{yn} \ge 1; z must not overlap the inputs.
For short operands this calls a hard-coded routine from a table (assembly on x86-64 with ADX for \mathit{xn} \le 16 and on ARM64 for \mathit{yn} \le 15; portable C for \mathit{xn} \le 7 elsewhere); otherwise _flint_mpn_mul selects between mpn_mul_1, GMP's mpn_mul_basecase (only without FLINT assembly), flint_mpn_mul_toom22, GMP's mpn_mul and the FFT.
void flint_mpn_mul_n(mp_ptr z, mp_srcptr x, mp_srcptr y, mp_size_t n)sourceSets (z, 2n) to the product of (x, n) and (y, n). Requires n \ge 1; z must not overlap the inputs.
mp_limb_t flint_mpn_sqr(mp_ptr z, mp_srcptr x, mp_size_t n)sourceSets (z, 2n) to the square of (x, n) and returns its top limb. Requires n \ge 1; z must not overlap x.
Sets (z_1, \mathit{an} + \mathit{xn}) to ax and (z_2, \mathit{bn} + \mathit{xn}) to bx. All lengths must be at least 1, in any order; z_1 and z_2 must overlap neither each other nor any input. When both products are long enough, the transform of x is computed only once (Integer products with a common operand).
flint_mpn_mul, flint_mpn_mul_n and flint_mpn_sqr are FLINT's replacements for GMP's mpn_mul, mpn_mul_n and mpn_sqr, with the same interface except for the order requirement of flint_mpn_mul (\mathit{xn} \ge \mathit{yn}) and the returned top limb. They are faster than GMP's functions at both ends of the size range: for short operands because of hand-written assembly routines for each size, and for long operands because of the small-prime FFT of fft_small. flint_mpn_mul_shared computes two products with a common factor.
Algorithm selection#
The functions are inline. On x86-64 with the ADX and BMI2 instructions (Broadwell and later, Zen and later) and on ARM64, products of short operands are computed by calling one routine from a table of hard-coded assembly kernels, with no further dispatch:
| Architecture | flint_mpn_mul | flint_mpn_mul_n | flint_mpn_sqr |
|---|---|---|---|
| x86-64 with ADX | \mathit{xn} \le 16 (any \mathit{yn} \le \mathit{xn}) | n \le 16 | n \le 14 |
| ARM64 | \mathit{yn} \le 15 (any \mathit{xn}) | n \le 15 | n \le 9 |
| other | \mathit{xn} \le 7, or \mathit{xn} \le 14 and \mathit{yn} = 1 | n \le 7 | none |
On machines without FLINT assembly the table holds portable C code. The ARM64 kernels are indexed by the shorter length and loop over the longer operand, so they serve any \mathit{xn}. Beyond the table, _flint_mpn_mul uses, in order:
mpn_mul_1if \mathit{yn} = 1 (on ARM64 alsoflint_mpn_mul_2if \mathit{yn} = 2); on machines without FLINT assembly, GMP'smpn_mul_basecasefor \mathit{xn} \le 16;- on x86-64 with ADX and on ARM64,
flint_mpn_mul_toom22(Karatsuba recursing into the assembly kernels) if the operands are nearly balanced, 5\,\mathit{yn} \ge 4\,\mathit{xn}, and \mathit{yn} is at most 230 (x86-64) or 483 (ARM64); - GMP's
mpn_mul(Toom–Cook) below the FFT range; - the FFT:
mpn_mul_default_mpn_ctxfromfft_smallif available, and otherwise the Schönhage–Strassen FFT of thefftmodule (flint_mpn_mul_fft_main, fromFLINT_FFT_MUL_THRESHOLDlimbs).
With fft_small and the threshold T = FLINT_FFT_SMALL_MUL_THRESHOLD (1000 limbs in the generic flint-mparam.h, between 240 and 1000 limbs in the machine-specific ones), the FFT is used when \mathit{yn} \ge T, and for unbalanced operands already when \mathit{xn} + \mathit{yn} > 2T and \mathit{yn} > T/3: the cost of GMP's multiplication grows like (\mathit{xn}/\mathit{yn}) M(\mathit{yn}) while that of the FFT depends on \mathit{xn} + \mathit{yn}. _flint_mpn_mul_n follows the same steps, and _flint_mpn_sqr uses GMP's mpn_sqr below FLINT_FFT_SMALL_SQR_THRESHOLD and the FFT above.
Shared operands#
flint_mpn_mul_shared computes ax and bx. With fft_small, when _flint_mpn_mul_shared_use_fft says so, it calls _fft_small_mpn_mul_shared, which transforms x once and uses the transform for both products; otherwise it makes two calls to flint_mpn_mul. With T = FLINT_MPN_MUL_SHARED_THRESHOLD (by default the fft_small multiplication threshold), the shared transform is used when the longer product (by \max(\mathit{an}, \mathit{bn})) satisfies the FFT rule of flint_mpn_mul above with threshold T and the shorter product satisfies it with threshold T/2: once the longer product is transformed, the shorter one costs about its share of the transforms and need not reach the FFT range on its own. This is used for instance by the Chinese remaindering trees of Multimodular reduction and Chinese remaindering, where each node is multiplied by two cofactors.
Example#
The product of two 2000-limb numbers (in the fft_small range) agrees with GMP's, and a shared product agrees with two separate ones:
#include <gmp.h>
#include <flint/flint.h>
#include <flint/mpn_extras.h>
#define N 2000
int main(void)
{
flint_rand_t state;
mp_limb_t x[N], y[N], a[N / 2], z[2 * N], w[2 * N], z1[2 * N], z2[2 * N];
flint_rand_init(state);
flint_mpn_urandomb(x, state, N * FLINT_BITS);
flint_mpn_urandomb(y, state, N * FLINT_BITS);
flint_mpn_urandomb(a, state, N / 2 * FLINT_BITS);
flint_mpn_mul_n(z, x, y, N);
mpn_mul_n(w, x, y, N);
flint_printf("mul_n agrees with GMP: %d\n", mpn_cmp(z, w, 2 * N) == 0);
flint_mpn_sqr(z, x, N);
mpn_sqr(w, x, N);
flint_printf("sqr agrees with GMP: %d\n", mpn_cmp(z, w, 2 * N) == 0);
/* z1 = y x and z2 = a x with one transform of x */
flint_mpn_mul_shared(z1, y, N, z2, a, N / 2, x, N);
flint_printf("shared transform: %d\n", _flint_mpn_mul_shared_use_fft(N, N / 2, N));
flint_mpn_mul_n(z, y, x, N);
flint_mpn_mul(w, x, N, a, N / 2);
flint_printf("shared products agree: %d\n",
mpn_cmp(z1, z, 2 * N) == 0 && mpn_cmp(z2, w, 3 * N / 2) == 0);
flint_rand_clear(state);
return 0;
}mul_n agrees with GMP: 1 sqr agrees with GMP: 1 shared transform: 1 shared products agree: 1
Guides#
- Multiplying integers and polynomials (
fft) - Where FLINT uses fft_small (
fft_small)
The FLINT team, “Multiplying limb arrays (mpn_extras.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/mpn_extras/mul.html (generated 2026-10-20).
@misc{flint-doc-mpn-extras-mul,
author = {{The FLINT team}},
title = {{Multiplying limb arrays (mpn\_extras.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/mpn_extras/mul.html},
note = {Generated 2026-10-20}
}