Greatest common divisors
#include <flint/nmod_poly.h>void nmod_poly_gcd(nmod_poly_t G, const nmod_poly_t A, const nmod_poly_t B)sourceSet G to a greatest common divisor of A and B. The underscore version requires \mathit{lenA} \ge \mathit{lenB} \ge 1, writes at most \mathit{lenB} coefficients, returns the length of G, does not make G monic and does not allow aliasing.
_nmod_poly_gcd uses the half-GCD algorithm if \mathit{lenB} is at least nmod_poly_gcd_hgcd_cutoff(mod), and the Euclidean algorithm otherwise, with Montgomery arithmetic when NMOD_POLY_GCD_EUCLIDEAN_USE_REDC_FAST holds.
slong _nmod_poly_hgcd(nn_ptr * M, slong * lenM, nn_ptr A, slong * lenA, nn_ptr B, slong * lenB, nn_srcptr a, slong lena, nn_srcptr b, slong lenb, nmod_t mod)sourceslong nmod_poly_hgcd(nmod_poly_t m11, nmod_poly_t m12, nmod_poly_t m21, nmod_poly_t m22, nmod_poly_t A, nmod_poly_t B, const nmod_poly_t a, const nmod_poly_t b)sourceThe half-GCD step. For \deg a > \deg b, compute consecutive remainders A, B of the Euclidean remainder sequence of a, b with \deg A \ge \deg(a)/2 > \deg B and the matrix
the product of the matrices \left(\begin{smallmatrix} q_i & 1 \\ 1 & 0 \end{smallmatrix}\right) for the quotients q_i of these steps. Return \det M = \pm 1.
nmod_poly_hgcd throws FLINT_ERROR unless \deg a > \deg b; for b = 0 it returns the identity matrix. The underscore version requires \mathit{lena} > \mathit{lenb} \ge 1; A and B and the four arrays M[0], ..., M[3] (for m_{11}, m_{12}, m_{21}, m_{22}) need space for lena coefficients, and the actual lengths are written to lenA, lenB and lenM. It allows A == a and B == b. Below nmod_poly_hgcd_iter_recursive_cutoff(mod) the recursion is replaced by Euclidean steps. See Half-GCD.
slong _nmod_poly_xgcd(nn_ptr G, nn_ptr S, nn_ptr T, nn_srcptr A, slong lenA, nn_srcptr B, slong lenB, nmod_t mod)sourcevoid nmod_poly_xgcd(nmod_poly_t G, nmod_poly_t S, nmod_poly_t T, const nmod_poly_t A, const nmod_poly_t B)sourceCompute the GCD G of A and B and cofactors S, T with SA + TB = G. The non-underscore version makes G monic; for B \ne 0 the cofactors satisfy \deg S < \max(\deg B - \deg G, 1) and \deg T < \max(\deg A - \deg G, 1), and for B = 0 it returns S = \operatorname{lc}(A)^{-1}, T = 0. G, S and T must be distinct objects.
The underscore version requires \mathit{lenA} \ge \mathit{lenB} \ge 1 and space for \mathit{lenB} coefficients in G, \max(\mathit{lenB} - 1, 2) in S and \max(\mathit{lenA} - 1, 2) in T, all of which may be written; it returns the length of G, does not make G monic and does not allow aliasing. It uses the half-GCD algorithm if \mathit{lenB} \ge nmod_poly_xgcd_hgcd_cutoff(mod) and the Euclidean algorithm otherwise.
slong _nmod_poly_gcdinv(ulong * G, ulong * S, const ulong * A, slong lenA, const ulong * B, slong lenB, const nmod_t mod)sourcevoid nmod_poly_gcdinv(nmod_poly_t G, nmod_poly_t S, const nmod_poly_t A, const nmod_poly_t B)sourceCompute G = \gcd(A, B) and S with G \equiv SA \pmod B and \deg S < \deg B - \deg G, without the cofactor of B. If \gcd(A, B) = 1, then S = A^{-1} \bmod B. The non-underscore version requires \mathit{lenB} \ge 2 (otherwise it throws FLINT_ERROR), reduces A modulo B first, makes G monic and returns G = S = 0 if B divides A. The underscore version requires 1 \le \mathit{lenA} < \mathit{lenB}, writes at most lenA coefficients to G and \mathit{lenB} - 1 to S, returns the length of G and does not make G monic.
Over a field \mathbb{F}_p, every pair of polynomials has a greatest common divisor, unique up to a constant factor, and the non-underscore functions return the monic one. nmod_poly_gcd computes G = \gcd(A, B), nmod_poly_xgcd also the cofactors S, T with SA + TB = G, and nmod_poly_gcdinv only the cofactor S of A, which is what is needed for inverses modulo B. The modulus must be prime; over \mathbb{Z}/n\mathbb{Z} with composite n the Euclidean algorithm can meet a non-invertible leading coefficient, which throws an exception.
nmod_poly.
Algorithms#
The Euclidean algorithm replaces (A, B) by (B, A \bmod B) until the remainder is zero. In the typical case each step lowers the degree by one and costs O(\deg B) operations, for O(\deg A \deg B) in total. FLINT's implementation calls _nmod_poly_rem, which has a dedicated routine for quotients of length 2, and for moduli with at least half the bits of a word (but at most \mathtt{FLINT\_BITS} - 2 bits) and \deg B \ge 31 it runs in Montgomery arithmetic (_nmod_poly_gcd_euclidean_redc_fast), where the long chains of dependent multiplications are cheaper.
The half-GCD algorithm [ThullYap1990] uses the fact that the quotients of the first half of the Euclidean algorithm depend only on the top half of the coefficients. It computes them recursively from the top coefficients and applies them to the full polynomials as a 2 \times 2 matrix of polynomials, so that the whole remainder sequence costs O(M(n) \log n) operations. nmod_poly_hgcd exposes one such step: it returns the matrix M and the two consecutive remainders A, B of the sequence that straddle half the degree of the input.
nmod_poly_gcd uses the half-GCD algorithm for \mathit{lenB} \ge nmod_poly_gcd_hgcd_cutoff(mod), between 470 and 2199 depending on the bit size of n, and nmod_poly_xgcd for \mathit{lenB} \ge nmod_poly_xgcd_hgcd_cutoff(mod), between 72 and 388; the cofactors make the Euclidean algorithm more expensive, so the extended version switches earlier.
Examples#
Two random polynomials of degree 2500 modulo a 50-bit prime, multiplied by a common factor of degree 500; both algorithms find the factor, made monic, and the cofactors of nmod_poly_xgcd satisfy Bézout's identity:
#include <flint/flint.h>
#include <flint/ulong_extras.h>
#include <flint/nmod_poly.h>
int main(void)
{
flint_rand_t state;
nmod_poly_t a, b, c, g, h, s, t, u;
ulong p = n_nextprime(UWORD(1) << 49, 1);
flint_rand_init(state);
nmod_poly_init(a, p); nmod_poly_init(b, p); nmod_poly_init(c, p);
nmod_poly_init(g, p); nmod_poly_init(h, p);
nmod_poly_init(s, p); nmod_poly_init(t, p); nmod_poly_init(u, p);
nmod_poly_rand_monic(c, state, 501);
nmod_poly_rand(a, state, 2501);
nmod_poly_rand(b, state, 2501);
nmod_poly_mul(a, a, c);
nmod_poly_mul(b, b, c);
nmod_poly_gcd(g, a, b); /* half-GCD at this length */
nmod_poly_gcd_euclidean(h, a, b);
flint_printf("deg gcd = %wd\n", nmod_poly_degree(g));
flint_printf("hgcd == euclidean: %d\n", nmod_poly_equal(g, h));
flint_printf("gcd == c: %d\n", nmod_poly_equal(g, c));
nmod_poly_xgcd(h, s, t, a, b);
nmod_poly_mul(s, s, a);
nmod_poly_mul(t, t, b);
nmod_poly_add(u, s, t);
flint_printf("s a + t b == g: %d\n", nmod_poly_equal(u, h));
nmod_poly_clear(a); nmod_poly_clear(b); nmod_poly_clear(c);
nmod_poly_clear(g); nmod_poly_clear(h);
nmod_poly_clear(s); nmod_poly_clear(t); nmod_poly_clear(u);
flint_rand_clear(state);
return 0;
}deg gcd = 500 hgcd == euclidean: 1 gcd == c: 1 s a + t b == g: 1
The common factor is found exactly because the random cofactors are coprime, which happens with probability 1 - 1/p.
nmod_poly_gcdinv returns the inverse of A modulo B together with the GCD, so that one call both tests and inverts. Modulo 7, x^2 + 1 is invertible modulo x^3 + x + 1 but not modulo x^3 + x^2 + x + 1 = (x + 1)(x^2 + 1):
#include <flint/flint.h>
#include <flint/nmod_poly.h>
int main(void)
{
nmod_poly_t a, b, g, s, t;
nmod_poly_init(a, 7);
nmod_poly_init(b, 7);
nmod_poly_init(g, 7);
nmod_poly_init(s, 7);
nmod_poly_init(t, 7);
nmod_poly_set_str(a, "3 7 1 0 1"); /* x^2 + 1 */
nmod_poly_set_str(b, "4 7 1 1 0 1"); /* x^3 + x + 1 */
nmod_poly_gcdinv(g, s, a, b);
flint_printf("g = "); nmod_poly_print_pretty(g, "x");
flint_printf(", s = "); nmod_poly_print_pretty(s, "x");
nmod_poly_mulmod(t, s, a, b);
flint_printf(", s a mod b = "); nmod_poly_print_pretty(t, "x");
flint_printf("\n");
nmod_poly_set_str(b, "4 7 1 1 1 1"); /* (x + 1)(x^2 + 1) */
nmod_poly_gcdinv(g, s, a, b);
flint_printf("g = "); nmod_poly_print_pretty(g, "x");
flint_printf(", s = "); nmod_poly_print_pretty(s, "x");
flint_printf("\n");
nmod_poly_clear(a);
nmod_poly_clear(b);
nmod_poly_clear(g);
nmod_poly_clear(s);
nmod_poly_clear(t);
return 0;
}g = 1, s = 6*x, s a mod b = 1 g = x^2+1, s = 1
Guides#
References#
- [ThullYap1990] Thull, K. and Yap, C. : A Unified Approach to HGCD Algorithms for Polynomials and Integers, (1990)
The FLINT team, “Greatest common divisors (nmod_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_poly/gcd.html (generated 2026-10-20).
@misc{flint-doc-nmod-poly-gcd,
author = {{The FLINT team}},
title = {{Greatest common divisors (nmod\_poly.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/nmod_poly/gcd.html},
note = {Generated 2026-10-20}
}