FLINT3.7.0-dev

Exponentiation modulo a polynomial

Synopsis#include <flint/nmod_poly.h>
void _nmod_poly_powmod_ui_binexp(nn_ptr res, nn_srcptr poly, ulong e, nn_srcptr f, slong lenf, nmod_t mod)source
void nmod_poly_powmod_ui_binexp(nmod_poly_t res, const nmod_poly_t poly, ulong e, const nmod_poly_t f)source
void _nmod_poly_powmod_fmpz_binexp(nn_ptr res, nn_srcptr poly, fmpz_t e, nn_srcptr f, slong lenf, nmod_t mod)source
void nmod_poly_powmod_fmpz_binexp(nmod_poly_t res, const nmod_poly_t poly, fmpz_t e, const nmod_poly_t f)source

Set res to \mathit{poly}^e \bmod f by left-to-right binary exponentiation, reducing each product with _nmod_poly_divrem. The non-underscore versions accept any poly (reducing it first) and e \ge 0, and throw FLINT_ERROR for f = 0. The underscore versions require \mathit{lenf} \ge 2, poly reduced and zero-padded to length \mathit{lenf} - 1, and e \ge 1; they write \mathit{lenf} - 1 coefficients and do not allow aliasing.

void _nmod_poly_powmod_ui_binexp_preinv(nn_ptr res, nn_srcptr poly, ulong e, nn_srcptr f, slong lenf, nn_srcptr finv, slong lenfinv, nmod_t mod)source
void nmod_poly_powmod_ui_binexp_preinv(nmod_poly_t res, const nmod_poly_t poly, ulong e, const nmod_poly_t f, const nmod_poly_t finv)source
void _nmod_poly_powmod_fmpz_binexp_preinv(nn_ptr res, nn_srcptr poly, fmpz_t e, nn_srcptr f, slong lenf, nn_srcptr finv, slong lenfinv, nmod_t mod)source
void nmod_poly_powmod_fmpz_binexp_preinv(nmod_poly_t res, const nmod_poly_t poly, fmpz_t e, const nmod_poly_t f, const nmod_poly_t finv)source

The same with a precomputed finv, reducing with _nmod_poly_divrem_newton_n_preinv. These are the functions to use for large f or many exponentiations modulo the same f.

void _nmod_poly_powmod_x_ui_preinv(nn_ptr res, ulong e, nn_srcptr f, slong lenf, nn_srcptr finv, slong lenfinv, nmod_t mod)source
void nmod_poly_powmod_x_ui_preinv(nmod_poly_t res, ulong e, const nmod_poly_t f, const nmod_poly_t finv)source
void _nmod_poly_powmod_x_fmpz_preinv(nn_ptr res, fmpz_t e, nn_srcptr f, slong lenf, nn_srcptr finv, slong lenfinv, nmod_t mod)source
void nmod_poly_powmod_x_fmpz_preinv(nmod_poly_t res, fmpz_t e, const nmod_poly_t f, const nmod_poly_t finv)source

Set res to x^e \bmod f, given finv. They use sliding-window exponentiation in which a multiplication by x^w is a shift followed by a reduction, so each window costs one reduction instead of a full multiplication. The underscore versions require \mathit{lenf} \ge 3 and e \ge 1 and write \mathit{lenf} - 1 coefficients; the non-underscore versions accept e \ge 0 and any nonzero f.

Powers a^e \bmod f with huge exponents are the basic operation of computations in finite fields \mathbb{F}_p[x]/(f): root finding and equal-degree factorisation use (x + \delta)^{(p-1)/2} \bmod f, distinct-degree factorisation and irreducibility tests use x^{p^i} \bmod f, and primality tests for polynomials use a^{p^n - 1}. These functions compute such powers by binary exponentiation, with \lfloor \log_2 e \rfloor squarings and at most as many multiplications, each followed by a reduction modulo f, for O(M(\deg f) \log e) operations in total.

Choosing a function#

  • The functions without preinv reduce with _nmod_poly_divrem, which for large \deg f computes an inverse of the reversed f in every reduction. For anything but small f, use the preinv versions with finv computed once, as explained in Computing modulo a fixed polynomial.
  • For the base x, the powmod_x functions are faster: they use a sliding window of about \log_2 \deg f bits, and multiplying by x^w for a window w is a shift followed by one reduction instead of a product. Requires \deg f \ge 2 for the underscore versions.
  • The fmpz versions accept exponents of any size, such as p^n.
  • The non-underscore functions reduce poly modulo f first, accept e = 0 (giving 1) and allow aliasing. The underscore functions require \deg f \ge 1, poly reduced and zero-padded to length \deg f, e \ge 1, and no aliasing.

For many powers of the same element, nmod_poly_powers_mod_bsgs computes a^0, \ldots, a^{k-1} in parallel, and for the iterates x^{p^i} of the Frobenius map, composition with x^p is faster than repeated exponentiation for large p; see Modular composition.

Example: counting roots#

The distinct roots of f in \mathbb{F}_p are the roots of \gcd(x^p - x, f), since x^p - x = \prod_{c \in \mathbb{F}_p} (x - c). One exponentiation of x modulo f therefore counts them, and the factor \gcd(x^p - x, f) splits into linear factors, whose roots nmod_poly_find_distinct_nonzero_roots finds:

Examplenmod_poly_count_roots.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/nmod_poly.h>

int main(void)
{
    nmod_poly_t f, finv, t, g, x;
    ulong p = 1000003, roots[10];
    slong i, k;

    nmod_poly_init(f, p);
    nmod_poly_init(finv, p);
    nmod_poly_init(t, p);
    nmod_poly_init(g, p);
    nmod_poly_init(x, p);

    /* f = (x - 10)(x - 20)(x - 30)^2 (x^50 + x + 7) */
    nmod_poly_set_str(f, "51 1000003  7 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1");
    for (i = 10; i <= 30; i += 10)
    {
        nmod_poly_set_coeff_ui(t, 1, 1);
        nmod_poly_set_coeff_ui(t, 0, p - i);
        nmod_poly_mul(f, f, t);
    }
    nmod_poly_mul(f, f, t);                       /* second factor x - 30 */

    nmod_poly_reverse(finv, f, f->length);
    nmod_poly_inv_series(finv, finv, f->length);

    nmod_poly_powmod_x_ui_preinv(t, p, f, finv);  /* x^p mod f */
    nmod_poly_set_coeff_ui(x, 1, 1);
    nmod_poly_sub(t, t, x);
    nmod_poly_gcd(g, t, f);
    flint_printf("deg f = %wd, distinct roots: %wd\n",
                 nmod_poly_degree(f), nmod_poly_degree(g));

    if (nmod_poly_find_distinct_nonzero_roots(roots, g))
    {
        k = nmod_poly_degree(g);
        flint_printf("roots:");
        for (i = 0; i < k; i++)
            flint_printf(" %wu", roots[i]);
        flint_printf("\n");
    }

    /* the same power by the generic function */
    nmod_poly_powmod_ui_binexp(g, x, p, f);
    nmod_poly_add(t, t, x);
    flint_printf("powmod agrees: %d\n", nmod_poly_equal(g, t));

    nmod_poly_clear(f);
    nmod_poly_clear(finv);
    nmod_poly_clear(t);
    nmod_poly_clear(g);
    nmod_poly_clear(x);
    return 0;
}
Output
deg f = 54, distinct roots: 3
roots: 10 20 30
powmod agrees: 1

Guides#

Cite this page

The FLINT team, “Exponentiation modulo a polynomial (nmod_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly/powmod.html (generated 2026-10-20).