Skip to content

Commit c0a4c48

Browse files
committed
Define extended GCD, combined GCD+LCM
1 parent f2423e9 commit c0a4c48

File tree

1 file changed

+162
-12
lines changed

1 file changed

+162
-12
lines changed

src/lib.rs

Lines changed: 162 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ extern crate std;
2121

2222
extern crate num_traits as traits;
2323

24-
use core::ops::Add;
24+
use core::mem;
25+
use core::ops::{Add, SubAssign};
2526

2627
use traits::{Num, Signed, Zero};
2728

@@ -95,6 +96,69 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
9596
/// ~~~
9697
fn lcm(&self, other: &Self) -> Self;
9798

99+
/// Greatest Common Divisor (GCD) and
100+
/// Lowest Common Multiple (LCM) together.
101+
///
102+
/// Potentially more efficient than calling `gcd` and `lcm`
103+
/// individually for identical inputs.
104+
///
105+
/// # Examples
106+
///
107+
/// ~~~
108+
/// # use num_integer::Integer;
109+
/// assert_eq!(10.gcd_lcm(&4), (2, 20));
110+
/// assert_eq!(8.gcd_lcm(&9), (1, 72));
111+
/// ~~~
112+
#[inline]
113+
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
114+
(self.gcd(other), self.lcm(other))
115+
}
116+
117+
/// Greatest common divisor and Bézout coefficients.
118+
///
119+
/// # Examples
120+
///
121+
/// ~~~
122+
/// # extern crate num_traits;
123+
/// # use num_integer::{ExtendedGcd, Integer};
124+
/// # use num_traits::NumAssign;
125+
/// fn check<A: Clone + Integer + NumAssign>(a: A, b: A) -> bool {
126+
/// let ExtendedGcd { gcd, coeffs: [x, y], .. } = a.extended_gcd(&b);
127+
/// gcd == x * a + y * b
128+
/// }
129+
/// assert!(check(10isize, 4isize));
130+
/// assert!(check(8isize, 9isize));
131+
/// ~~~
132+
#[inline]
133+
fn extended_gcd(&self, other: &Self) -> ExtendedGcd<Self>
134+
where Self: Clone + SubAssign<Self> {
135+
let mut s = (Self::zero(), Self::one());
136+
let mut t = (Self::one(), Self::zero());
137+
let mut r = (other.clone(), self.clone());
138+
139+
while !r.0.is_zero() {
140+
let q = r.1.clone() / r.0.clone();
141+
let f = |r: &mut (Self, Self)| {
142+
mem::swap(&mut r.0, &mut r.1);
143+
r.0 -= q.clone() * r.1.clone();
144+
};
145+
f(&mut r);
146+
f(&mut s);
147+
f(&mut t);
148+
}
149+
150+
if r.1 >= Self::zero() { ExtendedGcd { gcd: r.1, coeffs: [s.1, t.1], _hidden: () } }
151+
else { ExtendedGcd { gcd: Self::zero() - r.1,
152+
coeffs: [Self::zero() - s.1, Self::zero() - t.1], _hidden: () } }
153+
}
154+
155+
/// Greatest common divisor, least common multiple, and Bézout coefficients.
156+
#[inline]
157+
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self)
158+
where Self: Clone + SubAssign<Self> {
159+
(self.extended_gcd(other), self.lcm(other))
160+
}
161+
98162
/// Deprecated, use `is_multiple_of` instead.
99163
fn divides(&self, other: &Self) -> bool;
100164

@@ -173,6 +237,14 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
173237
}
174238
}
175239

