FLINT3.7.0-dev
Guide · fmpz_mod_poly.h

Getting started with fmpz_mod_poly

An fmpz_mod_poly_t is a polynomial over Z/nZ\mathbb{Z}/n\mathbb{Z} whose coefficients are fmpz integers in [0,n)[0, n). Unlike nmod_poly_t, it does not store its modulus: the modulus lives in an fmpz_mod_ctx_t (see fmpz_mod), which is created once and passed to every function, and which must outlive all polynomials used with it. The functions follow the conventions of nmod_poly (see Getting started with nmod_poly): outputs may alias inputs, underscore functions work on coefficient arrays, and the functions that divide require invertible leading coefficients.

A first program#

The following program works modulo the Mersenne prime p=2127−1p = 2^{127} - 1. It builds f=(x−3)(x−5)(x2+1)f = (x - 3)(x - 5)(x^2 + 1) and computes gcd⁡(f,xp−x)\gcd(f, x^p - x), the product of the linear factors of ff (Roots of polynomials over finite fields). Since p≡3(mod4)p \equiv 3 \pmod 4, the factor x2+1x^2 + 1 is irreducible and does not appear. The power xp mod fx^p \bmod f is computed with fmpz_mod_poly_powmod_x_fmpz_preinv, which needs the inverse of the reversal of ff as a power series; with it, every reduction modulo ff costs two multiplications.

Examplefmpz_mod_poly_first.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/fmpz_mod.h>
#include <flint/fmpz_mod_poly.h>

int main(void)
{
    fmpz_t p;
    fmpz_mod_ctx_t ctx;
    fmpz_mod_poly_t f, finv, t, h;

    fmpz_init(p);
    fmpz_ui_pow_ui(p, 2, 127);
    fmpz_sub_ui(p, p, 1);                       /* the prime 2^127 - 1 */
    fmpz_mod_ctx_init(ctx, p);

    fmpz_mod_poly_init(f, ctx);
    fmpz_mod_poly_init(finv, ctx);
    fmpz_mod_poly_init(t, ctx);
    fmpz_mod_poly_init(h, ctx);

    /* f = (x - 3)(x - 5)(x^2 + 1) */
    fmpz_mod_poly_set_coeff_ui(f, 1, 1, ctx);
    fmpz_mod_poly_set_coeff_si(f, 0, -3, ctx);
    fmpz_mod_poly_set_coeff_ui(t, 1, 1, ctx);
    fmpz_mod_poly_set_coeff_si(t, 0, -5, ctx);
    fmpz_mod_poly_mul(f, f, t, ctx);
    fmpz_mod_poly_zero(t, ctx);
    fmpz_mod_poly_set_coeff_ui(t, 2, 1, ctx);
    fmpz_mod_poly_set_coeff_ui(t, 0, 1, ctx);
    fmpz_mod_poly_mul(f, f, t, ctx);
    flint_printf("f = "); fmpz_mod_poly_print_pretty(f, "x", ctx); flint_printf("\n");

    /* finv = inverse of the reversal of f modulo x^len(f) */
    fmpz_mod_poly_reverse(finv, f, f->length, ctx);
    fmpz_mod_poly_inv_series(finv, finv, f->length, ctx);

    /* h = x^p mod f, then gcd(f, h - x) = product of the linear factors */
    fmpz_mod_poly_powmod_x_fmpz_preinv(h, p, f, finv, ctx);
    fmpz_mod_poly_zero(t, ctx);
    fmpz_mod_poly_set_coeff_ui(t, 1, 1, ctx);
    fmpz_mod_poly_sub(h, h, t, ctx);
    fmpz_mod_poly_gcd(h, f, h, ctx);
    flint_printf("gcd(f, x^p - x) = "); fmpz_mod_poly_print_pretty(h, "x", ctx); flint_printf("\n");

    fmpz_mod_poly_clear(f, ctx);
    fmpz_mod_poly_clear(finv, ctx);
    fmpz_mod_poly_clear(t, ctx);
    fmpz_mod_poly_clear(h, ctx);
    fmpz_mod_ctx_clear(ctx);
    fmpz_clear(p);
    return 0;
}
Output
f = x^4+170141183460469231731687303715884105719*x^3+16*x^2+170141183460469231731687303715884105719*x+15
gcd(f, x^p - x) = x^2+170141183460469231731687303715884105719*x+15

