Integer GCD
- Cost
- Euclid and Lehmer ; half-GCD for -limb operands.
The greatest common divisor of two integers is computed by the Euclidean algorithm or one of its accelerations. For -limb operands the Euclidean algorithm takes division steps of decreasing size and limb operations in total; Lehmer's algorithm keeps this complexity but replaces almost all multiprecision steps by word operations; and the half-GCD algorithm of Knuth and Schönhage reduces the cost to . The same algorithms compute continued fractions, Bézout coefficients and rational reconstructions, which are all byproducts of the sequence of quotients. The polynomial analogue is described in Half-GCD.
The Euclidean algorithm as a product of matrices#
Starting from , the Euclidean algorithm computes with until , and then . In matrix form,
The matrix has nonnegative entries and determinant , so its inverse is again an integer matrix, and its entries are the numerators and denominators of the convergents of the continued fraction . The second row of gives the cofactors with ; at these are Bézout coefficients. Each accelerated algorithm computes a product of many quotient matrices at once from leading parts of the operands, and applies it to the full operands.
The quotients that leading parts determine are characterised by a statement about continued fractions of real numbers. For a matrix as above, write .
Lemma 1. Let with integers . For every real , the continued fraction expansion of begins with . In particular, all real numbers in , for a set of reals greater than 1, have these first partial quotients.
Proof. By induction on . For , with , so . For , with , where is the product of the last matrices. By induction begins with , and since the rest of its expansion is positive; hence and the expansion of continues with that of .
Corollary 2. Let and , and write and with . Every partial quotient that is common to all real numbers in the open interval is a partial quotient of .
Proof. and , so lies in the interval.
Thus quotients can be computed from the top bits of and , as long as the computation checks that the quotients found are valid for the whole interval; by Lemma 1, it suffices to check that the transformed endpoints stay above 1.
Binary GCD#
Stein's binary algorithm [Ste1967] avoids divisions altogether: it removes common factors of 2, then repeatedly replaces the larger of two odd numbers by their difference with its factors of 2 removed. For single words this is the fastest method on current processors, since a subtraction, a count of trailing zeros and a shift are cheap. Stehlé and Zimmermann [SZ2004] gave a subquadratic "binary recursive" variant that works from the least significant bits; it is not used in FLINT.
Lehmer's algorithm#
Lehmer [Leh1938] observed that the first quotients of are almost always determined by the leading word of each operand. His algorithm runs the Euclidean algorithm on the leading words (taken from the same bit position of and ), accumulating the quotient matrix in single words, stops as soon as a quotient might differ from that of the full numbers, and then applies the matrix to and with a few multiply-and-add passes. Each pass reduces the operands by about one word at a cost of , so the total is with a small constant.
Collins and Jebelean [Jeb1993b] showed that, with the remainders and cofactors of the single-word computation, the quotients are those of if
(with in place of for the other parity of ). This is the interval condition of Corollary 2 written in terms of the cofactors. Using two words of each operand ("double-digit" Lehmer), as GMP does in mpn_hgcd2, roughly doubles the progress per pass.
The half-GCD algorithm#
The half-GCD of of limbs is a matrix such that , where are consecutive remainders with about limbs; the entries of then have about limbs too. Knuth [Knu1970] and Schönhage [Sch1971] observed that it can be computed recursively from the top halves of and .
Input: of limbs.
Output: a product of quotient matrices and with and of about limbs.
- If is below a threshold, run Lehmer's algorithm until the remainders have limbs and return.
- Let be about limbs. Compute recursively the half-GCD of the top parts (of about limbs), and set ; by Corollary 2, after removing the last one or two quotients if necessary, are consecutive remainders of of about limbs.
- Do one Euclidean step on .
- Compute recursively the half-GCD of the top parts of the result, of about limbs, and apply it as in step 2.
- Return , with the matrix of step 3, and the remainders.
Each level does two recursive calls of half size and a constant number of products of -limb numbers and matrices, so the cost is , which is . The GCD is obtained by applying the half-GCD repeatedly, each time halving the size, at the same total cost. The correctness of the truncations in steps 2 and 4, which is delicate in Schönhage's original formulation, was simplified by Möller [Mol2008], whose version is the one in GMP. FLINT's own version formulates it with intervals as in Corollary 2, which makes the check of step 2 a comparison of the transformed endpoints with 1.
Contents and least common multiples of vectors#
The content of a vector , the GCD of its entries, can be computed as a chain of GCDs, but the chain is dominated by the latency of the GCDs when the entries are small. The running GCD can only decrease, and after a few entries it is usually the content. Testing each entry for divisibility by first, and computing a GCD only when the test fails, replaces most GCDs by divisibility tests: a test fails only when loses a prime factor, so at most once per prime factor of , counted with multiplicity. The tests are independent of each other, so they run at the throughput of the multiplier.
For word-size and with odd, if and only if the low bits of are zero and , where is the word base: one multiplication and one comparison. If the test fails, the high word of satisfies , so starts from an operand already reduced modulo . An entry of several words is reduced modulo a word-size with independent multiplications, by Hensel remainders or by folding its words with precomputed powers . For a multi-word , which is kept odd, the entries are tested by Hensel divisibility tests, or for long by division with remainder.
The order of the entries matters when the content is 1 or the entries are large. For a polynomial, the leading and trailing coefficients are the most likely to differ from the others in their factors, so the entries at both ends of the vector come first. Single-word entries come before larger ones, so that usually fits in a word before the large entries are reduced; among the large entries, the two shortest come first. Once has settled, the remaining tests can be split between threads.
The least common multiple uses the same idea. The running LCM can only grow, and when the entries are denominators that are all 1, all equal or all divisors of a common denominator, every entry divides . Each entry is therefore first tested for , and is updated only when the test fails. While fits in a word the tests need no division, and afterwards the small entries are tested in batches with one remainder computation of per batch.
Compared with chained GCDs and LCMs (fmpz_gcd, fmpz_lcm), for vectors of 1000 entries on one core of a Cascade Lake Xeon:
| Entries | Content | Speedup | Entries | Speedup |
|---|---|---|---|---|
| 62 bits | random (content 1) | 1.0 | all 1 | 5.0 |
| 62 bits | 16-bit content | 57 | equal, 62 bits | 18 |
| 62 bits | content of half the bits | 38 | divisors of a 62-bit | 6.1 |
| 62 bits | 6-bit cofactors | 17 | 6.6 | |
| 128 bits | 16-bit content | 2.2 | 3.4 | |
| 640 bits | content of half the bits | 2.7 | random, 62 bits | 3.2 |
The gains are small where every entry needs a multiprecision GCD or division anyway: random entries with content 1, large entries with a word-size content, and random multi-word denominators.
Use in FLINT#
fmpz_gcd, fmpz_gcd_ui, fmpz_gcd3. An inline operand reduces the problem to GMP's mpn_gcd_1, which divides the large operand by the small one once and finishes with a binary GCD on words. Two large operands go to flint_mpz_gcd, that is flint_mpn_gcd, which sets aside the common power of two and calls _flint_mpn_gcd_inplace, a port of GMP's mpn_gcd [GMP] that uses FLINT's multiplication and division: Lehmer's algorithm with double-word steps (_flint_mpn_hgcd2) for small operands and Möller's half-GCD for large ones. Unlike GMP, each Lehmer step applies its word matrix to both operands in a single pass, and the matrix products of the half-GCD, whose entries share operands, are computed for large operands in transform space with fft_small (Arithmetic in transform space), so that each operand is transformed once and each result is transformed back once.
fmpz_xgcd, fmpz_xgcd_canonical_bezout, fmpz_gcdinv, fmpz_invmod. flint_mpz_gcdext and flint_mpz_invert, built on _flint_mpn_gcdext_inplace, the port of GMP's mpn_gcdext on the same half-GCD with cofactor tracking; unlike GMP, it accumulates the matrices of consecutive half-GCD steps from right to left before applying them to the cofactors, instead of updating the full-length cofactors after every step. For word-size operands, GMP's mpn_gcdext_1 (fmpz_xgcd_canonical_bezout) or FLINT's n_gcdinv (fmpz_gcdinv, fmpz_invmod) are used (GCD, LCM and Bézout coefficients). The modular inverses of mpn_mod call _flint_mpn_gcdext_inplace directly.
_fmpz_vec_content, _fmpz_vec_lcm and related functions. flint_mpn_gcd_vec_1 and flint_mpn_gcd_vec compute contents as in Contents and least common multiples of vectors, of single limbs and of multi-limb values given by pointer. _fmpz_vec_content, _fmpz_vec_content_chained, _fmpz_vec_lcm and _fmpz_vec_lcm_strided read the entries of an fmpz vector in place, small entries in blocks and large entries by pointer to their limbs, and use threads only for long vectors of large entries. They give the contents and canonical forms of fmpz_poly, fmpq_poly, fmpz_mat, fmpz_mpoly, fmpq_mpoly and fmpz_mpoly_q, and the common denominators of _fmpq_vec_get_fmpz_vec_fmpz and the fmpq_mat_get_fmpz_mat functions, whose numerators _fmpq_vec_get_fmpz_vec_scaled forms without divisions for denominators equal to the LCM or to 1.
n_gcd, n_xgcd, n_gcdinv. Word GCDs: n_gcd calls GMP's binary mpn_gcd_11; n_xgcd and n_gcdinv are FLINT's own extended Euclidean algorithms with special handling of small quotients.
fmpz_xgcd_partial. FLINT's own Lehmer algorithm with the Jebelean condition of Lehmer's algorithm, which stops when the remainder falls below a bound and returns the last two remainders and cofactors. It is the partial Euclidean algorithm of Shanks's NUCOMP and NUDUPL for binary quadratic forms in qfb, and is also used by Cornacchia's algorithm in ecpp.
_fmpq_hgcd and _fmpq_ball_get_cfrac. FLINT's own integer half-GCD in the interval formulation of Lemma 1: it maintains a matrix and a pair representing the interval , and generates quotients valid for the whole interval. It uses single Euclidean steps when the remaining size is below 4 limbs, a Lehmer algorithm with a two-limb half-GCD (_uiui_hgcd) up to 500 limbs, and above that recursion on the top halves as in Algorithm 1. It computes continued fractions (fmpq_get_cfrac), the simplest rational between two numbers (fmpq_simplest_between), Dedekind sums (fmpq_dedekind_sum) and rational reconstruction (fmpq_reconstruct_fmpz_2, which also has its own Lehmer loop for the final steps). Continued fractions of rational numbers and Rational reconstruction describe these applications.
void _fmpz_vec_content(fmpz_t res, const fmpz * vec, slong len)void _fmpz_vec_lcm(fmpz_t res, const fmpz * vec, slong len)Referenced from#
- Binary quadratic forms and class groups (algorithm)
- Continued fractions of rational numbers (algorithm)
- GCD, LCM and Bézout coefficients
- fmpz.h (module)
- Modular arithmetic with integers
_fmpz_vec_content,_fmpz_vec_content_chained_fmpz_vec_lcm,_fmpz_vec_lcm_strided- mpn_extras.h (module)
_flint_mpn_hgcd_matrix_init,_flint_mpn_hgcd_matrix_update_q,_flint_mpn_hgcd_matrix_mul_1,_flint_mpn_hgcd_matrix_mul,_flint_mpn_hgcd_matrix_adjust,_flint_mpn_hgcd_matrix22_mul_strassen_itch,_flint_mpn_hgcd_matrix22_mul_strassenqfb_nucomp
References#
- [Ste1967] J. Stein. Computational problems associated with Racah algebra. Journal of Computational Physics 1 (1967), 397–405. https://doi.org/10.1016/0021-9991(67)90047-2
- [SZ2004] D. Stehlé and P. Zimmermann. A binary recursive gcd algorithm. Algorithmic Number Theory (ANTS VI), LNCS 3076 (2004), 411–425. https://doi.org/10.1007/978-3-540-24847-7_31
- [Leh1938] D. H. Lehmer, "Euclid's algorithm for large numbers", American Mathematical Monthly 45:4 (1938), 227–233.
- [Jeb1993b] T. Jebelean. Improving the multiprecision Euclidean algorithm. DISCO '93, LNCS 722 (1993), 45–58.
- [Knu1970] D. E. Knuth, "The analysis of algorithms", Actes du Congrès International des Mathématiciens (Nice, 1970), tome 3, Gauthier-Villars (1971), 269–274
- [Sch1971] A. Schönhage, "Schnelle Berechnung von Kettenbruchentwicklungen", Acta Informatica 1 (1971), 139–144
- [Mol2008] N. Möller. On Schönhage's algorithm and subquadratic integer GCD computation. Mathematics of Computation 77 (2008), 589–607. https://doi.org/10.1090/S0025-5718-07-02017-0
- [GMP] T. Granlund and the GMP development team. GNU MP: The GNU Multiple Precision Arithmetic Library, version 6.3.0. Manual, 2023. https://gmplib.org/manual/
The FLINT team, “Integer GCD,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/integers/gcd.html (generated 2026-11-02).
@misc{flint-doc-algorithms-integers-gcd,
author = {{The FLINT team}},
title = {{Integer GCD}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/algorithms/integers/gcd.html},
note = {Generated 2026-11-02}
}