FLINT3.7.0-dev

Modular composition

Synopsis#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)source
void nmod_poly_compose_mod(nmod_poly_t res, const nmod_poly_t f, const nmod_poly_t g, const nmod_poly_t h)source

Set res to f(g) mod hf(g) \bmod h. The non-underscore version accepts any ff and gg and throws FLINT_DIVZERO for h=0h = 0. The underscore version requires lenh≥2\mathit{lenh} \ge 2, lenf≥1\mathit{lenf} \ge 1 and gg reduced and zero-padded to length lenh−1\mathit{lenh} - 1, writes lenh−1\mathit{lenh} - 1 coefficients and does not allow aliasing. It uses Horner's rule if lenh<8\mathit{lenh} < 8 or lenf≥lenh\mathit{lenf} \ge \mathit{lenh}, 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)source
void nmod_poly_compose_mod_horner(nmod_poly_t res, const nmod_poly_t f, const nmod_poly_t g, const nmod_poly_t h)source

Modular composition by Horner's rule, with lenf−1\mathit{lenf} - 1 multiplications modulo hh, 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)source
void 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)source

Modular composition by the Brent–Kung algorithm, with the conventions of _nmod_poly_compose_mod. Requires lenf<lenh\mathit{lenf} < \mathit{lenh} (deg⁡f<deg⁡h\deg f < \deg h); 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)source
void 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)source

Set res to poly1(poly2) mod poly3\mathit{poly1}(\mathit{poly2}) \bmod \mathit{poly3} by the Brent–Kung algorithm, given the inverse poly3inv of the reversal of poly3 as for nmod_poly_mulmod_preinv. Requires len1<len3\mathit{len1} < \mathit{len3}; the underscore version also requires poly2 reduced and zero-padded to length len3−1\mathit{len3} - 1.

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)source
void nmod_poly_precompute_matrix(nmod_mat_t A, const nmod_poly_t poly1, const nmod_poly_t poly2, const nmod_poly_t poly2inv)source

Set the rows of the m×nm \times n matrix AA to poly1i mod poly2\mathit{poly1}^i \bmod \mathit{poly2} for 0≤i<m0 \le i < m, where n=deg⁡poly2n = \deg \mathit{poly2} and m=⌊n⌋+1m = \lfloor \sqrt{n} \rfloor + 1, given the inverse poly2inv of the reversal of poly2. AA 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 nn.

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)source
void 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)source

Set res to poly1(g) mod poly3\mathit{poly1}(g) \bmod \mathit{poly3}, where AA is the matrix computed by nmod_poly_precompute_matrix for gg and poly3. Requires len1<len3\mathit{len1} < \mathit{len3}. This is the function to use for many compositions with the same gg and modulus, as in the computation of Frobenius powers in polynomial factorisation [GatSho1992] [KalSho1998].

Modular composition computes f(g) mod hf(g) \bmod h for polynomials f,g,hf, g, h with deg⁡f,deg⁡g<deg⁡h=n\deg f, \deg g < \deg h = n. It is the key operation of polynomial factorisation over finite fields, where it computes the iterates xpi mod hx^{p^i} \bmod h of the Frobenius map, and of the computation of minimal polynomials and of field embeddings. Horner's rule needs n−1n - 1 multiplications modulo hh, that is, O(nM(n))O(n M(n)) operations; the algorithm of Brent and Kung [BrentKung1978] reduces this to O(nM(n)+n(ω+1)/2)O(\sqrt n M(n) + n^{(\omega + 1)/2}), where ω\omega is the exponent of matrix multiplication.

AlgorithmComposition and modular compositionComputing f(g)f(g), f(g) mod xnf(g) \bmod x^n and f(g) mod hf(g) \bmod h 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 by nmod_poly.

The Brent–Kung algorithm#

Let m=⌊n⌋+1m = \lfloor \sqrt n \rfloor + 1 and write f=∑jfj(x) xjmf = \sum_{j} f_j(x)\, x^{jm} with blocks fjf_j of length mm. Then

f(g)=∑jfj(g) (gm)j mod h,fj(g)=∑i<mfj,i gi.f(g) = \sum_j f_j(g)\, (g^m)^j \bmod h, \qquad f_j(g) = \sum_{i < m} f_{j,i}\, g^i.

The baby steps g0,…,gm−1 mod hg^0, \ldots, g^{m-1} \bmod h form the rows of an m×nm \times n matrix AA, and all fj(g)f_j(g) together are the rows of the product C=BAC = BA, where row jj of the m×mm \times m matrix BB holds the coefficients of fjf_j. The giant steps evaluate ∑jCjHj\sum_j C_j H^j for H=gm mod hH = g^m \bmod h by Horner's rule or rectangular splitting (_nmod_poly_mod_matrix_rows_evaluate), with about n\sqrt n multiplications modulo hh. The matrix product is done by nmod_mat_mul (see Multiplying matrices over Z/nZ).

The matrix AA depends only on gg and hh. nmod_poly_precompute_matrix computes it once (it must be initialised with mm rows and nn columns), and nmod_poly_compose_mod_brent_kung_precomp_preinv then composes any ff with gg at the cost of the matrix product and the giant steps. The preinv versions take the inverse of the reversal of hh, as explained in Computing modulo a fixed polynomial.

nmod_poly_compose_mod uses Horner's rule for deg⁡h<7\deg h < 7 and when deg⁡f≥deg⁡h\deg f \ge \deg h (Brent–Kung requires deg⁡f<deg⁡h\deg f < \deg h), and the Brent–Kung algorithm otherwise.

Example: Rabin's irreducibility test#

A polynomial hh of degree nn over Fp\mathbb{F}_p is irreducible if and only if xpn≡x(modh)x^{p^n} \equiv x \pmod h and gcd⁡(xpn/q−x,h)=1\gcd(x^{p^{n/q}} - x, h) = 1 for every prime qq dividing nn [Rab1980b]. The powers xpix^{p^i} are computed by one exponentiation, xp mod hx^p \bmod h, followed by repeated composition with xpx^p, using one precomputed matrix:

Examplenmod_poly_rabin_irreducible.cchecked by make checkdoc
#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;
}
Output
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 m×mm \times m by m×nm \times n matrix product and about mm modular multiplications, against log⁡2p≈20\log_2 p \approx 20 squarings modulo hh for an exponentiation, which is where modular composition pays off for large pp [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.
Cite this page

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).