implement Stein's algorithm for gcd

This commit is contained in:
Emerentius 2015-09-07 02:40:53 +02:00
parent 85b9ac58bf
commit e892054813
1 changed files with 43 additions and 10 deletions

View File

@ -217,15 +217,38 @@ macro_rules! impl_integer_for_isize {
/// `other`. The result is always positive.
#[inline]
fn gcd(&self, other: &$T) -> $T {
// Use Euclid's algorithm
// Use Stein's algorithm
let mut m = *self;
let mut n = *other;
if m == 0 || n == 0 { return (m | n).abs() }
// find common factors of 2
let shift = (m | n).trailing_zeros();
// If one number is the minimum value, it cannot be represented as a
// positive number. It's also a power of two, so the gcd can
// trivially be calculated in that case by bitshifting
// The result is always positive in two's complement, unless
// a and b are the minimum value, then it's negative
// no other way to represent that number
if m == <$T>::min_value() || n == <$T>::min_value() { return 1 << shift }
// guaranteed to be positive now, rest like unsigned algorithm
m = m.abs();
n = n.abs();
// divide a and b by 2 until odd
// m inside loop
n >>= n.trailing_zeros();
while m != 0 {
let temp = m;
m = n % temp;
n = temp;
m >>= m.trailing_zeros();
if n > m { ::std::mem::swap(&mut n, &mut m) }
m -= n;
}
n.abs()
n << shift
}
/// Calculates the Lowest Common Multiple (LCM) of the number and
@ -396,15 +419,25 @@ macro_rules! impl_integer_for_usize {
/// Calculates the Greatest Common Divisor (GCD) of the number and `other`
#[inline]
fn gcd(&self, other: &$T) -> $T {
// Use Euclid's algorithm
// Use Stein's algorithm
let mut m = *self;
let mut n = *other;
if m == 0 || n == 0 { return m | n }
// find common factors of 2
let shift = (m | n).trailing_zeros();
// divide a and b by 2 until odd
// m inside loop
n >>= n.trailing_zeros();
while m != 0 {
let temp = m;
m = n % temp;
n = temp;
m >>= m.trailing_zeros();
if n > m { ::std::mem::swap(&mut n, &mut m) }
m -= n;
}
n
n << shift
}
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.