1 //! Benchmark comparing the current GCD implemtation against an older one.
2 
3 #![feature(test)]
4 
5 extern crate num_integer;
6 extern crate num_traits;
7 extern crate test;
8 
9 use num_integer::Integer;
10 use num_traits::{AsPrimitive, Bounded, Signed};
11 use test::{black_box, Bencher};
12 
13 trait GcdOld: Integer {
gcd_old(&self, other: &Self) -> Self14     fn gcd_old(&self, other: &Self) -> Self;
15 }
16 
17 macro_rules! impl_gcd_old_for_isize {
18     ($T:ty) => {
19         impl GcdOld for $T {
20             /// Calculates the Greatest Common Divisor (GCD) of the number and
21             /// `other`. The result is always positive.
22             #[inline]
23             fn gcd_old(&self, other: &Self) -> Self {
24                 // Use Stein's algorithm
25                 let mut m = *self;
26                 let mut n = *other;
27                 if m == 0 || n == 0 {
28                     return (m | n).abs();
29                 }
30 
31                 // find common factors of 2
32                 let shift = (m | n).trailing_zeros();
33 
34                 // The algorithm needs positive numbers, but the minimum value
35                 // can't be represented as a positive one.
36                 // It's also a power of two, so the gcd can be
37                 // calculated by bitshifting in that case
38 
39                 // Assuming two's complement, the number created by the shift
40                 // is positive for all numbers except gcd = abs(min value)
41                 // The call to .abs() causes a panic in debug mode
42                 if m == Self::min_value() || n == Self::min_value() {
43                     return (1 << shift).abs();
44                 }
45 
46                 // guaranteed to be positive now, rest like unsigned algorithm
47                 m = m.abs();
48                 n = n.abs();
49 
50                 // divide n and m by 2 until odd
51                 // m inside loop
52                 n >>= n.trailing_zeros();
53 
54                 while m != 0 {
55                     m >>= m.trailing_zeros();
56                     if n > m {
57                         std::mem::swap(&mut n, &mut m)
58                     }
59                     m -= n;
60                 }
61 
62                 n << shift
63             }
64         }
65     };
66 }
67 
68 impl_gcd_old_for_isize!(i8);
69 impl_gcd_old_for_isize!(i16);
70 impl_gcd_old_for_isize!(i32);
71 impl_gcd_old_for_isize!(i64);
72 impl_gcd_old_for_isize!(isize);
73 impl_gcd_old_for_isize!(i128);
74 
75 macro_rules! impl_gcd_old_for_usize {
76     ($T:ty) => {
77         impl GcdOld for $T {
78             /// Calculates the Greatest Common Divisor (GCD) of the number and
79             /// `other`. The result is always positive.
80             #[inline]
81             fn gcd_old(&self, other: &Self) -> Self {
82                 // Use Stein's algorithm
83                 let mut m = *self;
84                 let mut n = *other;
85                 if m == 0 || n == 0 {
86                     return m | n;
87                 }
88 
89                 // find common factors of 2
90                 let shift = (m | n).trailing_zeros();
91 
92                 // divide n and m by 2 until odd
93                 // m inside loop
94                 n >>= n.trailing_zeros();
95 
96                 while m != 0 {
97                     m >>= m.trailing_zeros();
98                     if n > m {
99                         std::mem::swap(&mut n, &mut m)
100                     }
101                     m -= n;
102                 }
103 
104                 n << shift
105             }
106         }
107     };
108 }
109 
110 impl_gcd_old_for_usize!(u8);
111 impl_gcd_old_for_usize!(u16);
112 impl_gcd_old_for_usize!(u32);
113 impl_gcd_old_for_usize!(u64);
114 impl_gcd_old_for_usize!(usize);
115 impl_gcd_old_for_usize!(u128);
116 
117 /// Return an iterator that yields all Fibonacci numbers fitting into a u128.
fibonacci() -> impl Iterator<Item = u128>118 fn fibonacci() -> impl Iterator<Item = u128> {
119     (0..185).scan((0, 1), |&mut (ref mut a, ref mut b), _| {
120         let tmp = *a;
121         *a = *b;
122         *b += tmp;
123         Some(*b)
124     })
125 }
126 
run_bench<T: Integer + Bounded + Copy + 'static>(b: &mut Bencher, gcd: fn(&T, &T) -> T) where T: AsPrimitive<u128>, u128: AsPrimitive<T>,127 fn run_bench<T: Integer + Bounded + Copy + 'static>(b: &mut Bencher, gcd: fn(&T, &T) -> T)
128 where
129     T: AsPrimitive<u128>,
130     u128: AsPrimitive<T>,
131 {
132     let max_value: u128 = T::max_value().as_();
133     let pairs: Vec<(T, T)> = fibonacci()
134         .collect::<Vec<_>>()
135         .windows(2)
136         .filter(|&pair| pair[0] <= max_value && pair[1] <= max_value)
137         .map(|pair| (pair[0].as_(), pair[1].as_()))
138         .collect();
139     b.iter(|| {
140         for &(ref m, ref n) in &pairs {
141             black_box(gcd(m, n));
142         }
143     });
144 }
145 
146 macro_rules! bench_gcd {
147     ($T:ident) => {
148         mod $T {
149             use crate::{run_bench, GcdOld};
150             use num_integer::Integer;
151             use test::Bencher;
152 
153             #[bench]
154             fn bench_gcd(b: &mut Bencher) {
155                 run_bench(b, $T::gcd);
156             }
157 
158             #[bench]
159             fn bench_gcd_old(b: &mut Bencher) {
160                 run_bench(b, $T::gcd_old);
161             }
162         }
163     };
164 }
165 
166 bench_gcd!(u8);
167 bench_gcd!(u16);
168 bench_gcd!(u32);
169 bench_gcd!(u64);
170 bench_gcd!(u128);
171 
172 bench_gcd!(i8);
173 bench_gcd!(i16);
174 bench_gcd!(i32);
175 bench_gcd!(i64);
176 bench_gcd!(i128);
177