gcd.rs 4.8 KB

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