Greatest common divisors of Gaussian integers
#include <flint/fmpzi.h>Set the output to the greatest common divisor of x and y in canonical form (see fmpzi_canonical_unit_i_pow); the GCD of 0 and 0 is 0. The euclidean version is Euclid's algorithm with fmpzi_divrem, the euclidean_improved version uses approximate quotients and finishes in double-precision arithmetic, the binary version is Weilert's (1+i)-ary algorithm [Wei2000] (which falls back to the Euclidean algorithm above 500 bits), and the shortest version finds the GCD as a shortest vector of the lattice generated by x and y. fmpzi_gcd chooses among them.
Since \mathbb{Z}[i] is a Euclidean domain, any two Gaussian integers x, y have a greatest common divisor g, unique up to multiplication by one of the units \pm 1, \pm i, and (x, y) = g\mathbb{Z}[i]. All five functions return the same g: the associate with positive real part a and imaginary part in (-a, a], so their results can be compared directly. They differ only in the algorithm.
AlgorithmGaussian integersDivision with remainder, the canonical associate, and the four GCD algorithms of fmpzi (Euclidean in exact and floating-point arithmetic, the (1+i)-ary binary algorithm, and a lattice algorithm), together with primality testing and the removal of factors 1+i.Algorithms#
fmpzi_gcd_euclideanruns Euclid's algorithm with the rounded quotients offmpzi_divrem; each step at least halves the norm.fmpzi_gcd_euclidean_improveduses the cheaper approximate quotients offmpzi_divrem_approx, and as soon as all parts are below 2^{50} it finishes in double-precision arithmetic, where the remainders are still computed exactly.fmpzi_gcd_binaryis Weilert's (1+i)-ary analogue of the binary GCD [Wei2000], which replaces divisions by additions and divisions by 1 + i. It is implemented for inputs of up to 500 bits and callsfmpzi_gcd_euclideanfor larger ones.fmpzi_gcd_shortestcomputes the Hermite normal form of the lattice spanned by x, ix, y, iy and reads off g as a shortest vector, using a quasi-linear continued fraction algorithm.fmpzi_gcduses the double-precision Euclidean algorithm when all parts are below 2^{50},fmpzi_gcd_euclidean_improvedwhen an input fits in single words or has at most 30000 bits, and otherwisefmpzi_gcd_shortest, preceded by one division if the sizes are very different.
Example#
With x = 3(1 + i)(2 + i)^3 and y = 3(2 + i)(2 - i)^2, the GCD is 3(2 + i) = 6 + 3i, since 2 + i and 2 - i are non-associate primes above 5. The program also shows a division with remainder, the removal of the factor 1 + i, and primality tests.
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/fmpzi.h>
int main(void)
{
fmpzi_t x, y, g, h, q, r, t;
fmpz_t n;
slong k;
fmpzi_init(x); fmpzi_init(y); fmpzi_init(g); fmpzi_init(h);
fmpzi_init(q); fmpzi_init(r); fmpzi_init(t);
fmpz_init(n);
/* x = 3 (1 + i) (2 + i)^3, y = 3 (2 + i) (2 - i)^2 */
fmpzi_set_si_si(t, 2, 1);
fmpzi_pow_ui(x, t, 3);
fmpzi_set_si_si(t, 3, 3);
fmpzi_mul(x, x, t);
fmpzi_set_si_si(t, 2, -1);
fmpzi_sqr(y, t);
fmpzi_set_si_si(t, 6, 3);
fmpzi_mul(y, y, t);
flint_printf("x = "); fmpzi_print(x);
flint_printf(", y = "); fmpzi_print(y); flint_printf("\n");
fmpzi_gcd(g, x, y);
flint_printf("gcd = "); fmpzi_print(g); flint_printf("\n");
fmpzi_gcd_binary(h, x, y);
flint_printf("binary: %d", fmpzi_equal(g, h));
fmpzi_gcd_shortest(h, x, y);
flint_printf(", shortest: %d", fmpzi_equal(g, h));
fmpzi_gcd_euclidean(h, x, y);
flint_printf(", euclidean: %d\n", fmpzi_equal(g, h));
fmpzi_divrem(q, r, x, y);
fmpzi_norm(n, r);
flint_printf("x = q*y + r with q = "); fmpzi_print(q);
flint_printf(", r = "); fmpzi_print(r);
flint_printf(", N(r) = "); fmpz_print(n);
fmpzi_norm(n, y);
flint_printf(", N(y) = "); fmpz_print(n); flint_printf("\n");
k = fmpzi_remove_one_plus_i(t, x);
flint_printf("x = (1+i)^%wd * (", k); fmpzi_print(t); flint_printf(")\n");
fmpzi_set_si_si(t, 2, 1);
flint_printf("prime: 2+i %d", fmpzi_is_prime(t));
fmpzi_set_si_si(t, 3, 0);
flint_printf(", 3 %d", fmpzi_is_prime(t));
fmpzi_set_si_si(t, 5, 0);
flint_printf(", 5 %d\n", fmpzi_is_prime(t));
fmpzi_clear(x); fmpzi_clear(y); fmpzi_clear(g); fmpzi_clear(h);
fmpzi_clear(q); fmpzi_clear(r); fmpzi_clear(t);
fmpz_clear(n);
return 0;
}x = -27+39*I, y = 30-15*I gcd = 6+3*I binary: 1, shortest: 1, euclidean: 1 x = q*y + r with q = -1+1*I, r = -12-6*I, N(r) = 180, N(y) = 1125 x = (1+i)^1 * (6+33*I) prime: 2+i 1, 3 1, 5 0
The remainder satisfies N(r) = 180 \le N(y)/2. The rational prime 3 stays prime in \mathbb{Z}[i] because 3 \equiv 3 \pmod 4, while 5 = (2 + i)(2 - i) splits.
References#
- [Wei2000] A. Weilert, "(1+i)-ary GCD computation in Z[i] as an analogue to the binary GCD algorithm", Journal of Symbolic Computation 30.5 (2000): 605-617, https://doi.org/10.1006/jsco.2000.0422
The FLINT team, “Greatest common divisors of Gaussian integers (fmpzi.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fmpzi/gcd.html (generated 2026-10-20).
@misc{flint-doc-fmpzi-gcd,
author = {{The FLINT team}},
title = {{Greatest common divisors of Gaussian integers (fmpzi.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/fmpzi/gcd.html},
note = {Generated 2026-10-20}
}