ads_rs/v1/math/gcd.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
use std::mem::{replace, swap};
/// Finds the GCD (Greatest Common Divisor) for an array of elements.
/// # Examples
/// ```
/// use ads_rs::prelude::v1::math::gcd_many;
///
/// let res0 = gcd_many(&[42, 8, 144]);
/// let res1 = gcd_many(&[89, 144, 233, 377, 610]);
/// let res2 = gcd_many(&[25, 105, 235, 100]);
///
/// assert_eq!(2, res0);
/// assert_eq!(1, res1);
/// assert_eq!(5, res2);
/// ```
/// ## Corner cases
/// - GCD of an empty array equals 0.
/// - GCD of a single element array equals that element.
/// ```
/// use ads_rs::prelude::v1::math::gcd_many;
///
/// let res0 = gcd_many(&[]);
/// let res1 = gcd_many(&[25]);
///
/// assert_eq!(0, res0);
/// assert_eq!(25, res1);
/// ```
/// # Implementation details
/// - Stein's algorithm is used.
/// - Time complexity: O(K * N<sup>2</sup>) where:
/// - N - bits count in the biggest number.
/// - K - number's count
pub fn gcd_many(elems: &[u64]) -> u64 {
if elems.is_empty() {
return 0;
}
if elems.len() == 1 {
return elems[0];
}
elems.iter().fold(0, |acc, e| {
let (mut lhs, mut rhs) = (acc, *e);
if lhs == 0 || rhs == 0 {
return lhs | rhs;
}
// find common factor of 2
let shift = (lhs | rhs).trailing_zeros();
// divide lhs and rhs by 2 until odd
rhs >>= rhs.trailing_zeros();
while lhs > 0 {
lhs >>= lhs.trailing_zeros();
if rhs > lhs {
swap(&mut lhs, &mut rhs);
}
lhs -= rhs
}
rhs << shift
})
}
/// Finds an extended GCD (Greatest Common Divisor) for a pair of numbers.
/// "Extended" means that algorithm will return not only GCD, but two coefficients `x` and `y` such that the equality
///
/// x * lhs + y * rhs = gcd(lhs, rhs)
///
/// holds.
/// # Examples
/// ```
/// use ads_rs::prelude::v1::math::extended_gcd;
///
/// let res0 = extended_gcd(30, 20);
/// let res1 = extended_gcd(15, 35);
/// let res2 = extended_gcd(161, 28);
///
/// assert_eq!((10, 1, -1), res0);
/// assert_eq!((5, -2, 1), res1);
/// assert_eq!((7, -1, 6), res2);
/// ```
/// ## Corner case
/// - Result of `extended_gcd(0, 0)` equals tuple `(0, 1, 0)`.
/// - Negative numbers is not supported, but implementation allows it.
/// ```
/// use ads_rs::prelude::v1::math::extended_gcd;
///
/// let res = extended_gcd(0, 0);
///
/// assert_eq!((0, 1, 0), res);
/// ```
/// # Implementation details
/// - Euclid's algorithm used, because its extended version is faster than Stein's algorithm
/// - Time complexity is O(log<sub>2</sub>(min(lhs, rhs)))
pub fn extended_gcd(lhs: u64, rhs: u64) -> (u64, i64, i64) {
let (mut x, mut y) = (1, 0);
let (mut x1, mut y1, mut lhs1, mut rhs1) = (0i64, 1i64, lhs, rhs);
while rhs1 > 0 {
let q = lhs1 / rhs1;
let new_x1 = x - (q as i64) * x1;
x = replace(&mut x1, new_x1);
let new_y1 = y - (q as i64) * y1;
y = replace(&mut y1, new_y1);
let new_rhs1 = lhs1 - q * rhs1;
lhs1 = replace(&mut rhs1, new_rhs1);
}
(lhs1, x, y)
}
/// Finds an GCD (Greatest Common Divisor) for a pair of numbers.
/// # Examples
/// ```
/// use ads_rs::prelude::v1::math::gcd;
///
/// let res0 = gcd(42, 144);
/// let res1 = gcd(377, 610);
/// let res2 = gcd(105, 25);
///
/// assert_eq!(6, res0);
/// assert_eq!(1, res1);
/// assert_eq!(5, res2);
/// ```
/// ## Corner case
/// GCD of both zero numbers equals 0.
/// ```
/// use ads_rs::prelude::v1::math::gcd;
///
/// let res = gcd(0, 0);
///
/// assert_eq!(0, res);
/// ```
/// # Implementation details
/// - Stein's algorithm used (from [`gcd_many`]).
/// - Time complexity: O(N<sup>2</sup>) where N - number of bits in the biggest number.
#[inline]
pub fn gcd(lhs: u64, rhs: u64) -> u64 {
gcd_many(&[lhs, rhs])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extended_gcd_works() {
// arrange
let test_suits = [
// Zero rhs
(123, 0, (123, 1, 0)),
// Zero lhs
(0, 123, (123, 0, 1)),
// Regular case
(2048, 48, (16, -1, 43)),
// Relative prime
(2052, 617, (1, 132, -439)),
// Zero lhs and rhs
(0, 0, (0, 1, 0)),
];
// act
let result: Vec<(u64, i64, i64)> =
test_suits.iter().map(|t| extended_gcd(t.0, t.1)).collect();
// assert
for i in 0..test_suits.len() {
assert_eq!(test_suits[i].2, result[i]);
}
}
#[test]
fn gcd_many_works() {
// arrange
let test_suits = [
// Empty array
(vec![], 0),
// Single element
(vec![223], 223),
// Relative prime numbers
(vec![1, 2, 3, 4, 5], 1),
// Regular case
(vec![8, 24, 156, 36], 4),
// All zeros
(vec![0, 0, 0, 0], 0),
];
// act
let result: Vec<u64> = test_suits.iter().map(|t| gcd_many(&t.0)).collect();
// assert
for i in 0..test_suits.len() {
assert_eq!(test_suits[i].1, result[i]);
}
}
}