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}