Skip to main content

malachite_nz/gaussian_integer/arithmetic/
gcd.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2022 Fredrik Johansson
6//
7// This file is part of Malachite.
8//
9// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
10// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
11// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
12
13use crate::gaussian_integer::GaussianInteger;
14use crate::gaussian_integer::arithmetic::div_rem::div_rem_approx;
15use crate::integer::Integer;
16use core::mem::take;
17use malachite_base::num::arithmetic::traits::{CanonicalizeUnit, Floor, Gcd, GcdAssign};
18use malachite_base::num::conversion::traits::ExactFrom;
19use malachite_base::num::logic::traits::SignificantBits;
20
21// When all four parts have at most this many bits, the whole GCD runs in double precision: the
22// products in the quotient estimate stay within a factor of 2 of the inputs, so every remainder is
23// computed exactly, and the estimate's rounding error is far too small to stall the descent.
24const DOUBLE_GCD_BITS: u64 = 50;
25
26fn fits_double(x: &GaussianInteger) -> bool {
27    x.real.significant_bits() <= DOUBLE_GCD_BITS
28        && x.imaginary.significant_bits() <= DOUBLE_GCD_BITS
29}
30
31// The Euclidean algorithm in double precision, from `_fmpzi_gcd_dddd`: the quotient is the nearest
32// Gaussian integer to x / y, evaluated as floor(x conj(y) / N(y) + (1 + i) / 2), and the remainder
33// x - qy is exact.
34fn gcd_double(mut a: f64, mut b: f64, mut c: f64, mut d: f64) -> GaussianInteger {
35    while c != 0.0 || d != 0.0 {
36        let t = a * c + b * d;
37        let u = b * c - a * d;
38        let v = c * c + d * d;
39        let w = 0.5 / v;
40        let qa = Floor::floor((2.0 * t + v) * w);
41        let qb = Floor::floor((2.0 * u + v) * w);
42        let t = a - (qa * c - qb * d);
43        let u = b - (qb * c + qa * d);
44        a = c;
45        b = d;
46        c = t;
47        d = u;
48    }
49    GaussianInteger {
50        real: Integer::exact_from(a),
51        imaginary: Integer::exact_from(b),
52    }
53    .canonicalize_unit()
54}
55
56// FLINT's `fmpzi_gcd` without its lattice tier: the double-precision kernel once the parts are
57// small, and the Euclidean algorithm over approximate divisions until then. FLINT switches to
58// `fmpzi_gcd_shortest`, a lattice method, when both operands exceed 30,000 bits; that method is not
59// ported yet, so such operands take the Euclidean path.
60fn gcd_helper(mut x: GaussianInteger, mut y: GaussianInteger) -> GaussianInteger {
61    if x == 0u32 {
62        return y.canonicalize_unit();
63    } else if y == 0u32 {
64        return x.canonicalize_unit();
65    }
66    loop {
67        if fits_double(&x) && fits_double(&y) {
68            return gcd_double(
69                f64::exact_from(&x.real),
70                f64::exact_from(&x.imaginary),
71                f64::exact_from(&y.real),
72                f64::exact_from(&y.imaginary),
73            );
74        }
75        let r = div_rem_approx(&x, &y).1;
76        x = y;
77        y = r;
78        if y == 0u32 {
79            return x.canonicalize_unit();
80        }
81    }
82}
83
84impl Gcd<Self> for GaussianInteger {
85    type Output = Self;
86
87    /// Computes the GCD (greatest common divisor) of two [`GaussianInteger`]s, taking both by
88    /// value.
89    ///
90    /// The Gaussian integers are a Euclidean domain, so any two have a GCD, defined up to
91    /// multiplication by one of the four units $\pm 1, \pm i$. The one returned is in canonical
92    /// unit form (see [`CanonicalizeUnit`]): its real part is positive and its imaginary part lies
93    /// in $(-\text{real}, \text{real}]$, unless it is zero. The GCD of 0 and $x$ is the canonical
94    /// form of $x$; in particular $\gcd(0, 0) = 0$, which makes sense if we interpret "greatest" to
95    /// mean "greatest by the divisibility order".
96    ///
97    /// $$
98    /// f(x, y) = \gcd(x, y).
99    /// $$
100    ///
101    /// # Worst-case complexity
102    /// $T(n) = O(n^2)$
103    ///
104    /// $M(n) = O(n)$
105    ///
106    /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
107    /// bits of the real and imaginary parts of `self` and `other`.
108    ///
109    /// # Examples
110    /// ```
111    /// use malachite_base::num::arithmetic::traits::Gcd;
112    /// use malachite_nz::gaussian_integer::GaussianInteger;
113    /// use std::str::FromStr;
114    ///
115    /// // 3+4i = (2+i)^2 and 5 = (2+i)(2-i)
116    /// let x = GaussianInteger::from_str("3+4i").unwrap();
117    /// let y = GaussianInteger::from(5);
118    /// assert_eq!((x.gcd(y)).to_string(), "2+i");
119    /// ```
120    #[inline]
121    fn gcd(self, other: Self) -> Self {
122        gcd_helper(self, other)
123    }
124}
125
126impl Gcd<&Self> for GaussianInteger {
127    type Output = Self;
128
129    /// Computes the GCD (greatest common divisor) of two [`GaussianInteger`]s, taking the first by
130    /// value and the second by reference.
131    ///
132    /// The Gaussian integers are a Euclidean domain, so any two have a GCD, defined up to
133    /// multiplication by one of the four units $\pm 1, \pm i$. The one returned is in canonical
134    /// unit form (see [`CanonicalizeUnit`]): its real part is positive and its imaginary part lies
135    /// in $(-\text{real}, \text{real}]$, unless it is zero. The GCD of 0 and $x$ is the canonical
136    /// form of $x$; in particular $\gcd(0, 0) = 0$, which makes sense if we interpret "greatest" to
137    /// mean "greatest by the divisibility order".
138    ///
139    /// $$
140    /// f(x, y) = \gcd(x, y).
141    /// $$
142    ///
143    /// # Worst-case complexity
144    /// $T(n) = O(n^2)$
145    ///
146    /// $M(n) = O(n)$
147    ///
148    /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
149    /// bits of the real and imaginary parts of `self` and `other`.
150    ///
151    /// # Examples
152    /// ```
153    /// use malachite_base::num::arithmetic::traits::Gcd;
154    /// use malachite_nz::gaussian_integer::GaussianInteger;
155    /// use std::str::FromStr;
156    ///
157    /// // 3+4i = (2+i)^2 and 5 = (2+i)(2-i)
158    /// let x = GaussianInteger::from_str("3+4i").unwrap();
159    /// let y = GaussianInteger::from(5);
160    /// assert_eq!((x.gcd(&y)).to_string(), "2+i");
161    /// ```
162    #[inline]
163    fn gcd(self, other: &Self) -> Self {
164        gcd_helper(self, other.clone())
165    }
166}
167
168impl Gcd<GaussianInteger> for &GaussianInteger {
169    type Output = GaussianInteger;
170
171    /// Computes the GCD (greatest common divisor) of two [`GaussianInteger`]s, taking the first by
172    /// reference and the second by value.
173    ///
174    /// The Gaussian integers are a Euclidean domain, so any two have a GCD, defined up to
175    /// multiplication by one of the four units $\pm 1, \pm i$. The one returned is in canonical
176    /// unit form (see [`CanonicalizeUnit`]): its real part is positive and its imaginary part lies
177    /// in $(-\text{real}, \text{real}]$, unless it is zero. The GCD of 0 and $x$ is the canonical
178    /// form of $x$; in particular $\gcd(0, 0) = 0$, which makes sense if we interpret "greatest" to
179    /// mean "greatest by the divisibility order".
180    ///
181    /// $$
182    /// f(x, y) = \gcd(x, y).
183    /// $$
184    ///
185    /// # Worst-case complexity
186    /// $T(n) = O(n^2)$
187    ///
188    /// $M(n) = O(n)$
189    ///
190    /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
191    /// bits of the real and imaginary parts of `self` and `other`.
192    ///
193    /// # Examples
194    /// ```
195    /// use malachite_base::num::arithmetic::traits::Gcd;
196    /// use malachite_nz::gaussian_integer::GaussianInteger;
197    /// use std::str::FromStr;
198    ///
199    /// // 3+4i = (2+i)^2 and 5 = (2+i)(2-i)
200    /// let x = GaussianInteger::from_str("3+4i").unwrap();
201    /// let y = GaussianInteger::from(5);
202    /// assert_eq!(((&x).gcd(y)).to_string(), "2+i");
203    /// ```
204    #[inline]
205    fn gcd(self, other: GaussianInteger) -> GaussianInteger {
206        gcd_helper(self.clone(), other)
207    }
208}
209
210impl Gcd<&GaussianInteger> for &GaussianInteger {
211    type Output = GaussianInteger;
212
213    /// Computes the GCD (greatest common divisor) of two [`GaussianInteger`]s, taking both by
214    /// reference.
215    ///
216    /// The Gaussian integers are a Euclidean domain, so any two have a GCD, defined up to
217    /// multiplication by one of the four units $\pm 1, \pm i$. The one returned is in canonical
218    /// unit form (see [`CanonicalizeUnit`]): its real part is positive and its imaginary part lies
219    /// in $(-\text{real}, \text{real}]$, unless it is zero. The GCD of 0 and $x$ is the canonical
220    /// form of $x$; in particular $\gcd(0, 0) = 0$, which makes sense if we interpret "greatest" to
221    /// mean "greatest by the divisibility order".
222    ///
223    /// $$
224    /// f(x, y) = \gcd(x, y).
225    /// $$
226    ///
227    /// # Worst-case complexity
228    /// $T(n) = O(n^2)$
229    ///
230    /// $M(n) = O(n)$
231    ///
232    /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
233    /// bits of the real and imaginary parts of `self` and `other`.
234    ///
235    /// # Examples
236    /// ```
237    /// use malachite_base::num::arithmetic::traits::Gcd;
238    /// use malachite_nz::gaussian_integer::GaussianInteger;
239    /// use std::str::FromStr;
240    ///
241    /// // 3+4i = (2+i)^2 and 5 = (2+i)(2-i)
242    /// let x = GaussianInteger::from_str("3+4i").unwrap();
243    /// let y = GaussianInteger::from(5);
244    /// assert_eq!(((&x).gcd(&y)).to_string(), "2+i");
245    /// ```
246    #[inline]
247    fn gcd(self, other: &GaussianInteger) -> GaussianInteger {
248        gcd_helper(self.clone(), other.clone())
249    }
250}
251
252impl GcdAssign<Self> for GaussianInteger {
253    /// Replaces a [`GaussianInteger`] by its GCD (greatest common divisor) with another
254    /// [`GaussianInteger`], taking the [`GaussianInteger`] on the right-hand side by value.
255    ///
256    /// The Gaussian integers are a Euclidean domain, so any two have a GCD, defined up to
257    /// multiplication by one of the four units $\pm 1, \pm i$. The one returned is in canonical
258    /// unit form (see [`CanonicalizeUnit`]): its real part is positive and its imaginary part lies
259    /// in $(-\text{real}, \text{real}]$, unless it is zero. The GCD of 0 and $x$ is the canonical
260    /// form of $x$; in particular $\gcd(0, 0) = 0$, which makes sense if we interpret "greatest" to
261    /// mean "greatest by the divisibility order".
262    ///
263    /// $$
264    /// x \gets \gcd(x, y).
265    /// $$
266    ///
267    /// # Worst-case complexity
268    /// $T(n) = O(n^2)$
269    ///
270    /// $M(n) = O(n)$
271    ///
272    /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
273    /// bits of the real and imaginary parts of `self` and `other`.
274    ///
275    /// # Examples
276    /// ```
277    /// use malachite_base::num::arithmetic::traits::GcdAssign;
278    /// use malachite_nz::gaussian_integer::GaussianInteger;
279    /// use std::str::FromStr;
280    ///
281    /// // 3+4i = (2+i)^2 and 5 = (2+i)(2-i)
282    /// let mut x = GaussianInteger::from_str("3+4i").unwrap();
283    /// x.gcd_assign(GaussianInteger::from(5));
284    /// assert_eq!(x.to_string(), "2+i");
285    /// ```
286    #[inline]
287    fn gcd_assign(&mut self, other: Self) {
288        *self = gcd_helper(take(self), other);
289    }
290}
291
292impl GcdAssign<&Self> for GaussianInteger {
293    /// Replaces a [`GaussianInteger`] by its GCD (greatest common divisor) with another
294    /// [`GaussianInteger`], taking the [`GaussianInteger`] on the right-hand side by reference.
295    ///
296    /// The Gaussian integers are a Euclidean domain, so any two have a GCD, defined up to
297    /// multiplication by one of the four units $\pm 1, \pm i$. The one returned is in canonical
298    /// unit form (see [`CanonicalizeUnit`]): its real part is positive and its imaginary part lies
299    /// in $(-\text{real}, \text{real}]$, unless it is zero. The GCD of 0 and $x$ is the canonical
300    /// form of $x$; in particular $\gcd(0, 0) = 0$, which makes sense if we interpret "greatest" to
301    /// mean "greatest by the divisibility order".
302    ///
303    /// $$
304    /// x \gets \gcd(x, y).
305    /// $$
306    ///
307    /// # Worst-case complexity
308    /// $T(n) = O(n^2)$
309    ///
310    /// $M(n) = O(n)$
311    ///
312    /// where $T$ is time, $M$ is additional memory, and $n$ is the maximum number of significant
313    /// bits of the real and imaginary parts of `self` and `other`.
314    ///
315    /// # Examples
316    /// ```
317    /// use malachite_base::num::arithmetic::traits::GcdAssign;
318    /// use malachite_nz::gaussian_integer::GaussianInteger;
319    /// use std::str::FromStr;
320    ///
321    /// // 3+4i = (2+i)^2 and 5 = (2+i)(2-i)
322    /// let mut x = GaussianInteger::from_str("3+4i").unwrap();
323    /// x.gcd_assign(&GaussianInteger::from(5));
324    /// assert_eq!(x.to_string(), "2+i");
325    /// ```
326    #[inline]
327    fn gcd_assign(&mut self, other: &Self) {
328        *self = gcd_helper(take(self), other.clone());
329    }
330}