From b2ff86b6c9ba6e054aece88704114dbae615bb6b Mon Sep 17 00:00:00 2001 From: Emerentius Date: Sun, 7 Jan 2018 00:57:40 +0100 Subject: [PATCH 1/7] allow_unused AsciiExt the methods are implemented on the types directly since rust 1.23 the trait's still needed for backwards compatibility --- src/bigint.rs | 1 + src/biguint.rs | 1 + src/tests/biguint.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/bigint.rs b/src/bigint.rs index bf55b895..29d97c5d 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -4,6 +4,7 @@ use std::str::{self, FromStr}; use std::fmt; use std::cmp::Ordering::{self, Less, Greater, Equal}; use std::{i64, u64}; +#[allow(unused)] use std::ascii::AsciiExt; #[cfg(feature = "serde")] diff --git a/src/biguint.rs b/src/biguint.rs index df551bac..cbcea74a 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -10,6 +10,7 @@ use std::cmp; use std::cmp::Ordering::{self, Less, Greater, Equal}; use std::{f32, f64}; use std::{u8, u64}; +#[allow(unused)] use std::ascii::AsciiExt; #[cfg(feature = "serde")] diff --git a/src/tests/biguint.rs b/src/tests/biguint.rs index e27cc175..2a61efb4 100644 --- a/src/tests/biguint.rs +++ b/src/tests/biguint.rs @@ -1569,6 +1569,7 @@ fn test_from_str_radix() { #[test] fn test_all_str_radix() { + #[allow(unused)] use std::ascii::AsciiExt; let n = BigUint::new((0..10).collect()); From ba54f17366f2a68f7cf1719e1d254d46fafc25fc Mon Sep 17 00:00:00 2001 From: Emerentius Date: Sun, 7 Jan 2018 00:59:21 +0100 Subject: [PATCH 2/7] implement Stein's algorithm for gcd same asymptotic complexity as euclidean but faster thanks to bitshifts and subtractions rather than division --- src/biguint.rs | 48 +++++++++++++++++++++++++++++++++++++++++------- src/lib.rs | 1 + 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/biguint.rs b/src/biguint.rs index cbcea74a..30902e87 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -941,16 +941,38 @@ impl Integer for BigUint { /// /// The result is always positive. #[inline] - fn gcd(&self, other: &BigUint) -> BigUint { - // Use Euclid's algorithm + fn gcd(&self, other: &Self) -> Self { + // Stein's algorithm + if self.is_zero() { + return (*other).clone(); + } + if other.is_zero() { + return (*self).clone(); + } let mut m = (*self).clone(); let mut n = (*other).clone(); - while !m.is_zero() { - let temp = m; - m = n % &temp; - n = temp; + + // find common factors of 2 + let shift = ::core::cmp::min( + n.trailing_zeros(), + m.trailing_zeros() + ); + + // divide m and n by 2 until odd + // m inside loop + n >>= n.trailing_zeros(); + + loop { + m >>= m.trailing_zeros(); + if n > m { ::core::mem::swap(&mut n, &mut m) } + m -= &n; + + if m.is_zero() { + break + } } - return n; + + n << shift } /// Calculates the Lowest Common Multiple (LCM) of the number and `other`. @@ -1608,6 +1630,18 @@ impl BigUint { return self.data.len() * big_digit::BITS - zeros as usize; } + // self is assumed to be normalized + fn trailing_zeros(&self) -> usize { + let mut zeros = 0; + for &bigdigit in self.data.iter() { + zeros += bigdigit.trailing_zeros() as usize; + if bigdigit != 0 { + break + } + } + zeros + } + /// Strips off trailing zero bigdigits - comparisons require the last element in the vector to /// be nonzero. #[inline] diff --git a/src/lib.rs b/src/lib.rs index deae08de..c232c70c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,6 +78,7 @@ extern crate rand; extern crate rustc_serialize; #[cfg(feature = "serde")] extern crate serde; +extern crate core; extern crate num_integer as integer; extern crate num_traits as traits; From 8893408c30662713e7ea2664634880b8e053753b Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 7 Feb 2018 21:56:28 -0800 Subject: [PATCH 3/7] add a gcd benchmark --- Cargo.toml | 3 ++ benches/gcd.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100755 benches/gcd.rs diff --git a/Cargo.toml b/Cargo.toml index 9eff6556..241e6af5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ readme = "README.md" [[bench]] name = "bigint" +[[bench]] +name = "gcd" + [[bench]] harness = false name = "shootout-pidigits" diff --git a/benches/gcd.rs b/benches/gcd.rs new file mode 100755 index 00000000..695aee8f --- /dev/null +++ b/benches/gcd.rs @@ -0,0 +1,84 @@ +#![feature(test)] + +extern crate test; +extern crate num_bigint; +extern crate num_integer; +extern crate num_traits; +extern crate rand; + +use test::Bencher; +use num_bigint::{BigUint, RandBigInt}; +use num_integer::Integer; +use num_traits::Zero; +use rand::{SeedableRng, StdRng}; + +fn get_rng() -> StdRng { + let seed: &[_] = &[1, 2, 3, 4]; + SeedableRng::from_seed(seed) +} + +fn bench(b: &mut Bencher, bits: usize, gcd: fn(&BigUint, &BigUint) -> BigUint) { + let mut rng = get_rng(); + let x = rng.gen_biguint(bits); + let y = rng.gen_biguint(bits); + + assert_eq!(euclid(&x, &y), x.gcd(&y)); + + b.iter(|| gcd(&x, &y)); +} + + +fn euclid(x: &BigUint, y: &BigUint) -> BigUint { + // Use Euclid's algorithm + let mut m = x.clone(); + let mut n = y.clone(); + while !m.is_zero() { + let temp = m; + m = n % &temp; + n = temp; + } + return n; +} + +#[bench] +fn gcd_euclid_0064(b: &mut Bencher) { + bench(b, 64, euclid); +} + +#[bench] +fn gcd_euclid_0256(b: &mut Bencher) { + bench(b, 256, euclid); +} + +#[bench] +fn gcd_euclid_1024(b: &mut Bencher) { + bench(b, 1024, euclid); +} + +#[bench] +fn gcd_euclid_4096(b: &mut Bencher) { + bench(b, 4096, euclid); +} + + +// Integer for BigUint now uses Stein for gcd + +#[bench] +fn gcd_stein_0064(b: &mut Bencher) { + bench(b, 64, BigUint::gcd); +} + +#[bench] +fn gcd_stein_0256(b: &mut Bencher) { + bench(b, 256, BigUint::gcd); +} + +#[bench] +fn gcd_stein_1024(b: &mut Bencher) { + bench(b, 1024, BigUint::gcd); +} + +#[bench] +fn gcd_stein_4096(b: &mut Bencher) { + bench(b, 4096, BigUint::gcd); +} From a46f4224126d30d858e20f594bed0d4d5b4e7abf Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 7 Feb 2018 21:57:41 -0800 Subject: [PATCH 4/7] Remove unnecessary core import --- src/biguint.rs | 5 +++-- src/lib.rs | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biguint.rs b/src/biguint.rs index 30902e87..1c991ffe 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -7,6 +7,7 @@ use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub, use std::str::{self, FromStr}; use std::fmt; use std::cmp; +use std::mem; use std::cmp::Ordering::{self, Less, Greater, Equal}; use std::{f32, f64}; use std::{u8, u64}; @@ -953,7 +954,7 @@ impl Integer for BigUint { let mut n = (*other).clone(); // find common factors of 2 - let shift = ::core::cmp::min( + let shift = cmp::min( n.trailing_zeros(), m.trailing_zeros() ); @@ -964,7 +965,7 @@ impl Integer for BigUint { loop { m >>= m.trailing_zeros(); - if n > m { ::core::mem::swap(&mut n, &mut m) } + if n > m { mem::swap(&mut n, &mut m) } m -= &n; if m.is_zero() { diff --git a/src/lib.rs b/src/lib.rs index c232c70c..deae08de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,6 @@ extern crate rand; extern crate rustc_serialize; #[cfg(feature = "serde")] extern crate serde; -extern crate core; extern crate num_integer as integer; extern crate num_traits as traits; From f5c05461d72be74e7760fddcf07ec34a4d92d91a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 7 Feb 2018 21:59:13 -0800 Subject: [PATCH 5/7] use an iterator for trailing_zeros --- src/biguint.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/biguint.rs b/src/biguint.rs index 1c991ffe..5677e8f3 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -1633,14 +1633,12 @@ impl BigUint { // self is assumed to be normalized fn trailing_zeros(&self) -> usize { - let mut zeros = 0; - for &bigdigit in self.data.iter() { - zeros += bigdigit.trailing_zeros() as usize; - if bigdigit != 0 { - break - } - } - zeros + self.data + .iter() + .enumerate() + .find(|&(_, &digit)| digit != 0) + .map(|(i, digit)| i * big_digit::BITS + digit.trailing_zeros() as usize) + .unwrap_or(0) } /// Strips off trailing zero bigdigits - comparisons require the last element in the vector to From 4714f829df77613d0dd29aec9c6cd5b3e7bf2a02 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 7 Feb 2018 21:59:31 -0800 Subject: [PATCH 6/7] small gcd cleanups --- src/biguint.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/biguint.rs b/src/biguint.rs index 5677e8f3..45dd62be 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -945,13 +945,13 @@ impl Integer for BigUint { fn gcd(&self, other: &Self) -> Self { // Stein's algorithm if self.is_zero() { - return (*other).clone(); + return other.clone(); } if other.is_zero() { - return (*self).clone(); + return self.clone(); } - let mut m = (*self).clone(); - let mut n = (*other).clone(); + let mut m = self.clone(); + let mut n = other.clone(); // find common factors of 2 let shift = cmp::min( @@ -963,14 +963,10 @@ impl Integer for BigUint { // m inside loop n >>= n.trailing_zeros(); - loop { + while !m.is_zero() { m >>= m.trailing_zeros(); if n > m { mem::swap(&mut n, &mut m) } m -= &n; - - if m.is_zero() { - break - } } n << shift From e45b2b7d0c0fdcb1cdcb85ffc03a79454d10eb0d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 7 Feb 2018 22:19:20 -0800 Subject: [PATCH 7/7] reduce allocations in shr --- src/algorithms.rs | 9 ++++++--- src/biguint.rs | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/algorithms.rs b/src/algorithms.rs index 5b745393..e65a778e 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -591,9 +591,12 @@ pub fn biguint_shr(n: Cow, bits: usize) -> BigUint { if n_unit >= n.data.len() { return Zero::zero(); } - let mut data = match n_unit { - 0 => n.into_owned().data, - _ => n.data[n_unit..].to_vec(), + let mut data = match n { + Cow::Borrowed(n) => n.data[n_unit..].to_vec(), + Cow::Owned(mut n) => { + n.data.drain(..n_unit); + n.data + } }; let n_bits = bits % big_digit::BITS; diff --git a/src/biguint.rs b/src/biguint.rs index 45dd62be..eebfc455 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -398,7 +398,8 @@ impl<'a> Shr for &'a BigUint { impl ShrAssign for BigUint { #[inline] fn shr_assign(&mut self, rhs: usize) { - *self = biguint_shr(Cow::Borrowed(&*self), rhs); + let n = mem::replace(self, BigUint::zero()); + *self = n >> rhs; } }