Modular composition
#include <flint/nmod_poly.h>void _nmod_poly_compose_mod(nn_ptr res, nn_srcptr f, slong lenf, nn_srcptr g, nn_srcptr h, slong lenh, nmod_t mod)sourcevoid nmod_poly_compose_mod(nmod_poly_t res, const nmod_poly_t f, const nmod_poly_t g, const nmod_poly_t h)sourceSet res to . The non-underscore version accepts any and and throws FLINT_DIVZERO for . The underscore version requires , and reduced and zero-padded to length , writes coefficients and does not allow aliasing. It uses Horner's rule if or , and the Brent–Kung algorithm otherwise.
void _nmod_poly_compose_mod_horner(nn_ptr res, nn_srcptr f, slong lenf, nn_srcptr g, nn_srcptr h, slong lenh, nmod_t mod)sourcevoid nmod_poly_compose_mod_horner(nmod_poly_t res, const nmod_poly_t f, const nmod_poly_t g, const nmod_poly_t h)sourceModular composition by Horner's rule, with multiplications modulo , and the conventions of _nmod_poly_compose_mod.
void _nmod_poly_compose_mod_brent_kung(nn_ptr res, nn_srcptr f, slong lenf, nn_srcptr g, nn_srcptr h, slong lenh, nmod_t mod)sourcevoid nmod_poly_compose_mod_brent_kung(nmod_poly_t res, const nmod_poly_t f, const nmod_poly_t g, const nmod_poly_t h)sourceModular composition by the Brent–Kung algorithm, with the conventions of _nmod_poly_compose_mod. Requires (); the non-underscore version throws FLINT_ERROR otherwise.
void _nmod_poly_compose_mod_brent_kung_preinv(nn_ptr res, nn_srcptr poly1, slong len1, nn_srcptr poly2, nn_srcptr poly3, slong len3, nn_srcptr poly3inv, slong len3inv, nmod_t mod)sourcevoid nmod_poly_compose_mod_brent_kung_preinv(nmod_poly_t res, const nmod_poly_t poly1, const nmod_poly_t poly2, const nmod_poly_t poly3, const nmod_poly_t poly3inv)sourceSet res to by the Brent–Kung algorithm, given the inverse poly3inv of the reversal of poly3 as for nmod_poly_mulmod_preinv. Requires ; the underscore version also requires poly2 reduced and zero-padded to length .
void _nmod_poly_precompute_matrix(nmod_mat_t A, nn_srcptr poly1, nn_srcptr poly2, slong len2, nn_srcptr poly2inv, slong len2inv, nmod_t mod)sourcevoid nmod_poly_precompute_matrix(nmod_mat_t A, const nmod_poly_t poly1, const nmod_poly_t poly2, const nmod_poly_t poly2inv)sourceSet the rows of the matrix to for , where and , given the inverse poly2inv of the reversal of poly2. must be initialised with these dimensions (the non-underscore version throws FLINT_ERROR otherwise). The underscore version requires poly1 reduced and zero-padded to length .
void _nmod_poly_compose_mod_brent_kung_precomp_preinv(nn_ptr res, nn_srcptr poly1, slong len1, const nmod_mat_t A, nn_srcptr poly3, slong len3, nn_srcptr poly3inv, slong len3inv, nmod_t mod)sourcevoid nmod_poly_compose_mod_brent_kung_precomp_preinv(nmod_poly_t res, const nmod_poly_t poly1, const nmod_mat_t A, const nmod_poly_t poly3, const nmod_poly_t poly3inv)sourceSet res to , where is the matrix computed by nmod_poly_precompute_matrix for and poly3. Requires . This is the function to use for many compositions with the same and modulus, as in the computation of Frobenius powers in polynomial factorisation [GatSho1992] [KalSho1998].
Modular composition computes for polynomials with . It is the key operation of polynomial factorisation over finite fields, where it computes the iterates of the Frobenius map, and of the computation of minimal polynomials and of field embeddings. Horner's rule needs multiplications modulo , that is, operations; the algorithm of Brent and Kung [BrentKung1978] reduces this to , where is the exponent of matrix multiplication.
AlgorithmComposition and modular compositionComputing , and by Horner's rule, divide and conquer, Taylor shifts, the baby-step giant-step method of Brent and Kung, and the algorithm of Kinoshita and Li, with the choices made bynmod_poly.
The Brent–Kung algorithm#
Let and write with blocks of length . Then
The baby steps form the rows of an matrix , and all together are the rows of the product , where row of the matrix holds the coefficients of . The giant steps evaluate for by Horner's rule or rectangular splitting (_nmod_poly_mod_matrix_rows_evaluate), with about multiplications modulo . The matrix product is done by nmod_mat_mul (see Multiplying matrices over Z/nZ).
The matrix depends only on and . nmod_poly_precompute_matrix computes it once (it must be initialised with rows and columns), and nmod_poly_compose_mod_brent_kung_precomp_preinv then composes any with at the cost of the matrix product and the giant steps. The preinv versions take the inverse of the reversal of , as explained in Computing modulo a fixed polynomial.
nmod_poly_compose_mod uses Horner's rule for and when (Brent–Kung requires ), and the Brent–Kung algorithm otherwise.
Example: Rabin's irreducibility test#
A polynomial of degree over is irreducible if and only if and for every prime dividing [Rab1980b]. The powers are computed by one exponentiation, , followed by repeated composition with , using one precomputed matrix:
#include <flint/flint.h>
#include <flint/ulong_extras.h>
#include <flint/nmod_mat.h>
#include <flint/nmod_poly.h>
#include <flint/nmod_poly_factor.h>
/* x^(p^i) mod h for i = 0, ..., n, by composition with x^p */
int is_irreducible_rabin(const nmod_poly_t h)
{
slong n = nmod_poly_degree(h), i, m = n_sqrt(n) + 1;
ulong p = h->mod.n;
nmod_poly_t hinv, xp, t, g;
nmod_mat_t A;
int result = 1;
nmod_poly_init_mod(hinv, h->mod);
nmod_poly_init_mod(xp, h->mod);
nmod_poly_init_mod(t, h->mod);
nmod_poly_init_mod(g, h->mod);
nmod_mat_init(A, m, n, p);
nmod_poly_reverse(hinv, h, h->length);
nmod_poly_inv_series(hinv, hinv, h->length);
nmod_poly_powmod_x_ui_preinv(xp, p, h, hinv); /* x^p mod h */
nmod_poly_precompute_matrix(A, xp, h, hinv);
nmod_poly_set(t, xp); /* t = x^(p^1) */
for (i = 1; i <= n; i++)
{
/* gcd test for the maximal proper divisors i = n/q of n */
if (i < n && n % i == 0 && n_is_prime(n / i))
{
nmod_poly_set_coeff_ui(g, 1, 1);
nmod_poly_sub(g, t, g); /* x^(p^i) - x */
nmod_poly_gcd(g, g, h);
if (!nmod_poly_is_one(g))
result = 0;
nmod_poly_zero(g);
}
if (i < n) /* t = t(x^p) */
nmod_poly_compose_mod_brent_kung_precomp_preinv(t, t, A, h, hinv);
}
if (!nmod_poly_is_gen(t)) /* x^(p^n) == x */
result = 0;
nmod_mat_clear(A);
nmod_poly_clear(hinv);
nmod_poly_clear(xp);
nmod_poly_clear(t);
nmod_poly_clear(g);
return result;
}
int main(void)
{
flint_rand_t state;
nmod_poly_t a, b, h;
ulong p = 1000003;
flint_rand_init(state);
nmod_poly_init(a, p);
nmod_poly_init(b, p);
nmod_poly_init(h, p);
nmod_poly_randtest_monic_irreducible(a, state, 61); /* degree 60 */
nmod_poly_randtest_monic_irreducible(b, state, 41); /* degree 40 */
flint_printf("a irreducible: %d (library: %d)\n",
is_irreducible_rabin(a), nmod_poly_is_irreducible(a));
nmod_poly_mul(h, a, b);
flint_printf("a*b irreducible: %d\n", is_irreducible_rabin(h));
nmod_poly_mul(h, a, a);
flint_printf("a^2 irreducible: %d\n", is_irreducible_rabin(h));
nmod_poly_clear(a);
nmod_poly_clear(b);
nmod_poly_clear(h);
flint_rand_clear(state);
return 0;
}a irreducible: 1 (library: 1) a*b irreducible: 0 a^2 irreducible: 0
nmod_poly_is_irreducible is declared in nmod_poly_factor.h. Each composition costs one by matrix product and about modular multiplications, against squarings modulo for an exponentiation, which is where modular composition pays off for large [GatSho1992] [KalSho1998].
Guides#
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
- [BrentKung1978] Brent, R. P. and Kung, H. T. : Fast Algorithms for Manipulating Formal Power Series, J. ACM 25:4 (1978) 581–595
- [Rab1980b] M. O. Rabin. Probabilistic algorithms in finite fields. SIAM Journal on Computing 9 (1980), 273–280.
The FLINT team, “Modular composition (nmod_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly/compose_mod.html (generated 2026-11-02).
@misc{flint-doc-nmod-poly-compose-mod,
author = {{The FLINT team}},
title = {{Modular composition (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/compose_mod.html},
note = {Generated 2026-11-02}
}