Greatest common divisors
#include <flint/fmpz_poly.h>void _fmpz_poly_gcd(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourcevoid fmpz_poly_gcd(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourceSet res to the GCD of poly1 and poly2 with positive leading coefficient. Common powers of x are removed first. Polynomials of length less than 6 use the subresultant algorithm; otherwise, when the coefficients have at most 40 bits in total, the heuristic GCD is tried, and the multimodular algorithm is used if it fails or the coefficients are larger. The underscore version requires \mathit{len1} \ge \mathit{len2} > 0 and writes len2 coefficients (zero padded); res may be equal to an input.
void _fmpz_poly_gcd_modular(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourcevoid fmpz_poly_gcd_modular(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourceGCD by the multimodular algorithm: the primitive parts are reduced modulo batches of word-size primes, their monic GCDs modulo each prime are scaled to a common leading (or constant) coefficient and combined by the Chinese remainder theorem, and the candidate is certified by checking that it divides both inputs. The result is always correct. The underscore version has the conventions of _fmpz_poly_gcd.
The batches grow geometrically and the modular GCDs of a batch are computed in parallel when threads are available. A full reconstruction is attempted only when a random signed sum of the images has stabilised, so the number of primes adapts to the size of the GCD rather than to an a priori bound. The divisibility checks are also multimodular: the quotients are computed modulo the primes already used, and the product of candidate and quotient is checked by coefficient bounds or by one multiplication. The first prime is a 20-bit prime unless the coefficients are small, since it usually suffices to show that the GCD is 1; further primes are 62-bit primes, or 50-bit primes that suit fft_small for polynomials of length at least 2500. See The multimodular algorithm.
int _fmpz_poly_gcd_heuristic(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourceint fmpz_poly_gcd_heuristic(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourceAttempt to compute the GCD by the heuristic GCD algorithm GCDHEU [CGG1989], evaluating the primitive parts at a power of two, taking the GCD of the two integers and reconstructing a polynomial from it. Return 1 on success, when the reconstructed polynomial is verified to divide both inputs, and 0 on failure, in which case res is undefined. The underscore version has the conventions of _fmpz_poly_gcd.
void _fmpz_poly_gcd_subresultant(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourcevoid fmpz_poly_gcd_subresultant(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourceGCD by the subresultant pseudo-remainder sequence, Algorithm 3.3.1 of [Coh1996], which keeps the intermediate coefficients small by exact divisions. It is quadratic in the degree and fast only for short polynomials. The underscore version has the conventions of _fmpz_poly_gcd.
void _fmpz_poly_xgcd(fmpz_t r, fmpz * s, fmpz * t, const fmpz * f, slong len1, const fmpz * g, slong len2)sourcevoid fmpz_poly_xgcd(fmpz_t r, fmpz_poly_t s, fmpz_poly_t t, const fmpz_poly_t f, const fmpz_poly_t g)sourcevoid _fmpz_poly_xgcd_modular(fmpz_t r, fmpz * s, fmpz * t, const fmpz * f, slong len1, const fmpz * g, slong len2)sourcevoid fmpz_poly_xgcd_modular(fmpz_t r, fmpz_poly_t s, fmpz_poly_t t, const fmpz_poly_t f, const fmpz_poly_t g)sourceSet r to the resultant of f and g and, if r \ne 0, find s, t with \deg s < \deg g, \deg t < \deg f and sf + tg = r. If r = 0 the function returns after setting r; s and t are then not computed and their values are undefined. The functions without _modular are inline wrappers of the multimodular algorithm, which computes the extended GCD modulo word-size primes and combines the cofactors by the Chinese remainder theorem until the identity is proved by a size bound.
If both f and g are nonzero, s and t are unique and their coefficients are minors of the Sylvester matrix, so they are integers. f and g need not be primitive.
The underscore versions require \mathit{len1} \ge \mathit{len2} > 0, write \mathit{len2} coefficients to s and \mathit{len1} to t (zero padded), and do not allow aliasing. When \operatorname{len}(f) < \operatorname{len}(g), the non-underscore functions swap the inputs and correct the sign, so that r is \operatorname{res}(f, g) in all cases.
void _fmpz_poly_lcm(fmpz * res, const fmpz * poly1, slong len1, const fmpz * poly2, slong len2)sourcevoid fmpz_poly_lcm(fmpz_poly_t res, const fmpz_poly_t poly1, const fmpz_poly_t poly2)sourceSet res to the least common multiple fg / \gcd(f, g) of the two polynomials with nonnegative leading coefficient, so that fg = \pm \gcd(f, g) \operatorname{lcm}(f, g). The LCM is zero if either input is zero. The underscore version requires \mathit{len1} \ge \mathit{len2} > 0, writes \mathit{len1} + \mathit{len2} - 1 coefficients (zero padded) and does not allow aliasing.
\mathbb{Z}[x] is a unique factorisation domain, so two polynomials have a greatest common divisor, unique up to sign. fmpz_poly_gcd returns the GCD with positive leading coefficient; its content is the GCD of the contents of the inputs, and its primitive part the GCD of their primitive parts. fmpz_poly_lcm returns the least common multiple. fmpz_poly_xgcd does not compute a GCD but the resultant r and cofactors with sf + tg = r, which is the integral form of the extended Euclidean algorithm: over \mathbb{Z}, the GCD itself is in general not a combination sf + tg with integral s and t.
Algorithms#
The Euclidean algorithm cannot be run in \mathbb{Z}[x] directly, and running it in \mathbb{Q}[x] produces coefficients whose size grows exponentially in the number of steps unless contents are removed at each step. FLINT has three algorithms.
- The subresultant algorithm (
fmpz_poly_gcd_subresultant) uses pseudo-division and divides each remainder by a known factor, so that the coefficients stay of the size of minors of the Sylvester matrix. It needs O(n^2) operations on coefficients of up to O(n(b + \log n)) bits for degree n and b-bit coefficients, and is used for polynomials of length less than 6. - The heuristic GCD (
fmpz_poly_gcd_heuristic) evaluates the primitive parts at x = 2^k, computes one integer GCD with GMP, and reads the polynomial GCD off its base-2^k digits [CGG1989]. The candidate is verified by checking that it divides both inputs; if it does not, the function returns 0. It is fast for small coefficients and is tried first when the coefficients of the two inputs together have at most 40 bits. - The multimodular algorithm (
fmpz_poly_gcd_modular) computes the monic GCDs of the primitive parts modulo many word-size primes with the fast GCD ofnmod_poly, scales them to a common normalisation and combines them by the Chinese remainder theorem [Bro1971]. Its running time is roughly linear in the size of the output times the cost of one modular GCD, and it is used in all other cases.
The result is always correct: the heuristic and multimodular algorithms certify their output by divisibility. The multimodular algorithm also adapts the number of primes to the size of the GCD, so that a GCD with small coefficients, which is the common case, costs little more than one or two modular GCDs, and a GCD equal to 1 is usually proved by a single 20-bit prime.
The extended GCD fmpz_poly_xgcd (always the multimodular fmpz_poly_xgcd_modular) computes the resultant first, then the extended GCDs modulo primes that do not divide it, and stops when the reconstructed cofactors are stable and the bound for |sf + tg - r| shows that the identity holds over \mathbb{Z}.
Examples#
GCDs keep the common content: \gcd(6x^2 - 6, 4x + 4) = 2x + 2. The extended GCD of two coprime polynomials returns their resultant and the cofactors; the LCM satisfies fg = \pm\gcd(f, g) \operatorname{lcm}(f, g).
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/fmpz_poly.h>
int main(void)
{
fmpz_poly_t f, g, h, s, t;
fmpz_t r;
fmpz_poly_init(f);
fmpz_poly_init(g);
fmpz_poly_init(h);
fmpz_poly_init(s);
fmpz_poly_init(t);
fmpz_init(r);
fmpz_poly_set_str(f, "3 -6 0 6"); /* 6x^2 - 6 */
fmpz_poly_set_str(g, "2 4 4"); /* 4x + 4 */
fmpz_poly_gcd(h, f, g);
flint_printf("gcd = "); fmpz_poly_print_pretty(h, "x"); flint_printf("\n");
fmpz_poly_lcm(h, f, g);
flint_printf("lcm = "); fmpz_poly_print_pretty(h, "x"); flint_printf("\n");
fmpz_poly_set_str(f, "3 1 0 1"); /* x^2 + 1 */
fmpz_poly_set_str(g, "3 -1 2 3"); /* 3x^2 + 2x - 1 */
fmpz_poly_xgcd(r, s, t, f, g);
flint_printf("r = "); fmpz_print(r);
flint_printf(", s = "); fmpz_poly_print_pretty(s, "x");
flint_printf(", t = "); fmpz_poly_print_pretty(t, "x");
fmpz_poly_mul(s, s, f);
fmpz_poly_mul(t, t, g);
fmpz_poly_add(h, s, t);
flint_printf(", s f + t g = "); fmpz_poly_print_pretty(h, "x");
flint_printf("\n");
fmpz_poly_clear(f);
fmpz_poly_clear(g);
fmpz_poly_clear(h);
fmpz_poly_clear(s);
fmpz_poly_clear(t);
fmpz_clear(r);
return 0;
}gcd = 2*x+2 lcm = 12*x^2-12 r = 20, s = 6*x+16, t = -2*x-4, s f + t g = 20
Two random polynomials of degree 300 with 200-bit coefficients, multiplied by a common factor of degree 100, and, for the quadratic subresultant algorithm, the coprime Chebyshev polynomials T_{31} and U_{30} multiplied by the cyclotomic polynomial \Phi_{35}. All algorithms find the common factor.
#include <flint/flint.h>
#include <flint/fmpz_poly.h>
int main(void)
{
flint_rand_t state;
fmpz_poly_t a, b, c, g, h;
flint_rand_init(state);
fmpz_poly_init(a);
fmpz_poly_init(b);
fmpz_poly_init(c);
fmpz_poly_init(g);
fmpz_poly_init(h);
fmpz_poly_randtest(a, state, 301, 200);
fmpz_poly_randtest(b, state, 301, 200);
fmpz_poly_randtest(c, state, 101, 50);
fmpz_poly_set_coeff_ui(c, 100, 1); /* degree exactly 100 */
fmpz_poly_mul(a, a, c);
fmpz_poly_mul(b, b, c);
fmpz_poly_gcd(g, a, b);
flint_printf("deg gcd = %wd\n", fmpz_poly_degree(g));
flint_printf("gcd == c: %d\n", fmpz_poly_equal(g, c));
fmpz_poly_gcd_modular(h, a, b);
flint_printf("modular: %d\n", fmpz_poly_equal(g, h));
fmpz_poly_chebyshev_t(a, 31); /* coprime to U_30 */
fmpz_poly_chebyshev_u(b, 30);
fmpz_poly_cyclotomic(c, 35); /* degree 24 */
fmpz_poly_mul(a, a, c);
fmpz_poly_mul(b, b, c);
fmpz_poly_gcd(g, a, b);
flint_printf("small: gcd == c: %d\n", fmpz_poly_equal(g, c));
flint_printf("heuristic: %d", fmpz_poly_gcd_heuristic(h, a, b));
flint_printf(" %d\n", fmpz_poly_equal(g, h));
fmpz_poly_gcd_subresultant(h, a, b);
flint_printf("subresultant: %d\n", fmpz_poly_equal(g, h));
fmpz_poly_clear(a);
fmpz_poly_clear(b);
fmpz_poly_clear(c);
fmpz_poly_clear(g);
fmpz_poly_clear(h);
flint_rand_clear(state);
return 0;
}deg gcd = 100 gcd == c: 1 modular: 1 small: gcd == c: 1 heuristic: 1 1 subresultant: 1
In the first case the GCD is c, which is monic, because the random cofactors are coprime and have coprime contents, which holds with high probability.
Guides#
References#
- [CGG1989] B. W. Char, K. O. Geddes and G. H. Gonnet. GCDHEU: heuristic polynomial GCD algorithm based on integer GCD computation. Journal of Symbolic Computation 7 (1989), 31–48. https://doi.org/10.1016/S0747-7171(89)80004-6
- [Coh1996] Cohen, Henri : A course in computational algebraic number theory, Springer, 1996
- [Bro1971] W. S. Brown. On Euclid's algorithm and the computation of polynomial greatest common divisors. Journal of the ACM 18:4 (1971), 478–504. https://doi.org/10.1145/321662.321664
The FLINT team, “Greatest common divisors (fmpz_poly.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fmpz_poly/gcd.html (generated 2026-10-20).
@misc{flint-doc-fmpz-poly-gcd,
author = {{The FLINT team}},
title = {{Greatest common divisors (fmpz\_poly.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/fmpz_poly/gcd.html},
note = {Generated 2026-10-20}
}