use std::mem::{replace, swap};
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;
}
let shift = (lhs | rhs).trailing_zeros();
rhs >>= rhs.trailing_zeros();
while lhs > 0 {
lhs >>= lhs.trailing_zeros();
if rhs > lhs {
swap(&mut lhs, &mut rhs);
}
lhs -= rhs
}
rhs << shift
})
}
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)
}
#[inline]
pub fn gcd(lhs: u64, rhs: u64) -> u64 {
gcd_many(&[lhs, rhs])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extended_gcd_works() {
let test_suits = [
(123, 0, (123, 1, 0)),
(0, 123, (123, 0, 1)),
(2048, 48, (16, -1, 43)),
(2052, 617, (1, 132, -439)),
(0, 0, (0, 1, 0)),
];
let result: Vec<(u64, i64, i64)> =
test_suits.iter().map(|t| extended_gcd(t.0, t.1)).collect();
for i in 0..test_suits.len() {
assert_eq!(test_suits[i].2, result[i]);
}
}
#[test]
fn gcd_many_works() {
let test_suits = [
(vec![], 0),
(vec![223], 223),
(vec![1, 2, 3, 4, 5], 1),
(vec![8, 24, 156, 36], 4),
(vec![0, 0, 0, 0], 0),
];
let result: Vec<u64> = test_suits.iter().map(|t| gcd_many(&t.0)).collect();
for i in 0..test_suits.len() {
assert_eq!(test_suits[i].1, result[i]);
}
}
}