Browse Source

Auto merge of #205 - cuviper:bigint-sub, r=hauleth

bigint: allow `Sub` to work in-place on the RHS

A new Fibonacci benchmark demonstrates the improvement by using both
addition and subtraction in each iteration of the loop, like #200.

Before:

    test fib2_100          ... bench:       4,558 ns/iter (+/- 3,357)
    test fib2_1000         ... bench:      62,575 ns/iter (+/- 5,200)
    test fib2_10000        ... bench:   2,898,425 ns/iter (+/- 207,973)

After:

    test fib2_100          ... bench:       1,973 ns/iter (+/- 102)
    test fib2_1000         ... bench:      41,203 ns/iter (+/- 947)
    test fib2_10000        ... bench:   2,544,272 ns/iter (+/- 45,183)
Homu 8 years ago
parent
commit
7fcd5f7304
2 changed files with 64 additions and 1 deletions
  1. 28 0
      benches/bigint.rs
  2. 36 1
      bigint/src/lib.rs

+ 28 - 0
benches/bigint.rs

@@ -40,6 +40,7 @@ fn factorial(n: usize) -> BigUint {
     f
 }
 
+/// Compute Fibonacci numbers
 fn fib(n: usize) -> BigUint {
     let mut f0: BigUint = Zero::zero();
     let mut f1: BigUint = One::one();
@@ -50,6 +51,18 @@ fn fib(n: usize) -> BigUint {
     f0
 }
 
+/// Compute Fibonacci numbers with two ops per iteration
+/// (add and subtract, like issue #200)
+fn fib2(n: usize) -> BigUint {
+    let mut f0: BigUint = Zero::zero();
+    let mut f1: BigUint = One::one();
+    for _ in 0..n {
+        f1 = f1 + &f0;
+        f0 = &f1 - f0;
+    }
+    f0
+}
+
 #[bench]
 fn multiply_0(b: &mut Bencher) {
     multiply_bench(b, 1 << 8, 1 << 8);
@@ -100,6 +113,21 @@ fn fib_10000(b: &mut Bencher) {
     b.iter(|| fib(10000));
 }
 
+#[bench]
+fn fib2_100(b: &mut Bencher) {
+    b.iter(|| fib2(100));
+}
+
+#[bench]
+fn fib2_1000(b: &mut Bencher) {
+    b.iter(|| fib2(1000));
+}
+
+#[bench]
+fn fib2_10000(b: &mut Bencher) {
+    b.iter(|| fib2(10000));
+}
+
 #[bench]
 fn fac_to_string(b: &mut Bencher) {
     let fac = factorial(100);

+ 36 - 1
bigint/src/lib.rs

@@ -827,7 +827,8 @@ impl<'a> Add<&'a BigUint> for BigUint {
     }
 }
 
-forward_all_binop_to_val_ref!(impl Sub for BigUint, sub);
+forward_val_val_binop!(impl Sub for BigUint, sub);
+forward_ref_ref_binop!(impl Sub for BigUint, sub);
 
 fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
     let mut borrow = 0;
@@ -861,6 +862,40 @@ impl<'a> Sub<&'a BigUint> for BigUint {
     }
 }
 
+fn sub2rev(a: &[BigDigit], b: &mut [BigDigit]) {
+    debug_assert!(b.len() >= a.len());
+
+    let mut borrow = 0;
+
+    let len = cmp::min(a.len(), b.len());
+    let (a_lo, a_hi) = a.split_at(len);
+    let (b_lo, b_hi) = b.split_at_mut(len);
+
+    for (a, b) in a_lo.iter().zip(b_lo) {
+        *b = sbb(*a, *b, &mut borrow);
+    }
+
+    assert!(a_hi.is_empty());
+
+    // note: we're _required_ to fail on underflow
+    assert!(borrow == 0 && b_hi.iter().all(|x| *x == 0),
+            "Cannot subtract b from a because b is larger than a.");
+}
+
+impl<'a> Sub<BigUint> for &'a BigUint {
+    type Output = BigUint;
+
+    fn sub(self, mut other: BigUint) -> BigUint {
+        if other.data.len() < self.data.len() {
+            let extra = self.data.len() - other.data.len();
+            other.data.extend(repeat(0).take(extra));
+        }
+
+        sub2rev(&self.data[..], &mut other.data[..]);
+        other.normalize()
+    }
+}
+
 fn sub_sign(a: &[BigDigit], b: &[BigDigit]) -> BigInt {
     // Normalize:
     let a = &a[..a.iter().rposition(|&x| x != 0).map_or(0, |i| i + 1)];