Coefficients are printed as their representatives in [0,p)[0, p): the coefficient of xx in the gcd is p−8p - 8, so the gcd is x2−8x+15=(x−3)(x−5)x^2 - 8x + 15 = (x - 3)(x - 5).

Arithmetic modulo a fixed polynomial#

Many functions come in a plain version and a _preinv version that takes the inverse finv\mathit{finv} of the reversal rev⁡(f)=xdeg⁡ff(1/x)\operatorname{rev}(f) = x^{\deg f} f(1/x) modulo xdeg⁡f+1x^{\deg f + 1}: fmpz_mod_poly_mulmod_preinv, the powmod functions, the division functions _newton_n_preinv and the Brent–Kung modular composition. Computing finv\mathit{finv} costs about as much as two multiplications (Newton iteration for power series and division), so it pays off as soon as more than a few reductions modulo the same ff are needed.

Composite moduli#

Most functions work for any modulus, but those that invert elements (division, GCDs, inverses) fail when they meet a non-invertible leading coefficient: the plain versions then raise an exception. The versions with suffix _f instead return a nontrivial factor of nn in their first argument, which is set to 1 when the computation succeeds. This is useful when the factorisation of nn is unknown, as in elliptic curve methods or in algorithms that work "as if nn were prime" and either succeed or split nn.

Here n=1000003⋅998244353n = 1000003 \cdot 998244353, and the Euclidean algorithm for gcd⁡((x+1)(x+2),(x+1)(1000003x+3))\gcd((x + 1)(x + 2), (x + 1)(1000003x + 3)) needs to invert the leading coefficient 1000003 of the second polynomial:

Examplefmpz_mod_poly_gcd_f.cchecked by make checkdoc
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/fmpz_mod.h>
#include <flint/fmpz_mod_poly.h>

int main(void)
{
    fmpz_t p, q, n, f;
    fmpz_mod_ctx_t ctx;
    fmpz_mod_poly_t a, b, g;

    fmpz_init(p); fmpz_init(q); fmpz_init(n); fmpz_init(f);
    fmpz_set_ui(p, 1000003);
    fmpz_set_ui(q, 998244353);
    fmpz_mul(n, p, q);                          /* n = p q, assumed unknown */
    fmpz_mod_ctx_init(ctx, n);

    fmpz_mod_poly_init(a, ctx);
    fmpz_mod_poly_init(b, ctx);
    fmpz_mod_poly_init(g, ctx);

    /* a = (x + 1)(x + 2), b = (x + 1)(p x + 3) */
    fmpz_mod_poly_set_coeff_ui(a, 2, 1, ctx);
    fmpz_mod_poly_set_coeff_ui(a, 1, 3, ctx);
    fmpz_mod_poly_set_coeff_ui(a, 0, 2, ctx);
    fmpz_mod_poly_set_coeff_fmpz(b, 2, p, ctx);
    fmpz_add_ui(f, p, 3);
    fmpz_mod_poly_set_coeff_fmpz(b, 1, f, ctx);
    fmpz_mod_poly_set_coeff_ui(b, 0, 3, ctx);

    fmpz_mod_poly_gcd_f(f, g, a, b, ctx);
    if (fmpz_is_one(f))
    {
        flint_printf("gcd = "); fmpz_mod_poly_print_pretty(g, "x", ctx); flint_printf("\n");
    }
    else
    {
        flint_printf("factor of n found: "); fmpz_print(f); flint_printf("\n");
    }

    fmpz_mod_poly_clear(a, ctx);
    fmpz_mod_poly_clear(b, ctx);
    fmpz_mod_poly_clear(g, ctx);
    fmpz_mod_ctx_clear(ctx);
    fmpz_clear(p); fmpz_clear(q); fmpz_clear(n); fmpz_clear(f);
    return 0;
}
Output
factor of n found: 1000003

Choosing a polynomial type#

For moduli n<2FLINT_BITSn < 2^{\mathtt{FLINT\_BITS}}, use nmod_poly, whose coefficients are words and whose multiplication uses dedicated FFTs, which makes it considerably faster. For larger moduli fmpz_mod_poly is the standard choice. The generic gr_poly type over an fmpz_mod or mpn_mod context provides the same algorithms; the mpn_mod context, which stores coefficients as limb arrays of fixed size, is designed to be faster than fmpz coefficients for moduli of a few words.

Cite this page

The FLINT team, “Getting started with fmpz_mod_poly (fmpz_mod_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fmpz_mod_poly/getting-started.html (generated 2026-11-02).