Factoring polynomials over a prime field
#include <flint/nmod_poly_factor.h>ulong nmod_poly_factor(nmod_poly_factor_t result, const nmod_poly_t input)sourceSets result to the factorisation of input into monic irreducible polynomials over and returns the leading coefficient of input, so that input equals the leading coefficient times the product of the factors raised to their exponents. A constant input gives the empty factorisation and returns the constant (0 for the zero polynomial).
This is gr_poly_factor_finite_field over the nmod context. If input is a polynomial in for some , it is first deflated, and the factors of the deflated polynomial are replaced by the factors of . The squarefree factors of the (deflated) input of degree are then split by the Cantor–Zassenhaus algorithm if (for example for and for ), and by the Kaltofen–Shoup algorithm otherwise, which means always for primes of 40 bits or more. See Factoring polynomials over a prime field.
ulong nmod_poly_factor_with_cantor_zassenhaus(nmod_poly_factor_t result, const nmod_poly_t input)sourceulong nmod_poly_factor_with_berlekamp(nmod_poly_factor_t result, const nmod_poly_t input)sourceulong nmod_poly_factor_with_kaltofen_shoup(nmod_poly_factor_t result, const nmod_poly_t input)sourceLike nmod_poly_factor, but split the squarefree factors with the Cantor–Zassenhaus algorithm, Berlekamp's algorithm or the Kaltofen–Shoup algorithm respectively. Deflation is still applied.
void nmod_poly_factor_cantor_zassenhaus(nmod_poly_factor_t res, const nmod_poly_t f)sourcevoid nmod_poly_factor_berlekamp(nmod_poly_factor_t res, const nmod_poly_t f)sourcevoid nmod_poly_factor_kaltofen_shoup(nmod_poly_factor_t res, const nmod_poly_t f)sourceAppend the factorisation of f into monic irreducible polynomials, with exponents, to res, using the Cantor–Zassenhaus algorithm, Berlekamp's algorithm or the Kaltofen–Shoup algorithm (with deflation, as in nmod_poly_factor). The leading coefficient of f is discarded; nothing is appended for a constant f.
nmod_poly_factor computes the complete factorisation
of a polynomial into distinct monic irreducible polynomials with multiplicities : the factors and exponents are stored in an nmod_poly_factor_t, and the leading coefficient is the return value. The modulus must be prime. The _with_ variants force one algorithm and are otherwise identical; the three void functions append to an existing factorisation instead of overwriting it and discard , which is convenient when factoring the parts of a product one by one.
gr_poly chooses between them.
Algorithms#
All functions are wrappers of the generic implementation in gr_poly over the nmod context, which proceeds in four steps:
- Deflation. If with , is factored first, and then each separately.
- Squarefree factorisation, by Yun-type gcd iterations and -th roots (Squarefree factorisation), which costs a few gcds.
- Distinct-degree factorisation of each squarefree part, which separates the irreducible factors by degree.
- Equal-degree factorisation, a randomised splitting of each product of irreducible factors of the same degree.
The algorithms differ in steps 3 and 4. The Cantor–Zassenhaus algorithm finds the factors of degree as for , computing each by an exponentiation by ; its cost is about multiplications modulo for an irreducible of degree , so it suits small and small degrees. The Kaltofen–Shoup algorithm replaces the exponentiations by modular compositions in a baby-step giant-step scheme, about compositions and multiplications modulo , independent of . Berlekamp's algorithm computes the kernel of an matrix over instead; at (or ) it is slower for large , and is mainly useful for checking the others.
nmod_poly_factor chooses Cantor–Zassenhaus when the degree of the (deflated) input satisfies and Kaltofen–Shoup otherwise:
| 2 | 3 | 7 | 31 | ||||
|---|---|---|---|---|---|---|---|
| Cantor–Zassenhaus for | 125 | 69 | 33 | 16 | 9 | 4 | 2 |
For large inputs the work is dominated by the distinct-degree factorisation, which uses several threads when they are available (flint_set_num_threads); see The complete algorithms and FLINT's choice.
The factors are returned in no particular order: grouped by squarefree part, and within a part in the order in which the distinct-degree and equal-degree steps find them. The random choices use a random state with a fixed seed, so repeated calls give the same output.
Examples#
A product of known factors modulo 7, one of them with multiplicity , is recovered together with its leading coefficient. The squarefree factorisation alone separates the multiplicities:
#include <flint/flint.h>
#include <flint/nmod_poly.h>
#include <flint/nmod_poly_factor.h>
int main(void)
{
nmod_poly_t f, g;
nmod_poly_factor_t fac;
ulong c;
nmod_poly_init(f, 7);
nmod_poly_init(g, 7);
nmod_poly_factor_init(fac);
/* f = 3 x (x^2 + 1)^2 (x + 1)^7 (x^3 + x + 1) */
nmod_poly_set_str(f, "3 7 1 0 1");
nmod_poly_pow(f, f, 2);
nmod_poly_set_str(g, "2 7 1 1");
nmod_poly_pow(g, g, 7);
nmod_poly_mul(f, f, g);
nmod_poly_set_str(g, "4 7 1 1 0 1");
nmod_poly_mul(f, f, g);
nmod_poly_shift_left(f, f, 1);
nmod_poly_scalar_mul_nmod(f, f, 3);
c = nmod_poly_factor(fac, f);
flint_printf("leading coefficient %wu\n", c);
nmod_poly_factor_print_pretty(fac, "x");
nmod_poly_factor_squarefree(fac, f);
flint_printf("squarefree:\n");
nmod_poly_factor_print_pretty(fac, "x");
nmod_poly_clear(f);
nmod_poly_clear(g);
nmod_poly_factor_clear(fac);
return 0;
}leading coefficient 3 x ^ 1 x^3+x+1 ^ 1 x^2+1 ^ 2 x+1 ^ 7 squarefree: x^4+x^2+x ^ 1 x^2+1 ^ 2 x+1 ^ 7
The factor has a multiplicity divisible by ; the squarefree factorisation finds it by taking a -th root.
By Theorem 1 of Factoring polynomials over finite fields, is the product of all monic irreducible polynomials over whose degree divides 6. Factoring it and counting the factors by degree gives the numbers of monic irreducible polynomials of degree ; all three algorithms agree:
#include <flint/flint.h>
#include <flint/nmod_poly.h>
#include <flint/nmod_poly_factor.h>
static void count(const nmod_poly_factor_t fac)
{
slong i, num[7] = {0};
for (i = 0; i < fac->num; i++)
num[nmod_poly_degree(fac->p + i)]++;
flint_printf("%wd %wd %wd %wd %wd %wd\n",
num[1], num[2], num[3], num[4], num[5], num[6]);
}
int main(void)
{
nmod_poly_t f;
nmod_poly_factor_t fac;
nmod_poly_init(f, 3);
nmod_poly_factor_init(fac);
nmod_poly_set_coeff_ui(f, 729, 1);
nmod_poly_set_coeff_ui(f, 1, 2); /* x^729 - x */
nmod_poly_factor(fac, f);
count(fac);
nmod_poly_factor_with_cantor_zassenhaus(fac, f);
count(fac);
nmod_poly_factor_with_berlekamp(fac, f);
count(fac);
nmod_poly_clear(f);
nmod_poly_factor_clear(fac);
return 0;
}3 3 8 0 0 116 3 3 8 0 0 116 3 3 8 0 0 116
The FLINT team, “Factoring polynomials over a prime field (nmod_poly_factor.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly_factor/factor.html (generated 2026-11-02).
@misc{flint-doc-nmod-poly-factor-factor,
author = {{The FLINT team}},
title = {{Factoring polynomials over a prime field (nmod\_poly\_factor.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/nmod_poly_factor/factor.html},
note = {Generated 2026-11-02}
}