@@ -21,7 +21,8 @@ extern crate std;
21
21
22
22
extern crate num_traits as traits;
23
23
24
- use core:: ops:: Add ;
24
+ use core:: mem;
25
+ use core:: ops:: { Add , SubAssign } ;
25
26
26
27
use traits:: { Num , Signed , Zero } ;
27
28
@@ -95,6 +96,69 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
95
96
/// ~~~
96
97
fn lcm ( & self , other : & Self ) -> Self ;
97
98
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
+
98
162
/// Deprecated, use `is_multiple_of` instead.
99
163
fn divides ( & self , other : & Self ) -> bool ;
100
164
@@ -173,6 +237,14 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
173
237
}
174
238
}
175
239
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
+
176
248
/// Simultaneous integer division and modulus
177
249
#[ inline]
178
250
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 {
206
278
x. lcm ( & y)
207
279
}
208
280
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
+
209
288
macro_rules! impl_integer_for_isize {
210
289
( $T: ty, $test_mod: ident) => {
211
290
impl Integer for $T {
@@ -294,16 +373,31 @@ macro_rules! impl_integer_for_isize {
294
373
m << shift
295
374
}
296
375
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
+
297
385
/// Calculates the Lowest Common Multiple (LCM) of the number and
298
386
/// `other`.
299
387
#[ inline]
300
388
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)
307
401
}
308
402
309
403
/// Deprecated, use `is_multiple_of` instead.
@@ -488,6 +582,38 @@ macro_rules! impl_integer_for_isize {
488
582
assert_eq!( ( 11 as $T) . lcm( & 5 ) , 55 as $T) ;
489
583
}
490
584
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
+
491
617
#[ test]
492
618
fn test_even( ) {
493
619
assert_eq!( ( -4 as $T) . is_even( ) , true ) ;
@@ -569,14 +695,29 @@ macro_rules! impl_integer_for_usize {
569
695
m << shift
570
696
}
571
697
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
+
572
707
/// Calculates the Lowest Common Multiple (LCM) of the number and `other`.
573
708
#[ inline]
574
709
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)
580
721
}
581
722
582
723
/// Deprecated, use `is_multiple_of` instead.
@@ -672,6 +813,15 @@ macro_rules! impl_integer_for_usize {
672
813
assert_eq!( ( 15 as $T) . lcm( & 17 ) , 255 as $T) ;
673
814
}
674
815
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
+
675
825
#[ test]
676
826
fn test_is_multiple_of( ) {
677
827
assert!( ( 6 as $T) . is_multiple_of( & ( 6 as $T) ) ) ;
0 commit comments