From d9be24e354df2f43376af5188c58fb6ecbe212b5 Mon Sep 17 00:00:00 2001 From: nicoo Date: Mon, 3 Aug 2020 14:19:00 +0200 Subject: [PATCH] factor: Make the implementation of GCD more readable (#1576) * factor::numeric::gcd: Switch variable names to be more consistent * factor::numeric::gcd: Improve comments * factor::numeric::gcd: Extend loop invariant to v --- src/uu/factor/src/numeric/gcd.rs | 44 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/uu/factor/src/numeric/gcd.rs b/src/uu/factor/src/numeric/gcd.rs index 0cde65472..ec72570ba 100644 --- a/src/uu/factor/src/numeric/gcd.rs +++ b/src/uu/factor/src/numeric/gcd.rs @@ -9,39 +9,43 @@ use std::cmp::min; use std::mem::swap; -pub fn gcd(mut n: u64, mut m: u64) -> u64 { +pub fn gcd(mut u: u64, mut v: u64) -> u64 { // Stein's binary GCD algorithm // Base cases: gcd(n, 0) = gcd(0, n) = n - if n == 0 { - return m; - } else if m == 0 { - return n; + if u == 0 { + return v; + } else if v == 0 { + return u; } - // Extract common factor-2: gcd(2ⁱ n, 2ⁱ m) = 2ⁱ gcd(n, m) - // and reducing until odd gcd(2ⁱ n, m) = gcd(n, m) if m is odd + // gcd(2ⁱ u, 2ʲ v) = 2ᵏ gcd(u, v) with u, v odd and k = min(i, j) + // 2ᵏ is the greatest power of two that divides both u and v let k = { - let k_n = n.trailing_zeros(); - let k_m = m.trailing_zeros(); - n >>= k_n; - m >>= k_m; - min(k_n, k_m) + let i = u.trailing_zeros(); + let j = v.trailing_zeros(); + u >>= i; + v >>= j; + min(i, j) }; loop { - // Invariant: n odd - debug_assert!(n % 2 == 1, "n = {} is even", n); + // Loop invariant: u and v are odd + debug_assert!(u % 2 == 1, "u = {} is even", u); + debug_assert!(v % 2 == 1, "v = {} is even", v); - if n > m { - swap(&mut n, &mut m); + // gcd(u, v) = gcd(|u - v|, min(u, v)) + if u > v { + swap(&mut u, &mut v); } - m -= n; + v -= u; - if m == 0 { - return n << k; + if v == 0 { + // Reached the base case; gcd is 2ᵏ u + return u << k; } - m >>= m.trailing_zeros(); + // gcd(u, 2ʲ v) = gcd(u, v) as u is odd + v >>= v.trailing_zeros(); } }