From 388a3132b8bcc7c5200bcdbd4889ad04f822ba0a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 8 Jul 2016 17:34:12 -0700 Subject: [PATCH] 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) --- benches/bigint.rs | 28 ++++++++++++++++++++++++++++ bigint/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/benches/bigint.rs b/benches/bigint.rs index 4f2b886..30522d5 100644 --- a/benches/bigint.rs +++ b/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); diff --git a/bigint/src/lib.rs b/bigint/src/lib.rs index 195f02d..616389e 100644 --- a/bigint/src/lib.rs +++ b/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 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)];