Skip to content

Commit 58055fb

Browse files
committed
Define extended GCD, combined GCD+LCM
1 parent 48caf28 commit 58055fb

File tree

1 file changed

+162
-6
lines changed

1 file changed

+162
-6
lines changed

src/lib.rs

Lines changed: 162 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ extern crate std;
2222

2323
extern crate num_traits as traits;
2424

25-
use core::ops::Add;
25+
use core::mem;
26+
use core::ops::{Add, SubAssign};
2627

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

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

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

@@ -174,6 +238,14 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
174238
}
175239
}
176240

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

282+
/// Calculates the Greatest Common Divisor (GCD) and
283+
/// Lowest Common Multiple (LCM) of the number and `other`.
284+
#[inline(always)]
285+
pub fn gcd_lcm<T: Integer>(x: T, y: T) -> (T, T) {
286+
x.gcd_lcm(&y)
287+
}
288+
210289
macro_rules! impl_integer_for_isize {
211290
($T:ty, $test_mod:ident) => (
212291
impl Integer for $T {
@@ -290,13 +369,31 @@ macro_rules! impl_integer_for_isize {
290369
m << shift
291370
}
292371

372+
#[inline]
373+
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self) {
374+
let egcd = self.extended_gcd(other);
375+
// should not have to recalculate abs
376+
let lcm = if egcd.gcd.is_zero() { Self::zero() }
377+
else { (*self * (*other / egcd.gcd)).abs() };
378+
(egcd, lcm)
379+
}
380+
293381
/// Calculates the Lowest Common Multiple (LCM) of the number and
294382
/// `other`.
295383
#[inline]
296384
fn lcm(&self, other: &Self) -> Self {
297-
if self.is_zero() && other.is_zero() { Self::zero() }
298-
else { // should not have to recalculate abs
299-
(*self * (*other / self.gcd(other))).abs() }
385+
self.gcd_lcm(other).1
386+
}
387+
388+
/// Calculates the Greatest Common Divisor (GCD) and
389+
/// Lowest Common Multiple (LCM) of the number and `other`.
390+
#[inline]
391+
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
392+
if self.is_zero() && other.is_zero() { return (Self::zero(), Self::zero()) }
393+
let gcd = self.gcd(other);
394+
// should not have to recalculate abs
395+
let lcm = (*self * (*other / gcd)).abs();
396+
(gcd, lcm)
300397
}
301398

302399
/// Deprecated, use `is_multiple_of` instead.
@@ -477,6 +574,38 @@ macro_rules! impl_integer_for_isize {
477574
assert_eq!((11 as $T).lcm(&5), 55 as $T);
478575
}
479576

577+
#[test]
578+
fn test_gcd_lcm() {
579+
use core::iter::once;
580+
let r = once(0).chain((1..).take(127).flat_map(|a| once(a).chain(once(-a)))).chain(once(-128));
581+
for i in r.clone() {
582+
for j in r.clone() {
583+
assert_eq!(i.gcd_lcm(&j), (i.gcd(&j), i.lcm(&j)));
584+
}
585+
}
586+
}
587+
588+
#[test]
589+
fn test_extended_gcd_lcm() {
590+
use ExtendedGcd;
591+
use traits::NumAssign;
592+
593+
fn check<A: Clone + Integer + NumAssign>(a: A, b: A) -> bool {
594+
let ExtendedGcd { gcd, coeffs: [x, y], .. } = a.extended_gcd(&b);
595+
gcd == x * a + y * b
596+
}
597+
598+
use core::iter::once;
599+
let r = once(0).chain((1..).take(127).flat_map(|a| once(a).chain(once(-a)))).chain(once(-128));
600+
for i in r.clone() {
601+
for j in r.clone() {
602+
check(i, j);
603+
let (ExtendedGcd { gcd, .. }, lcm) = i.extended_gcd_lcm(&j);
604+
assert_eq!((gcd, lcm), (i.gcd(&j), i.lcm(&j)));
605+
}
606+
}
607+
}
608+
480609
#[test]
481610
fn test_even() {
482611
assert_eq!((-4 as $T).is_even(), true);
@@ -556,11 +685,29 @@ macro_rules! impl_integer_for_usize {
556685
m << shift
557686
}
558687

688+
#[inline]
689+
fn extended_gcd_lcm(&self, other: &Self) -> (ExtendedGcd<Self>, Self) {
690+
let egcd = self.extended_gcd(other);
691+
// should not have to recalculate abs
692+
let lcm = if egcd.gcd.is_zero() { Self::zero() }
693+
else { *self * (*other / egcd.gcd) };
694+
(egcd, lcm)
695+
}
696+
559697
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.
560698
#[inline]
561699
fn lcm(&self, other: &Self) -> Self {
562-
if self.is_zero() && other.is_zero() { Self::zero() }
563-
else { *self * (*other / self.gcd(other)) }
700+
self.gcd_lcm(other).1
701+
}
702+
703+
/// Calculates the Greatest Common Divisor (GCD) and
704+
/// Lowest Common Multiple (LCM) of the number and `other`.
705+
#[inline]
706+
fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
707+
if self.is_zero() && other.is_zero() { return (Self::zero(), Self::zero()) }
708+
let gcd = self.gcd(other);
709+
let lcm = *self * (*other / gcd);
710+
(gcd, lcm)
564711
}
565712

566713
/// Deprecated, use `is_multiple_of` instead.
@@ -656,6 +803,15 @@ macro_rules! impl_integer_for_usize {
656803
assert_eq!((15 as $T).lcm(&17), 255 as $T);
657804
}
658805

806+
#[test]
807+
fn test_gcd_lcm() {
808+
for i in (0..).take(256) {
809+
for j in (0..).take(256) {
810+
assert_eq!(i.gcd_lcm(&j), (i.gcd(&j), i.lcm(&j)));
811+
}
812+
}
813+
}
814+
659815
#[test]
660816
fn test_is_multiple_of() {
661817
assert!((6 as $T).is_multiple_of(&(6 as $T)));

0 commit comments

Comments
 (0)