Roots modulo a prime and modulo composite numbers
#include <flint/nmod_poly_factor.h>void nmod_poly_roots(nmod_poly_factor_t r, const nmod_poly_t f, int with_multiplicity)sourceSets r to the linear factors for the distinct roots of the nonzero polynomial f. If with_multiplicity is nonzero, the exponent of is the multiplicity of as a root of f, otherwise it is 1. The modulus must be prime. A zero f causes an exception.
This is gr_poly_roots_finite_field: squarefree factorisation, then for each squarefree part the split into the roots that are squares and those that are non-squares, followed by equal-degree factorisation with . See Roots of polynomials over finite fields and Roots modulo a prime and modulo composite numbers.
int nmod_poly_roots_factored(nmod_poly_factor_t r, const nmod_poly_t f, int with_multiplicity, const n_factor_t * n)sourceSets r to the linear factors for the distinct roots of the nonzero polynomial f, where n holds the prime factorisation of the modulus of f (not checked). If with_multiplicity is nonzero, the exponent of is the largest such that divides in , otherwise it is
- Returns 1 on success and 0 if the roots were not computed because there are too many of them (more than about , or on 32-bit systems). A zero
fcauses an exception.
The roots modulo each prime are found with nmod_poly_roots (or are all of if ), lifted to roots modulo the full prime power by Hensel lifting, where each root modulo can lift to several roots modulo or to none, and combined by the Chinese remainder theorem. If some prime power gives no root, the function returns 1 with no roots, even if another prime power has too many. See Roots modulo prime powers.
nmod_poly_roots finds all roots of a nonzero polynomial in the prime field and returns them as the linear factors of an nmod_poly_factor_t, with the multiplicity of each root as its exponent if requested. nmod_poly_roots_factored does the same over for a composite modulus whose prime factorisation is known. Over the number of roots is not bounded by the degree, and the multiplicity of is defined as the largest with in .
To check whether a polynomial splits into distinct linear factors with nonzero roots, and find them if it does, the specialised nmod_poly_find_distinct_nonzero_roots in nmod_poly is faster, since it skips the squarefree factorisation (see Finding roots that are expected to be distinct).
Algorithms#
Modulo a prime, nmod_poly_roots computes the squarefree factorisation of , then for each squarefree part the two gcds and , whose product is the product of the distinct nonzero linear factors of , and splits them with random shifts . The cost is dominated by the exponentiations, in expectation for degree ; the irreducible factors of degree at least 2 are never separated from each other. The root 0 is split off directly.
Modulo , nmod_poly_roots_factored finds the roots modulo each , lifts every one of them to all roots modulo that reduce to it, using Lemma 3 of Roots of polynomials over finite fields with doubling precision, and combines the roots for different primes with the Chinese remainder theorem. A root that is simple modulo lifts uniquely, while a multiple root can lift to many roots or to none. The function returns 0, without roots, if a list of roots would exceed entries on 64-bit systems.
Example#
Modulo 7, has the roots 1 and 2 (the factor is irreducible since ). Modulo , has roots, and has the roots with , that is, the multiples of :
#include <stdlib.h>
#include <flint/flint.h>
#include <flint/ulong_extras.h>
#include <flint/nmod.h>
#include <flint/nmod_poly.h>
#include <flint/nmod_poly_factor.h>
static int cmp(const void * a, const void * b)
{
ulong x = *(const ulong *) a, y = *(const ulong *) b;
return (x > y) - (x < y);
}
/* print the roots a (from the factors x - a) in increasing order,
with the multiplicity if it is greater than 1 */
static void print_roots(const nmod_poly_factor_t r, ulong n)
{
ulong a[64][2];
slong i;
for (i = 0; i < r->num; i++)
{
a[i][0] = (n - r->p[i].coeffs[0]) % n;
a[i][1] = r->exp[i];
}
qsort(a, r->num, sizeof(a[0]), cmp);
flint_printf("%wd roots mod %wu:", r->num, n);
for (i = 0; i < r->num; i++)
{
flint_printf(" %wu", a[i][0]);
if (a[i][1] > 1)
flint_printf("^%wu", a[i][1]);
}
flint_printf("\n");
}
int main(void)
{
nmod_poly_t f, g;
nmod_poly_factor_t r;
n_factor_t fac;
slong i;
nmod_poly_init(f, 7);
nmod_poly_init(g, 7);
nmod_poly_factor_init(r);
nmod_poly_set_str(f, "2 7 6 1"); /* x - 1 */
nmod_poly_pow(f, f, 3);
nmod_poly_set_str(g, "2 7 5 1"); /* x - 2 */
nmod_poly_mul(f, f, g);
nmod_poly_set_str(g, "3 7 1 0 1"); /* x^2 + 1 */
nmod_poly_mul(f, f, g);
nmod_poly_roots(r, f, 1);
for (i = 0; i < r->num; i++)
flint_printf("root %wu with multiplicity %wd\n",
nmod_neg(r->p[i].coeffs[0], f->mod), r->exp[i]);
nmod_poly_clear(f);
nmod_poly_init(f, 120);
n_factor_init(&fac);
n_factor(&fac, 120, 1);
nmod_poly_set_str(f, "3 120 119 0 1"); /* x^2 - 1 */
if (nmod_poly_roots_factored(r, f, 1, &fac))
print_roots(r, 120);
nmod_poly_set_str(f, "3 120 0 0 1"); /* x^2 */
if (nmod_poly_roots_factored(r, f, 1, &fac))
print_roots(r, 120);
nmod_poly_clear(f);
nmod_poly_clear(g);
nmod_poly_factor_clear(r);
return 0;
}root 2 with multiplicity 1 root 1 with multiplicity 3 16 roots mod 120: 1 11 19 29 31 41 49 59 61 71 79 89 91 101 109 119 2 roots mod 120: 0^2 60^2
The example reads only the constant coefficients of the factors, which have the modulus of f. Both roots of have multiplicity 2, since .
The FLINT team, “Roots modulo a prime and modulo composite numbers (nmod_poly_factor.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly_factor/roots.html (generated 2026-11-02).
@misc{flint-doc-nmod-poly-factor-roots,
author = {{The FLINT team}},
title = {{Roots modulo a prime and modulo composite numbers (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/roots.html},
note = {Generated 2026-11-02}
}