Polynomial multiplication over Z/nZ for multiword n
#include <flint/mpn_mod.h>int _mpn_mod_poly_mullow(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong len, gr_ctx_t ctx)sourceint _mpn_mod_poly_mulmid(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong nlo, slong nhi, gr_ctx_t ctx)sourceLow and middle products with automatic choice of algorithm. These are the _gr_poly_mullow and _gr_poly_mulmid methods of the ring.
int _mpn_mod_poly_mullow_classical(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong len, gr_ctx_t ctx)sourceint _mpn_mod_poly_mulmid_classical(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong nlo, slong nhi, gr_ctx_t ctx)sourceSchoolbook multiplication: each output coefficient is an unreduced dot product of fixed limb size, reduced once.
int _mpn_mod_poly_mullow_karatsuba(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong len, slong cutoff, gr_ctx_t ctx)sourceint _mpn_mod_poly_mulmid_karatsuba(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong nlo, slong nhi, slong cutoff, gr_ctx_t ctx)sourceKaratsuba multiplication on unreduced integer coefficients (_flint_mpn_poly_mulmid_karatsuba), recursing down to schoolbook multiplication when the shorter operand is shorter than cutoff; a cutoff of selects the tuned value. The coefficients are reduced only at the end.
int _mpn_mod_poly_mulmid_mpn(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong nlo, slong nhi, gr_ctx_t ctx)sourceMiddle product without intermediate reductions, choosing between the schoolbook algorithm and Karatsuba with the tuned cutoff of _flint_mpn_poly_mulmid, with output sizes given by _flint_mpn_poly_mulmid_slimbs. This is what _mpn_mod_poly_mulmid uses for short and medium lengths.
int _mpn_mod_poly_mullow_KS(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong len, gr_ctx_t ctx)sourceint _mpn_mod_poly_mulmid_KS(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong nlo, slong nhi, gr_ctx_t ctx)sourceMultiplication by Kronecker substitution: the coefficients are packed into integers with bits per coefficient, the integers are multiplied with flint_mpn_mul and the output coefficients are extracted and reduced.
int _mpn_mod_poly_mullow_fft_small(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong len, gr_ctx_t ctx)sourceint _mpn_mod_poly_mulmid_fft_small(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, slong len2, slong nlo, slong nhi, gr_ctx_t ctx)sourceMultiplication with the small-prime FFT of fft_small: the coefficients are reduced modulo up to eight 50-bit primes, multiplied by number-theoretic transforms and reconstructed by Chinese remaindering. Returns GR_UNABLE, writing nothing, if fft_small is not available or the product of eight primes is too small for the coefficients of the integer product, which limits to about 190 bits (196 bits for very short, 189 bits for lengths around ).
These functions multiply polynomials over an mpn_mod ring. The dispatchers _mpn_mod_poly_mullow and _mpn_mod_poly_mulmid are the ring's _gr_poly_mullow and _gr_poly_mulmid methods, so every generic polynomial algorithm over the ring (gr_poly_mul, Newton iteration, half-gcd, composition, ...) multiplies through them. The other functions are the individual algorithms, useful for testing and profiling.
All of them multiply the residues as integers in and reduce each output coefficient once, at the end. The algorithms differ in how they compute the integer coefficients of the product: schoolbook multiplication and Karatsuba on fixed-size limb arrays, Kronecker substitution into one large integer product, or number-theoretic transforms modulo small primes.
Windows#
A middle product with window computes only the coefficients , , of , and stores at res[0]. Coefficients of and that cannot contribute to the window are skipped first: terms of index at least , and, for , the lowest coefficients of an operand that is longer than the number of coefficients from to the end of the product (they only reach indices below ). After this, _mpn_mod_poly_mulmid chooses an algorithm for the remaining lengths.
Choice of algorithm#
Let after the truncation.
- If or the window has at most 4 coefficients, the schoolbook algorithm (
_mpn_mod_poly_mulmid_classical) is used. - A cutoff is read from one of five tables indexed by the bit size of in steps of 16 bits, for squaring, low squaring, unbalanced products (), full products and other windows. The tables were generated by the profiling program
p-poly_mullow; is between 25 and 173 and decreases as grows. For example, for full products of balanced operands is 91 at 80 bits, 95 at 256 bits, 54 at 512 bits and 25 at 1024 bits. - If and , the product is computed without intermediate reductions by
_mpn_mod_poly_mulmid_mpn, which leaves the choice between the schoolbook algorithm and Karatsuba to_flint_mpn_poly_mulmidofmpn_extras, with its own tuned cutoff. - Otherwise the small-prime FFT (
_mpn_mod_poly_mulmid_fft_small) is tried, and if it declines (fft_smallunavailable, or larger than about 190 bits), Kronecker substitution (_mpn_mod_poly_mulmid_KS) is used. The tables assume that the FFT always beats Kronecker substitution when it can be used, which is nearly but not always true.
Schoolbook and Karatsuba#
The schoolbook algorithm computes each as an exact sum of full products in limbs and reduces it with mpn_mod_set_mpn. A sum of at most products of residues below is less than , so
limbs suffice, usually or . The sums are computed by the generated fixed-size kernels of mpn_extras (with the dot products inlined into the loop over the output coefficients) for and with flint_mpn_mul_n and mpn_add_n otherwise. The first and last coefficients, which are single products, are computed with mpn_mod_mul instead.
Karatsuba's algorithm [KO1962] splits each operand into a low and a high half and computes the three half-size products , and recursively. Over with reduced coefficients the sums would need reductions, and the middle term needs a subtraction; FLINT instead runs the whole recursion on the unreduced integer coefficients, which grow by about one bit per level for the sums and are allowed a sign, and reduces only the final coefficients. The output size bound of _flint_mpn_poly_mulmid_slimbs accounts for the growth: bits (with one extra limb when the operand coefficients have no spare high bit and the recursion must extend them). This is why Karatsuba pays off from short lengths: an addition of residues of limbs costs much less than a multiplication.
Kronecker substitution#
Kronecker substitution [Kro1882; Sch1982] evaluates both polynomials at with , which exceeds the bit size of every coefficient of the integer product, multiplies the two resulting integers with flint_mpn_mul (which uses GMP, or fft_small for large operands) and reads the coefficients off the product bits at a time, reducing each modulo . Only the windowed coefficients are extracted. The packing wastes the padding bits but reduces the polynomial product to a single integer product, which is efficient for large , where the small-prime FFT is not available.
Small-prime FFT#
_mpn_mod_poly_mulmid_fft_small reduces every coefficient modulo the 50-bit primes of fft_small, multiplies the polynomials modulo each prime with truncated number-theoretic transforms and reconstructs the integer coefficients by Chinese remaindering, reducing them modulo directly from the remainder representation. It needs a product of primes exceeding , and at most eight primes are available, which limits to about 190 (196 bits for very short products, 189 bits at length ); above that it returns GR_UNABLE. See Multiplication with several primes and CRT and FFTs modulo word-size primes in floating point for the transforms and the reconstruction.
Example#
The program below computes a full product and a middle product of two polynomials of length 3 modulo , the second time also with Kronecker substitution. Coefficients are stored in arrays of MPN_MOD_MAX_LIMBS limbs per slot to be independent of the word size; nl is the actual number of limbs.
#include <flint/fmpz.h>
#include <flint/gr.h>
#include <flint/mpn_mod.h>
#define MAXL MPN_MOD_MAX_LIMBS
static void print_poly(nn_srcptr a, slong len, gr_ctx_t ctx)
{
slong i, nl = MPN_MOD_CTX_NLIMBS(ctx);
fmpz_t c;
fmpz_init(c);
for (i = 0; i < len; i++)
{
GR_MUST_SUCCEED(mpn_mod_get_fmpz(c, a + i * nl, ctx));
fmpz_print(c);
printf(i + 1 < len ? " " : "\n");
}
fmpz_clear(c);
}
int main(void)
{
gr_ctx_t ctx;
fmpz_t p;
ulong f[3 * MAXL], g[3 * MAXL], h[5 * MAXL];
slong i, nl;
int status = GR_SUCCESS;
fmpz_init(p);
fmpz_ui_pow_ui(p, 2, 127);
fmpz_sub_ui(p, p, 1);
GR_MUST_SUCCEED(gr_ctx_init_mpn_mod(ctx, p));
nl = MPN_MOD_CTX_NLIMBS(ctx);
/* f = 1 + 2x + 3x^2, g = 4 + 5x - 6x^2 */
for (i = 0; i < 3; i++)
{
status |= mpn_mod_set_si(f + i * nl, i + 1, ctx);
status |= mpn_mod_set_si(g + i * nl, i == 2 ? -6 : i + 4, ctx);
}
/* full product: coefficients 0 to 4 */
status |= _mpn_mod_poly_mullow(h, f, 3, g, 3, 5, ctx);
print_poly(h, 5, ctx);
/* middle product: coefficients 1, 2 and 3 */
status |= _mpn_mod_poly_mulmid(h, f, 3, g, 3, 1, 4, ctx);
print_poly(h, 3, ctx);
/* the same middle product by Kronecker substitution */
status |= _mpn_mod_poly_mulmid_KS(h, f, 3, g, 3, 1, 4, ctx);
print_poly(h, 3, ctx);
gr_ctx_clear(ctx);
fmpz_clear(p);
printf("status %d\n", status);
return 0;
}4 13 16 3 170141183460469231731687303715884105709 13 16 3 13 16 3 status 0
The leading coefficient is : residues are always printed in .
mpn_mod evaluates sums of products modulo an -limb exactly in to limbs and reduces once, in dot products, polynomial multiplication and division, Euclid's algorithm, Gaussian elimination and matrix multiplication, and how this plugs into the generic algorithms.OverviewMultiplying polynomials over Z/nZHow nmod_poly multiplies: classical products built on modular dot products, Kronecker substitution (KS, KS2, KS4), and the small-prime FFT of fft_small, with the length and modulus-size cutoffs that choose between them for full, low, middle and high products.References#
- [KO1962] A. Karatsuba and Yu. Ofman, "Multiplication of multidigit numbers on automata", Doklady Akademii Nauk SSSR 145 (1962), 293–294; English translation in Soviet Physics Doklady 7 (1963), 595–596
- [Kro1882] L. Kronecker, "Grundzüge einer arithmetischen Theorie der algebraischen Grössen", Journal für die reine und angewandte Mathematik 92 (1882), 1–122
- [Sch1982] A. Schönhage, "Asymptotically fast algorithms for the numerical multiplication and division of polynomials with complex coefficients", Computer Algebra (EUROCAM '82), LNCS 144 (1982), 3–15
The FLINT team, “Polynomial multiplication over Z/nZ for multiword n (mpn_mod.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/mpn_mod/poly-mul.html (generated 2026-11-02).
@misc{flint-doc-mpn-mod-poly-mul,
author = {{The FLINT team}},
title = {{Polynomial multiplication over Z/nZ for multiword n (mpn\_mod.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/mpn_mod/poly-mul.html},
note = {Generated 2026-11-02}
}