@@ -22,7 +22,8 @@ extern crate std;
22
22
23
23
extern crate num_traits as traits;
24
24
25
- use core:: ops:: Add ;
25
+ use core:: mem;
26
+ use core:: ops:: { Add , SubAssign } ;
26
27
27
28
use traits:: { Num , Signed , Zero } ;
28
29
@@ -96,6 +97,69 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
96
97
/// ~~~
97
98
fn lcm ( & self , other : & Self ) -> Self ;
98
99
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
+
99
163
/// Deprecated, use `is_multiple_of` instead.
100
164
fn divides ( & self , other : & Self ) -> bool ;
101
165
@@ -174,6 +238,14 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
174
238
}
175
239
}
176
240
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
+
177
249
/// Simultaneous integer division and modulus
178
250
#[ inline]
179
251
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 {
207
279
x. lcm ( & y)
208
280
}
209
281
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
+
210
289
macro_rules! impl_integer_for_isize {
211
290
( $T: ty, $test_mod: ident) => (
212
291
impl Integer for $T {
@@ -290,13 +369,31 @@ macro_rules! impl_integer_for_isize {
290
369
m << shift
291
370
}
292
371
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
+
293
381
/// Calculates the Lowest Common Multiple (LCM) of the number and
294
382
/// `other`.
295
383
#[ inline]
296
384
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)
300
397
}
301
398
302
399
/// Deprecated, use `is_multiple_of` instead.
@@ -477,6 +574,38 @@ macro_rules! impl_integer_for_isize {
477
574
assert_eq!( ( 11 as $T) . lcm( & 5 ) , 55 as $T) ;
478
575
}
479
576
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
+
480
609
#[ test]
481
610
fn test_even( ) {
482
611
assert_eq!( ( -4 as $T) . is_even( ) , true ) ;
@@ -556,11 +685,29 @@ macro_rules! impl_integer_for_usize {
556
685
m << shift
557
686
}
558
687
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
+
559
697
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.
560
698
#[ inline]
561
699
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)
564
711
}
565
712
566
713
/// Deprecated, use `is_multiple_of` instead.
@@ -656,6 +803,15 @@ macro_rules! impl_integer_for_usize {
656
803
assert_eq!( ( 15 as $T) . lcm( & 17 ) , 255 as $T) ;
657
804
}
658
805
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
+
659
815
#[ test]
660
816
fn test_is_multiple_of( ) {
661
817
assert!( ( 6 as $T) . is_multiple_of( & ( 6 as $T) ) ) ;
0 commit comments