240+
/// Greatest common divisor and Bézout coefficients
241+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242+
pub struct ExtendedGcd<A> {
243+
pub gcd: A,
244+
pub coeffs: [A; 2],
245+
_hidden: (),
246+
}
247+
176248
/// Simultaneous integer division and modulus
177249
#[inline]
178250
pub fn div_rem<T: Integer>(x: T, y: T) -> (T, T) {
@@ -206,6 +278,13 @@ pub fn lcm<T: Integer>(x: T, y: T) -> T {
206278
x.lcm(&y)
207279
}
208280

281+
/// Calculates the Greatest Common Divisor (GCD) and
282+
/// Lowest Common Multiple (LCM) of the number and `other`.
283+
#[inline(always)]
284+
pub fn gcd_lcm<T: Integer>(x: T, y: T) -> (T, T) {
285+
x.gcd_lcm(&y)
286+
}
287+
209288
macro_rules! impl_integer_for_isize {
210289
($T:ty, $test_mod:ident) => {
211290
impl Integer for $T {
@@ -294,16 +373,31 @@ macro_rules! impl_integer_for_isize {
294373
m << shift
295374
}
296375

376+
#[inline]
377+
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self) {
378+
let egcd = self.extended_gcd(other);
379+
// should not have to recalculate abs
380+
let lcm = if egcd.gcd.is_zero() { Self::zero() }
381+
else { (*self * (*other / egcd.gcd)).abs() };
382+
(egcd, lcm)
383+
}
384+
297385
/// Calculates the Lowest Common Multiple (LCM) of the number and
298386
/// `other`.
299387
#[inline]
300388
fn lcm(&self, other: &Self) -> Self {
301-
if self.is_zero() && other.is_zero() {
302-
Self::zero()
303-
} else {
304-
// should not have to recalculate abs
305-
(*self * (*other / self.gcd(other))).abs()
306-
}
389+
self.gcd_lcm(other).1
390+
}
391+
392+
/// Calculates the Greatest Common Divisor (GCD) and
393+
/// Lowest Common Multiple (LCM) of the number and `other`.
394+
#[inline]
395+
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
396+
if self.is_zero() && other.is_zero() { return (Self::zero(), Self::zero()) }
397+
let gcd = self.gcd(other);
398+
// should not have to recalculate abs
399+
let lcm = (*self * (*other / gcd)).abs();
400+
(gcd, lcm)
307401
}
308402

309403
/// Deprecated, use `is_multiple_of` instead.
@@ -488,6 +582,38 @@ macro_rules! impl_integer_for_isize {
488582
assert_eq!((11 as $T).lcm(&5), 55 as $T);
489583
}
490584

585+
#[test]
586+
fn test_gcd_lcm() {
587+
use core::iter::once;
588+
let r = once(0).chain((1..).take(127).flat_map(|a| once(a).chain(once(-a)))).chain(once(-128));
589+
for i in r.clone() {
590+
for j in r.clone() {
591+
assert_eq!(i.gcd_lcm(&j), (i.gcd(&j), i.lcm(&j)));
592+
}
593+
}
594+
}
595+
596+
#[test]
597+
fn test_extended_gcd_lcm() {
598+
use ExtendedGcd;
599+
use traits::NumAssign;
600+
601+
fn check<A: Clone + Integer + NumAssign>(a: A, b: A) -> bool {
602+
let ExtendedGcd { gcd, coeffs: [x, y], .. } = a.extended_gcd(&b);
603+
gcd == x * a + y * b
604+
}
605+
606+
use core::iter::once;
607+
let r = once(0).chain((1..).take(127).flat_map(|a| once(a).chain(once(-a)))).chain(once(-128));
608+
for i in r.clone() {
609+
for j in r.clone() {
610+
check(i, j);
611+
let (ExtendedGcd { gcd, .. }, lcm) = i.extended_gcd_lcm(&j);
612+
assert_eq!((gcd, lcm), (i.gcd(&j), i.lcm(&j)));
613+
}
614+
}
615+
}
616+
491617
#[test]
492618
fn test_even() {
493619
assert_eq!((-4 as $T).is_even(), true);
@@ -569,14 +695,29 @@ macro_rules! impl_integer_for_usize {
569695
m << shift
570696
}
571697

698+
#[inline]
699+
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self) {
700+
let egcd = self.extended_gcd(other);
701+
// should not have to recalculate abs
702+
let lcm = if egcd.gcd.is_zero() { Self::zero() }
703+
else { *self * (*other / egcd.gcd) };
704+
(egcd, lcm)
705+
}
706+
572707
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.
573708
#[inline]
574709
fn lcm(&self, other: &Self) -> Self {
575-
if self.is_zero() && other.is_zero() {
576-
Self::zero()
577-
} else {
578-
*self * (*other / self.gcd(other))
579-
}
710+
self.gcd_lcm(other).1
711+
}
712+
713+
/// Calculates the Greatest Common Divisor (GCD) and
714+
/// Lowest Common Multiple (LCM) of the number and `other`.
715+
#[inline]
716+
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
717+
if self.is_zero() && other.is_zero() { return (Self::zero(), Self::zero()) }
718+
let gcd = self.gcd(other);
719+
let lcm = *self * (*other / gcd);
720+
(gcd, lcm)
580721
}
581722

582723
/// Deprecated, use `is_multiple_of` instead.
@@ -672,6 +813,15 @@ macro_rules! impl_integer_for_usize {
672813
assert_eq!((15 as $T).lcm(&17), 255 as $T);
673814
}
674815

816+
#[test]
817+
fn test_gcd_lcm() {
818+
for i in (0..).take(256) {
819+
for j in (0..).take(256) {
820+
assert_eq!(i.gcd_lcm(&j), (i.gcd(&j), i.lcm(&j)));
821+
}
822+
}
823+
}
824+
675825
#[test]
676826
fn test_is_multiple_of() {
677827
assert!((6 as $T).is_multiple_of(&(6 as $T)));

0 commit comments

Comments
 (0)