malachite_base/num/arithmetic/
gcd.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2009, 2016 William Hart
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::num::arithmetic::traits::{Gcd, GcdAssign};
14use crate::num::basic::unsigneds::PrimitiveUnsigned;
15use core::cmp::min;
16
17#[cfg(feature = "test_build")]
18pub fn gcd_euclidean<T: PrimitiveUnsigned>(x: T, y: T) -> T {
19    if y == T::ZERO {
20        x
21    } else {
22        gcd_euclidean(y, x % y)
23    }
24}
25
26#[cfg(feature = "test_build")]
27pub fn gcd_binary<T: PrimitiveUnsigned>(x: T, y: T) -> T {
28    if x == y {
29        x
30    } else if x == T::ZERO {
31        y
32    } else if y == T::ZERO {
33        x
34    } else if x.even() {
35        if y.odd() {
36            gcd_binary(x >> 1, y)
37        } else {
38            gcd_binary(x >> 1, y >> 1) << 1
39        }
40    } else if y.even() {
41        gcd_binary(x, y >> 1)
42    } else if x > y {
43        gcd_binary((x - y) >> 1, y)
44    } else {
45        gcd_binary((y - x) >> 1, x)
46    }
47}
48
49pub_test! {gcd_fast_a<T: PrimitiveUnsigned>(mut x: T, mut y: T) -> T {
50    if x == T::ZERO {
51        return y;
52    }
53    if y == T::ZERO {
54        return x;
55    }
56    let x_zeros = x.trailing_zeros();
57    let y_zeros = y.trailing_zeros();
58    let f = min(x_zeros, y_zeros);
59    x >>= x_zeros;
60    y >>= y_zeros;
61    while x != y {
62        if x < y {
63            y -= x;
64            y >>= y.trailing_zeros();
65        } else {
66            x -= y;
67            x >>= x.trailing_zeros();
68        }
69    }
70    x << f
71}}
72
73#[cfg(feature = "test_build")]
74// This is a modified version of `n_xgcd` from `ulong_extras/xgcd.c`, FLINT 2.7.1.
75pub fn gcd_fast_b<T: PrimitiveUnsigned>(mut x: T, y: T) -> T {
76    let mut v;
77    if x >= y {
78        v = y;
79    } else {
80        v = x;
81        x = y;
82    }
83    let mut d;
84    // x and y both have their top bit set.
85    if (x & v).get_highest_bit() {
86        d = x - v;
87        x = v;
88        v = d;
89    }
90    // The second value has its second-highest set.
91    while (v << 1u32).get_highest_bit() {
92        d = x - v;
93        x = v;
94        if d < v {
95            v = d;
96        } else if d < (v << 1) {
97            v = d - x;
98        } else {
99            v = d - (x << 1);
100        }
101    }
102    while v != T::ZERO {
103        // Overflow is not possible due to top 2 bits of v not being set. Avoid divisions when
104        // quotient < 4.
105        if x < (v << 2) {
106            d = x - v;
107            x = v;
108            if d < v {
109                v = d;
110            } else if d < (v << 1) {
111                v = d - x;
112            } else {
113                v = d - (x << 1);
114            }
115        } else {
116            let rem = x % v;
117            x = v;
118            v = rem;
119        }
120    }
121    x
122}
123
124macro_rules! impl_gcd {
125    ($t:ident) => {
126        impl Gcd<$t> for $t {
127            type Output = $t;
128
129            /// Computes the GCD (greatest common divisor) of two numbers.
130            ///
131            /// The GCD of 0 and $n$, for any $n$, is 0. In particular, $\gcd(0, 0) = 0$, which
132            /// makes sense if we interpret "greatest" to mean "greatest by the divisibility order".
133            ///
134            /// $$
135            /// f(x, y) = \gcd(x, y).
136            /// $$
137            ///
138            /// # Worst-case complexity
139            /// $T(n) = O(n^2)$
140            ///
141            /// $M(n) = O(n)$
142            ///
143            /// where $T$ is time, $M$ is additional memory, and $n$ is
144            /// `max(self.significant_bits(), other.significant_bits())`.
145            ///
146            /// # Examples
147            /// See [here](super::gcd#gcd).
148            #[inline]
149            fn gcd(self, other: $t) -> $t {
150                gcd_fast_a(self, other)
151            }
152        }
153
154        impl GcdAssign<$t> for $t {
155            /// Replaces another with the GCD (greatest common divisor) of it and another number.
156            ///
157            /// The GCD of 0 and $n$, for any $n$, is 0. In particular, $\gcd(0, 0) = 0$, which
158            /// makes sense if we interpret "greatest" to mean "greatest by the divisibility order".
159            ///
160            /// $$
161            /// x \gets \gcd(x, y).
162            /// $$
163            ///
164            /// # Worst-case complexity
165            /// $T(n) = O(n^2)$
166            ///
167            /// $M(n) = O(n)$
168            ///
169            /// where $T$ is time, $M$ is additional memory, and $n$ is
170            /// `max(self.significant_bits(), other.significant_bits())`.
171            ///
172            /// # Examples
173            /// See [here](super::gcd#gcd_assign).
174            #[inline]
175            fn gcd_assign(&mut self, other: $t) {
176                *self = gcd_fast_a(*self, other);
177            }
178        }
179    };
180}
181apply_to_unsigneds!(impl_gcd);