diff --git a/Cargo.toml b/Cargo.toml index d5a2d2c..42539d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ harness = false name = "shootout-pidigits" [dependencies] +smallvec = "0.6.7" [dependencies.num-integer] version = "0.1.39" @@ -57,4 +58,4 @@ version = "1.0" default = ["std", "i128", "u64_digit"] i128 = ["num-integer/i128", "num-traits/i128"] std = ["num-integer/std", "num-traits/std"] -u64_digit = [] \ No newline at end of file +u64_digit = [] diff --git a/src/algorithms.rs b/src/algorithms.rs index ea02594..14627d6 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -14,6 +14,9 @@ use bigint::Sign::{Minus, NoSign, Plus}; use big_digit::{self, BigDigit, DoubleBigDigit, SignedDoubleBigDigit}; +use super::super::VEC_SIZE; +use smallvec::SmallVec; + // Generic functions for add/subtract/multiply with carry/borrow: // Add with carry: @@ -181,12 +184,12 @@ pub fn sub_sign(a: &[BigDigit], b: &[BigDigit]) -> (Sign, BigUint) { match cmp_slice(a, b) { Greater => { - let mut a = a.to_vec(); + let mut a: SmallVec<[BigDigit; VEC_SIZE]> = a.into(); sub2(&mut a, b); (Plus, BigUint::new_native(a)) } Less => { - let mut b = b.to_vec(); + let mut b: SmallVec<[BigDigit; VEC_SIZE]> = b.into(); sub2(&mut b, a); (Minus, BigUint::new_native(b)) } @@ -313,7 +316,9 @@ fn mac3(acc: &mut [BigDigit], b: &[BigDigit], c: &[BigDigit]) { * appropriately here: x1.len() >= x0.len and y1.len() >= y0.len(): */ let len = x1.len() + y1.len() + 1; - let mut p = BigUint { data: vec![0; len] }; + let mut p = BigUint { + data: smallvec![0; len], + }; // p2 = x1 * y1 mac3(&mut p.data[..], x1, y1); @@ -478,7 +483,9 @@ fn mac3(acc: &mut [BigDigit], b: &[BigDigit], c: &[BigDigit]) { pub fn mul3(x: &[BigDigit], y: &[BigDigit]) -> BigUint { let len = x.len() + y.len() + 1; - let mut prod = BigUint { data: vec![0; len] }; + let mut prod = BigUint { + data: smallvec![0; len], + }; mac3(&mut prod.data[..], x, y); prod.normalized() @@ -499,10 +506,11 @@ pub fn div_rem(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { if u.is_zero() { return (Zero::zero(), Zero::zero()); } - if d.data == [1] { - return (u.clone(), Zero::zero()); - } if d.data.len() == 1 { + if d.data[0] == 1 { + return (u.clone(), Zero::zero()); + } + let (div, rem) = div_rem_digit(u.clone(), d.data[0]); return (div, rem.into()); } @@ -541,7 +549,7 @@ pub fn div_rem(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { let bn = *b.data.last().unwrap(); let q_len = a.data.len() - b.data.len() + 1; let mut q = BigUint { - data: vec![0; q_len], + data: smallvec![0; q_len], }; // We reuse the same temporary to avoid hitting the allocator in our inner loop - this is @@ -549,7 +557,7 @@ pub fn div_rem(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { // can be bigger). // let mut tmp = BigUint { - data: Vec::with_capacity(2), + data: SmallVec::with_capacity(2), }; for j in (0..q_len).rev() { @@ -613,7 +621,7 @@ pub fn biguint_shl(n: Cow, bits: usize) -> BigUint { 0 => n.into_owned().data, _ => { let len = n_unit + n.data.len() + 1; - let mut data = Vec::with_capacity(len); + let mut data = SmallVec::with_capacity(len); data.extend(repeat(0).take(n_unit)); data.extend(n.data.iter().cloned()); data @@ -642,12 +650,9 @@ pub fn biguint_shr(n: Cow, bits: usize) -> BigUint { if n_unit >= n.data.len() { return Zero::zero(); } - 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 mut data: SmallVec<[BigDigit; VEC_SIZE]> = match n { + Cow::Borrowed(n) => n.data[n_unit..].into(), + Cow::Owned(n) => n.data[n_unit..].into(), }; let n_bits = bits % big_digit::BITS; diff --git a/src/bigint.rs b/src/bigint.rs index 1edf427..f7dccbe 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -23,7 +23,10 @@ use traits::{ ToPrimitive, Zero, }; +use smallvec::SmallVec; + use self::Sign::{Minus, NoSign, Plus}; +use super::VEC_SIZE; use super::ParseBigIntError; use big_digit::{self, BigDigit, DoubleBigDigit}; @@ -261,7 +264,7 @@ impl<'a> Not for &'a BigInt { // + 1 & -ff = ...0 01 & ...f 01 = ...0 01 = + 1 // +ff & - 1 = ...0 ff & ...f ff = ...0 ff = +ff // answer is pos, has length of a -fn bitand_pos_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitand_pos_neg(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_b = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { let twos_b = negate_carry(bi, &mut carry_b); @@ -273,7 +276,7 @@ fn bitand_pos_neg(a: &mut Vec, b: &[BigDigit]) { // - 1 & +ff = ...f ff & ...0 ff = ...0 ff = +ff // -ff & + 1 = ...f 01 & ...0 01 = ...0 01 = + 1 // answer is pos, has length of b -fn bitand_neg_pos(a: &mut Vec, b: &[BigDigit]) { +fn bitand_neg_pos(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_a = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { let twos_a = negate_carry(*ai, &mut carry_a); @@ -292,7 +295,7 @@ fn bitand_neg_pos(a: &mut Vec, b: &[BigDigit]) { // -ff & - 1 = ...f 01 & ...f ff = ...f 01 = - ff // -ff & -fe = ...f 01 & ...f 02 = ...f 00 = -100 // answer is neg, has length of longest with a possible carry -fn bitand_neg_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitand_neg_neg(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_b = 1; let mut carry_and = 1; @@ -392,7 +395,7 @@ impl<'a> BitAndAssign<&'a BigInt> for BigInt { // + 1 | -ff = ...0 01 | ...f 01 = ...f 01 = -ff // +ff | - 1 = ...0 ff | ...f ff = ...f ff = - 1 // answer is neg, has length of b -fn bitor_pos_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitor_pos_neg(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_b = 1; let mut carry_or = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -417,7 +420,7 @@ fn bitor_pos_neg(a: &mut Vec, b: &[BigDigit]) { // - 1 | +ff = ...f ff | ...0 ff = ...f ff = - 1 // -ff | + 1 = ...f 01 | ...0 01 = ...f 01 = -ff // answer is neg, has length of a -fn bitor_neg_pos(a: &mut Vec, b: &[BigDigit]) { +fn bitor_neg_pos(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_or = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -439,7 +442,7 @@ fn bitor_neg_pos(a: &mut Vec, b: &[BigDigit]) { // - 1 | -ff = ...f ff | ...f 01 = ...f ff = -1 // -ff | - 1 = ...f 01 | ...f ff = ...f ff = -1 // answer is neg, has length of shortest -fn bitor_neg_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitor_neg_neg(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_b = 1; let mut carry_or = 1; @@ -523,7 +526,7 @@ impl<'a> BitOrAssign<&'a BigInt> for BigInt { // + 1 ^ -ff = ...0 01 ^ ...f 01 = ...f 00 = -100 // +ff ^ - 1 = ...0 ff ^ ...f ff = ...f 00 = -100 // answer is neg, has length of longest with a possible carry -fn bitxor_pos_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitxor_pos_neg(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_b = 1; let mut carry_xor = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -552,7 +555,7 @@ fn bitxor_pos_neg(a: &mut Vec, b: &[BigDigit]) { // - 1 ^ +ff = ...f ff ^ ...0 ff = ...f 00 = -100 // -ff ^ + 1 = ...f 01 ^ ...0 01 = ...f 00 = -100 // answer is neg, has length of longest with a possible carry -fn bitxor_neg_pos(a: &mut Vec, b: &[BigDigit]) { +fn bitxor_neg_pos(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_xor = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -581,7 +584,7 @@ fn bitxor_neg_pos(a: &mut Vec, b: &[BigDigit]) { // - 1 ^ -ff = ...f ff ^ ...f 01 = ...0 fe = +fe // -ff & - 1 = ...f 01 ^ ...f ff = ...0 fe = +fe // answer is pos, has length of longest -fn bitxor_neg_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitxor_neg_neg(a: &mut SmallVec<[BigDigit; VEC_SIZE]>, b: &[BigDigit]) { let mut carry_a = 1; let mut carry_b = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { @@ -2426,7 +2429,7 @@ impl IntDigits for BigInt { self.data.digits() } #[inline] - fn digits_mut(&mut self) -> &mut Vec { + fn digits_mut(&mut self) -> &mut SmallVec<[BigDigit; VEC_SIZE]> { self.data.digits_mut() } #[inline] diff --git a/src/bigrand.rs b/src/bigrand.rs index c2635a2..62a8135 100644 --- a/src/bigrand.rs +++ b/src/bigrand.rs @@ -40,7 +40,7 @@ impl RandBigInt for R { fn gen_biguint(&mut self, bit_size: usize) -> BigUint { use super::big_digit::BITS; let (digits, rem) = bit_size.div_rem(&BITS); - let mut data = vec![BigDigit::default(); digits + (rem > 0) as usize]; + let mut data = smallvec![BigDigit::default(); digits + (rem > 0) as usize]; // `fill_bytes` is faster than many `gen::` calls self.fill_bytes(data[..].as_byte_slice_mut()); // Swap bytes per the `Rng::fill` source. This might be diff --git a/src/biguint.rs b/src/biguint.rs index 937cb9d..8733aec 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -26,6 +26,8 @@ use traits::{ use big_digit::{self, BigDigit}; +use smallvec::SmallVec; + #[path = "algorithms.rs"] mod algorithms; #[path = "monty.rs"] @@ -36,6 +38,7 @@ use self::algorithms::{biguint_shl, biguint_shr}; use self::algorithms::{cmp_slice, fls, ilog2}; use self::algorithms::{div_rem, div_rem_digit, mac_with_carry, mul3, scalar_mul}; use self::monty::monty_modpow; +use super::VEC_SIZE; use UsizePromotion; @@ -44,7 +47,7 @@ use ParseBigIntError; /// A big unsigned integer type. #[derive(Clone, Debug, Hash)] pub struct BigUint { - pub data: Vec, + pub data: SmallVec<[BigDigit; VEC_SIZE]>, } impl PartialEq for BigUint { @@ -148,7 +151,7 @@ fn from_inexact_bitwise_digits_le(v: &[u8], bits: usize) -> BigUint { debug_assert!(v.iter().all(|&c| (c as BigDigit) < (1 << bits))); let big_digits = (v.len() * bits + big_digit::BITS - 1) / big_digit::BITS; - let mut data = Vec::with_capacity(big_digits); + let mut data = SmallVec::with_capacity(big_digits); let mut d = 0; let mut dbits = 0; // number of bits we currently have in d @@ -184,7 +187,7 @@ fn from_radix_digits_be(v: &[u8], radix: u32) -> BigUint { // Estimate how big the result will be, so we can pre-allocate it. let bits = (radix as f64).log2() * v.len() as f64; let big_digits = (bits / big_digit::BITS as f64).ceil(); - let mut data = Vec::with_capacity(big_digits as usize); + let mut data = SmallVec::with_capacity(big_digits as usize); let (base, power) = get_radix_base(radix); let radix = radix as BigDigit; @@ -1679,6 +1682,7 @@ impl FromPrimitive for BigUint { } } +#[cfg(not(u64_digit))] impl From for BigUint { #[inline] fn from(mut n: u64) -> Self { @@ -1694,6 +1698,14 @@ impl From for BigUint { } } +#[cfg(u64_digit)] +impl From for BigUint { + #[inline] + fn from(mut n: u64) -> Self { + BigUint::new_native(smallvec![n]) + } +} + #[cfg(has_i128)] impl From for BigUint { #[inline] @@ -1907,19 +1919,19 @@ pub fn to_str_radix_reversed(u: &BigUint, radix: u32) -> Vec { #[cfg(not(feature = "u64_digit"))] #[inline] -fn ensure_big_digit(raw: Vec) -> Vec { - raw +fn ensure_big_digit(raw: Vec) -> SmallVec<[BigDigit; VEC_SIZE]> { + raw.into() } #[cfg(feature = "u64_digit")] #[inline] -fn ensure_big_digit(raw: Vec) -> Vec { +fn ensure_big_digit(raw: Vec) -> SmallVec<[BigDigit; VEC_SIZE]> { ensure_big_digit_slice(&raw) } #[cfg(feature = "u64_digit")] #[inline] -fn ensure_big_digit_slice(raw: &[u32]) -> Vec { +fn ensure_big_digit_slice(raw: &[u32]) -> SmallVec<[BigDigit; VEC_SIZE]> { raw.chunks(2) .map(|chunk| { // raw could have odd length @@ -1945,7 +1957,7 @@ impl BigUint { /// /// The digits are in little-endian base matching `BigDigit`. #[inline] - pub fn new_native(digits: Vec) -> BigUint { + pub fn new_native(digits: SmallVec<[BigDigit; VEC_SIZE]>) -> BigUint { BigUint { data: digits }.normalized() } @@ -1962,7 +1974,7 @@ impl BigUint { /// The digits are in little-endian base matching `BigDigit` #[inline] pub fn from_slice_native(slice: &[BigDigit]) -> BigUint { - BigUint::new_native(slice.to_vec()) + BigUint::new_native(slice.into()) } pub fn get_limb(&self, i: usize) -> BigDigit { @@ -2340,7 +2352,7 @@ impl_product_iter_type!(BigUint); pub trait IntDigits { fn digits(&self) -> &[BigDigit]; - fn digits_mut(&mut self) -> &mut Vec; + fn digits_mut(&mut self) -> &mut SmallVec<[BigDigit; VEC_SIZE]>; fn normalize(&mut self); fn capacity(&self) -> usize; fn len(&self) -> usize; @@ -2352,7 +2364,7 @@ impl IntDigits for BigUint { &self.data } #[inline] - fn digits_mut(&mut self) -> &mut Vec { + fn digits_mut(&mut self) -> &mut SmallVec<[BigDigit; VEC_SIZE]> { &mut self.data } #[inline] @@ -3009,8 +3021,8 @@ fn get_radix_base(radix: u32) -> (BigDigit, usize) { #[cfg(not(feature = "u64_digit"))] #[test] fn test_from_slice() { - fn check(slice: &[u32], data: &[BigDigit]) { - assert_eq!(BigUint::from_slice(slice).data, data); + fn check(slice: &[BigDigit], data: &[BigDigit]) { + assert_eq!(&BigUint::from_slice(slice).data[..], data); } check(&[1], &[1]); check(&[0, 0, 0], &[]); @@ -3025,7 +3037,7 @@ fn test_from_slice() { fn test_from_slice() { fn check(slice: &[u32], data: &[BigDigit]) { assert_eq!( - BigUint::from_slice(slice).data, + &BigUint::from_slice(slice).data[..], data, "from {:?}, to {:?}", slice, @@ -3038,13 +3050,13 @@ fn test_from_slice() { check(&[1, 2, 0, 0], &[8_589_934_593]); check(&[0, 0, 1, 2], &[0, 8_589_934_593]); check(&[0, 0, 1, 2, 0, 0], &[0, 8_589_934_593]); - check(&[-1i32 as u32], &[(-1i32 as u32) as BigDigit]); + check(&[(-1i32 as u32) as BigDigit], &[(-1i32 as u32) as BigDigit]); } #[test] fn test_from_slice_native() { fn check(slice: &[BigDigit], data: &[BigDigit]) { - assert!(BigUint::from_slice_native(slice).data == data); + assert!(&BigUint::from_slice_native(slice).data[..] == data); } check(&[1], &[1]); check(&[0, 0, 0], &[]); @@ -3059,7 +3071,7 @@ fn test_assign_from_slice_native() { fn check(slice: &[BigDigit], data: &[BigDigit]) { let mut p = BigUint::from_slice_native(&[2627, 0, 9182, 42]); p.assign_from_slice_native(slice); - assert!(p.data == data); + assert!(&p.data[..] == data); } check(&[1], &[1]); check(&[0, 0, 0], &[]); diff --git a/src/lib.rs b/src/lib.rs index 68a6ab1..8ccdcfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,6 +85,9 @@ extern crate rand; #[cfg(feature = "serde")] extern crate serde; +#[macro_use] +extern crate smallvec; + extern crate num_integer as integer; extern crate num_traits as traits; @@ -165,6 +168,12 @@ pub use bigint::ToBigInt; #[cfg(feature = "rand")] pub use bigrand::{RandBigInt, RandomBits, UniformBigInt, UniformBigUint}; +#[cfg(not(feature = "u64_digit"))] +pub const VEC_SIZE: usize = 8; + +#[cfg(feature = "u64_digit")] +pub const VEC_SIZE: usize = 4; + mod big_digit { /// A `BigDigit` is a `BigUint`'s composing element. #[cfg(not(feature = "u64_digit"))] diff --git a/tests/biguint.rs b/tests/biguint.rs index 2c7cd0a..9c27b65 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -1,6 +1,8 @@ extern crate num_bigint_dig as num_bigint; extern crate num_integer; extern crate num_traits; +#[macro_use] +extern crate smallvec; use num_bigint::Sign::Plus; use num_bigint::{BigInt, ToBigInt}; @@ -1710,28 +1712,3 @@ fn test_pow() { #[cfg(has_i128)] check!(u128); } - -#[test] -fn dummy() { - let n = BigUint { - data: vec![2091882447511797859u64, 13026742510440650545u64], - }; - let e = BigUint { - data: vec![65537u64], - }; - let d = BigUint { - data: vec![3067126905460138833u64, 4701489854577608180u64], - }; - let p1 = BigUint { - data: vec![13836070790002140521u64], - }; - let p2 = BigUint { - data: vec![17367718686279877099u64], - }; - - println!("n: {:?}", n.to_bytes_le()); - println!("e: {:?}", e.to_bytes_le()); - println!("d: {:?}", d.to_bytes_le()); - println!("p1: {:?} {:?}", p1, p1.to_bytes_le()); - println!("p2: {:?} {:?}", p2, p2.to_bytes_le()); -}