nmod_poly.hComputing modulo a fixed polynomial
Many algorithms compute in the quotient ring : arithmetic in a finite field for an irreducible of degree , distinct-degree factorisation, primality tests for polynomials, and the computation of in general. An element of the quotient ring is represented by its remainder modulo , a polynomial of degree less than , and the basic operation is a product followed by a reduction modulo . The leading coefficient of must be invertible; for a field, must be prime and irreducible.
Reduction with a precomputed inverse#
A remainder modulo can be computed with nmod_poly_rem, which for large uses Newton iteration to find the quotient. That iteration computes an inverse of the reversed divisor each time. When the same is used many times, this inverse should be computed once. Write for the reversal of a polynomial viewed with length . If with and of length , then reversing gives
so the quotient is
a truncated product with the power series inverse of the reversal of , and the remainder is , of which only the low coefficients need to be computed. The reversal has constant term , which is why the leading coefficient must be invertible.
FLINT calls this inverse finv (or Binv, poly3inv, ...) and computes it with
nmod_poly_reverse(finv, f, f->length);
nmod_poly_inv_series(finv, finv, f->length);
A product of two reduced elements has length at most , so the quotient has length at most and finv must be correct modulo ; computing it modulo as above is the convention. Every function with preinv in its name expects this finv: nmod_poly_mulmod_preinv, nmod_poly_divrem_newton_n_preinv, nmod_poly_div_newton_n_preinv, the powmod functions with preinv, nmod_poly_powers_mod_bsgs (which computes it itself) and the Brent–Kung modular composition functions. A reduction then costs two products of length about , against the inversion and two products of nmod_poly_rem.
For moduli with at most five nonzero coefficients below the leading one, such as the trinomials and pentanomials returned by nmod_poly_minimal_irreducible, _nmod_poly_divrem_newton_n_preinv detects the sparsity (for ) and reduces directly, with a few multiply-adds per coefficient of the dividend and no products.
Which function to use#
| Task | Function |
|---|---|
| One product, small | nmod_poly_mulmod |
| Many products modulo | nmod_poly_mulmod_preinv |
| Many products by the same | nmod_poly_mulmod_precond |
nmod_poly_powmod_ui_binexp_preinv, nmod_poly_powmod_fmpz_binexp_preinv | |
nmod_poly_powmod_x_ui_preinv, nmod_poly_powmod_x_fmpz_preinv | |
nmod_poly_powers_mod_bsgs | |
nmod_poly_invmod | |
nmod_poly_compose_mod, or with a precomputed matrix nmod_poly_compose_mod_brent_kung_precomp_preinv |
The preinv and precond functions require their inputs to be reduced modulo ; the plain mulmod and powmod functions reduce them first. nmod_poly_mulmod_precond precomputes either the matrix of multiplication by (fastest for up to a few hundred) or the quotient (Shoup's method), and nmod_poly_mulmod_precond_init_num chooses between them from the expected number of multiplications. The precomputed object keeps pointers to , and finv, which must not change while it is used.
The function is the Frobenius automorphism of , and for successive is the core of distinct-degree factorisation. Once is known, is a modular composition, which Brent and Kung's algorithm computes in operations instead of the of an exponentiation [GatSho1992] [KalSho1998]. The matrix of powers of can be precomputed with nmod_poly_precompute_matrix and reused for every step; see Modular composition.
Example: arithmetic in a finite field#
The program constructs with the sparsest irreducible polynomial of degree 8 and checks some identities: the Frobenius automorphism has order 8, the multiplicative group has order , inverses multiply to 1, and 1000 multiplications by a fixed element with a preconditioned object agree with one exponentiation.
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/nmod_poly.h>
int main(void)
{
nmod_poly_t f, finv, a, b, t;
nmod_poly_mulmod_precond_t pre;
fmpz_t e;
slong i;
nmod_poly_init(f, 101);
nmod_poly_init_mod(finv, f->mod);
nmod_poly_init_mod(a, f->mod);
nmod_poly_init_mod(b, f->mod);
nmod_poly_init_mod(t, f->mod);
fmpz_init(e);
nmod_poly_minimal_irreducible(f, 8);
flint_printf("f = "); nmod_poly_print_pretty(f, "x"); flint_printf("\n");
/* finv = 1 / rev(f) mod x^(d+1) */
nmod_poly_reverse(finv, f, f->length);
nmod_poly_inv_series(finv, finv, f->length);
/* x^(p^8) = x in GF(p^8), but x^(p^4) != x */
fmpz_ui_pow_ui(e, 101, 8);
nmod_poly_powmod_x_fmpz_preinv(t, e, f, finv);
flint_printf("x^(p^8) == x: %d\n", nmod_poly_is_gen(t));
fmpz_ui_pow_ui(e, 101, 4);
nmod_poly_powmod_x_fmpz_preinv(t, e, f, finv);
flint_printf("x^(p^4) == x: %d\n", nmod_poly_is_gen(t));
/* a^(p^8 - 1) = 1 for a = x + 2 */
nmod_poly_set_coeff_ui(a, 1, 1);
nmod_poly_set_coeff_ui(a, 0, 2);
fmpz_ui_pow_ui(e, 101, 8);
fmpz_sub_ui(e, e, 1);
nmod_poly_powmod_fmpz_binexp_preinv(t, a, e, f, finv);
flint_printf("a^(p^8 - 1) == 1: %d\n", nmod_poly_is_one(t));
/* inverse */
nmod_poly_invmod(b, a, f);
nmod_poly_mulmod_preinv(t, a, b, f, finv);
flint_printf("a * a^-1 == 1: %d\n", nmod_poly_is_one(t));
/* 1000 multiplications by a with a preconditioned object */
nmod_poly_mulmod_precond_init_num(pre, a, f, finv, 1000);
nmod_poly_one(b);
for (i = 0; i < 1000; i++)
nmod_poly_mulmod_precond(b, pre, b);
nmod_poly_mulmod_precond_clear(pre);
nmod_poly_powmod_ui_binexp_preinv(t, a, 1000, f, finv);
flint_printf("a^1000 agrees: %d\n", nmod_poly_equal(b, t));
fmpz_clear(e);
nmod_poly_clear(f);
nmod_poly_clear(finv);
nmod_poly_clear(a);
nmod_poly_clear(b);
nmod_poly_clear(t);
return 0;
}f = x^8+2 x^(p^8) == x: 1 x^(p^4) == x: 0 a^(p^8 - 1) == 1: 1 a * a^-1 == 1: 1 a^1000 agrees: 1
The modulus is a binomial: an irreducible exists because the only prime factor 2 of 8 divides and, as , also . Reduction modulo costs one multiplication per coefficient, and products modulo are then dominated by the multiplication itself.
References#
- [GatSho1992] J. von zur Gathen and V. Shoup, "Computing Frobenius maps and factoring polynomials", Computational Complexity 2 (1992), 187-224. https://doi.org/10.1007/BF01272074
- [KalSho1998] E. Kaltofen and V. Shoup, "Subquadratic-time factoring of polynomials over finite fields", Mathematics of Computation 67 (1998), 1179-1197. https://doi.org/10.1090/S0025-5718-98-00944-2
The FLINT team, “Computing modulo a fixed polynomial (nmod_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly/modular-arithmetic.html (generated 2026-11-02).
@misc{flint-doc-nmod-poly-modular-arithmetic,
author = {{The FLINT team}},
title = {{Computing modulo a fixed polynomial (nmod\_poly.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/nmod_poly/modular-arithmetic.html},
note = {Generated 2026-11-02}
}