Skip to content

Commit

Permalink
BigUint always use u64 as the internal machine unsigned integer
Browse files Browse the repository at this point in the history
This change allow a speedup of ~1.5 on shootout-pidigits on a i32
system.  `DoubleBigDigit` is used to abstract the internal
unsigned integer used in computation to simplity future
architecture specialization.

`BigDigit::from_uint` and `BigDigit::to_uint` become
`BigDigit::from_doublebigdigit` and `BigDigit::to_doublebigdigit`.

[breaking-change]
  • Loading branch information
TeXitoi committed May 1, 2014
1 parent 239557d commit 593f6a4
Showing 1 changed file with 41 additions and 166 deletions.
207 changes: 41 additions & 166 deletions src/libnum/bigint.rs
Expand Up @@ -31,50 +31,43 @@ use std::{i64, u64};

/**
A `BigDigit` is a `BigUint`'s composing element.
A `BigDigit` is half the size of machine word size.
*/
#[cfg(target_word_size = "32")]
pub type BigDigit = u16;
pub type BigDigit = u32;

/**
A `BigDigit` is a `BigUint`'s composing element.
A `BigDigit` is half the size of machine word size.
A `DoubleBigDigit` is the internal type used to do the computations. Its
size is the double of the size of `BigDigit`.
*/
#[cfg(target_word_size = "64")]
pub type BigDigit = u32;
pub type DoubleBigDigit = u64;

pub static ZERO_BIG_DIGIT: BigDigit = 0;
static ZERO_VEC: [BigDigit, ..1] = [ZERO_BIG_DIGIT];

pub mod BigDigit {
use super::BigDigit;
use super::DoubleBigDigit;

#[cfg(target_word_size = "32")]
pub static bits: uint = 16;

#[cfg(target_word_size = "64")]
// `DoubleBigDigit` size dependent
pub static bits: uint = 32;

pub static base: uint = 1 << bits;
static lo_mask: uint = (-1 as uint) >> bits;
pub static base: DoubleBigDigit = 1 << bits;
static lo_mask: DoubleBigDigit = (-1 as DoubleBigDigit) >> bits;

#[inline]
fn get_hi(n: uint) -> BigDigit { (n >> bits) as BigDigit }
fn get_hi(n: DoubleBigDigit) -> BigDigit { (n >> bits) as BigDigit }
#[inline]
fn get_lo(n: uint) -> BigDigit { (n & lo_mask) as BigDigit }
fn get_lo(n: DoubleBigDigit) -> BigDigit { (n & lo_mask) as BigDigit }

/// Split one machine sized unsigned integer into two `BigDigit`s.
/// Split one `DoubleBigDigit` into two `BigDigit`s.
#[inline]
pub fn from_uint(n: uint) -> (BigDigit, BigDigit) {
pub fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) {
(get_hi(n), get_lo(n))
}

/// Join two `BigDigit`s into one machine sized unsigned integer
/// Join two `BigDigit`s into one `DoubleBigDigit`
#[inline]
pub fn to_uint(hi: BigDigit, lo: BigDigit) -> uint {
(lo as uint) | ((hi as uint) << bits)
pub fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit {
(lo as DoubleBigDigit) | ((hi as DoubleBigDigit) << bits)
}
}

Expand Down Expand Up @@ -202,7 +195,8 @@ impl Add<BigUint, BigUint> for BigUint {

let mut carry = 0;
let mut sum: Vec<BigDigit> = a.data.iter().zip(b.data.iter().chain(zeros)).map(|(ai, bi)| {
let (hi, lo) = BigDigit::from_uint((*ai as uint) + (*bi as uint) + (carry as uint));
let (hi, lo) = BigDigit::from_doublebigdigit(
(*ai as DoubleBigDigit) + (*bi as DoubleBigDigit) + (carry as DoubleBigDigit));
carry = hi;
lo
}).collect();
Expand All @@ -219,8 +213,11 @@ impl Sub<BigUint, BigUint> for BigUint {

let mut borrow = 0;
let diff: Vec<BigDigit> = a.take(new_len).zip(b).map(|(ai, bi)| {
let (hi, lo) = BigDigit::from_uint(
BigDigit::base + (*ai as uint) - (*bi as uint) - (borrow as uint)
let (hi, lo) = BigDigit::from_doublebigdigit(
BigDigit::base
+ (*ai as DoubleBigDigit)
- (*bi as DoubleBigDigit)
- (borrow as DoubleBigDigit)
);
/*
hi * (base) + lo == 1*(base) + ai - bi - borrow
Expand Down Expand Up @@ -274,8 +271,8 @@ impl Mul<BigUint, BigUint> for BigUint {

let mut carry = 0;
let mut prod: Vec<BigDigit> = a.data.iter().map(|ai| {
let (hi, lo) = BigDigit::from_uint(
(*ai as uint) * (n as uint) + (carry as uint)
let (hi, lo) = BigDigit::from_doublebigdigit(
(*ai as DoubleBigDigit) * (n as DoubleBigDigit) + (carry as DoubleBigDigit)
);
carry = hi;
lo
Expand Down Expand Up @@ -440,10 +437,10 @@ impl Integer for BigUint {
let mut d = Vec::with_capacity(an.len());
let mut carry = 0;
for elt in an.iter().rev() {
let ai = BigDigit::to_uint(carry, *elt);
let di = ai / (bn as uint);
let ai = BigDigit::to_doublebigdigit(carry, *elt);
let di = ai / (bn as DoubleBigDigit);
assert!(di < BigDigit::base);
carry = (ai % (bn as uint)) as BigDigit;
carry = (ai % (bn as DoubleBigDigit)) as BigDigit;
d.push(di as BigDigit)
}
d.reverse();
Expand Down Expand Up @@ -515,39 +512,14 @@ impl ToPrimitive for BigUint {
})
}

#[cfg(target_word_size = "32")]
#[inline]
fn to_u64(&self) -> Option<u64> {
match self.data.len() {
0 => Some(0),
1 => Some(self.data.as_slice()[0] as u64),
2 => {
Some(BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0]) as u64)
}
3 => {
let n_lo = BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0]) as
u64;
let n_hi = self.data.as_slice()[2] as u64;
Some((n_hi << 32) + n_lo)
}
4 => {
let n_lo = BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0])
as u64;
let n_hi = BigDigit::to_uint(self.data.as_slice()[3], self.data.as_slice()[2])
as u64;
Some((n_hi << 32) + n_lo)
}
_ => None
}
}

#[cfg(target_word_size = "64")]
// `DoubleBigDigit` size dependent
#[inline]
fn to_u64(&self) -> Option<u64> {
match self.data.len() {
0 => Some(0),
1 => Some(self.data.as_slice()[0] as u64),
2 => Some(BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0]) as u64),
2 => Some(BigDigit::to_doublebigdigit(self.data.as_slice()[1], self.data.as_slice()[0])
as u64),
_ => None
}
}
Expand All @@ -565,26 +537,10 @@ impl FromPrimitive for BigUint {
}
}

#[cfg(target_word_size = "32")]
#[inline]
fn from_u64(n: u64) -> Option<BigUint> {
let n_lo = (n & 0x0000_0000_FFFF_FFFF) as uint;
let n_hi = (n >> 32) as uint;

let n = match (BigDigit::from_uint(n_hi), BigDigit::from_uint(n_lo)) {
((0, 0), (0, 0)) => Zero::zero(),
((0, 0), (0, n0)) => BigUint::new(vec!(n0)),
((0, 0), (n1, n0)) => BigUint::new(vec!(n0, n1)),
((0, n2), (n1, n0)) => BigUint::new(vec!(n0, n1, n2)),
((n3, n2), (n1, n0)) => BigUint::new(vec!(n0, n1, n2, n3)),
};
Some(n)
}

#[cfg(target_word_size = "64")]
// `DoubleBigDigit` size dependent
#[inline]
fn from_u64(n: u64) -> Option<BigUint> {
let n = match BigDigit::from_uint(n as uint) {
let n = match BigDigit::from_doublebigdigit(n) {
(0, 0) => Zero::zero(),
(0, n0) => BigUint::new(vec!(n0)),
(n1, n0) => BigUint::new(vec!(n0, n1))
Expand Down Expand Up @@ -650,8 +606,8 @@ impl ToStrRadix for BigUint {
}
return fill_concat(convert_base(self, base).as_slice(), radix, max_len);

fn convert_base(n: &BigUint, base: uint) -> Vec<BigDigit> {
let divider = FromPrimitive::from_uint(base).unwrap();
fn convert_base(n: &BigUint, base: DoubleBigDigit) -> Vec<BigDigit> {
let divider = base.to_biguint().unwrap();
let mut result = Vec::new();
let mut m = n.clone();
while m >= divider {
Expand Down Expand Up @@ -709,7 +665,7 @@ impl BigUint {
/// Creates and initializes a `BigUint`.
pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<BigUint> {
let (base, unit_len) = get_radix_base(radix);
let base_num = match FromPrimitive::from_uint(base) {
let base_num = match base.to_biguint() {
Some(base_num) => base_num,
None => { return None; }
};
Expand Down Expand Up @@ -756,8 +712,8 @@ impl BigUint {

let mut carry = 0;
let mut shifted: Vec<BigDigit> = self.data.iter().map(|elem| {
let (hi, lo) = BigDigit::from_uint(
(*elem as uint) << n_bits | (carry as uint)
let (hi, lo) = BigDigit::from_doublebigdigit(
(*elem as DoubleBigDigit) << n_bits | (carry as DoubleBigDigit)
);
carry = hi;
lo
Expand Down Expand Up @@ -797,33 +753,9 @@ impl BigUint {
}
}

#[cfg(target_word_size = "32")]
// `DoubleBigDigit` size dependent
#[inline]
fn get_radix_base(radix: uint) -> (uint, uint) {
assert!(1 < radix && radix <= 16);
match radix {
2 => (65536, 16),
3 => (59049, 10),
4 => (65536, 8),
5 => (15625, 6),
6 => (46656, 6),
7 => (16807, 5),
8 => (32768, 5),
9 => (59049, 5),
10 => (10000, 4),
11 => (14641, 4),
12 => (20736, 4),
13 => (28561, 4),
14 => (38416, 4),
15 => (50625, 4),
16 => (65536, 4),
_ => fail!()
}
}

#[cfg(target_word_size = "64")]
#[inline]
fn get_radix_base(radix: uint) -> (uint, uint) {
fn get_radix_base(radix: uint) -> (DoubleBigDigit, uint) {
assert!(1 < radix && radix <= 16);
match radix {
2 => (4294967296, 32),
Expand Down Expand Up @@ -1599,36 +1531,7 @@ mod biguint_tests {
"88887777666655554444333322221111");
}

#[cfg(target_word_size = "32")]
#[test]
fn test_convert_i64() {
fn check(b1: BigUint, i: i64) {
let b2: BigUint = FromPrimitive::from_i64(i).unwrap();
assert!(b1 == b2);
assert!(b1.to_i64().unwrap() == i);
}

check(Zero::zero(), 0);
check(One::one(), 1);
check(i64::MAX.to_biguint().unwrap(), i64::MAX);

check(BigUint::new(vec!( )), 0);
check(BigUint::new(vec!( 1 )), (1 << (0*BigDigit::bits)));
check(BigUint::new(vec!(-1 )), (1 << (1*BigDigit::bits)) - 1);
check(BigUint::new(vec!( 0, 1 )), (1 << (1*BigDigit::bits)));
check(BigUint::new(vec!(-1, -1 )), (1 << (2*BigDigit::bits)) - 1);
check(BigUint::new(vec!( 0, 0, 1 )), (1 << (2*BigDigit::bits)));
check(BigUint::new(vec!(-1, -1, -1 )), (1 << (3*BigDigit::bits)) - 1);
check(BigUint::new(vec!( 0, 0, 0, 1 )), (1 << (3*BigDigit::bits)));
check(BigUint::new(vec!(-1, -1, -1, -1 >> 1)), i64::MAX);

assert_eq!(i64::MIN.to_biguint(), None);
assert_eq!(BigUint::new(vec!(-1, -1, -1, -1 )).to_i64(), None);
assert_eq!(BigUint::new(vec!( 0, 0, 0, 0, 1)).to_i64(), None);
assert_eq!(BigUint::new(vec!(-1, -1, -1, -1, -1)).to_i64(), None);
}

#[cfg(target_word_size = "64")]
// `DoubleBigDigit` size dependent
#[test]
fn test_convert_i64() {
fn check(b1: BigUint, i: i64) {
Expand All @@ -1653,35 +1556,7 @@ mod biguint_tests {
assert_eq!(BigUint::new(vec!(-1, -1, -1)).to_i64(), None);
}

#[cfg(target_word_size = "32")]
#[test]
fn test_convert_u64() {
fn check(b1: BigUint, u: u64) {
let b2: BigUint = FromPrimitive::from_u64(u).unwrap();
assert!(b1 == b2);
assert!(b1.to_u64().unwrap() == u);
}

check(Zero::zero(), 0);
check(One::one(), 1);
check(u64::MIN.to_biguint().unwrap(), u64::MIN);
check(u64::MAX.to_biguint().unwrap(), u64::MAX);

check(BigUint::new(vec!( )), 0);
check(BigUint::new(vec!( 1 )), (1 << (0*BigDigit::bits)));
check(BigUint::new(vec!(-1 )), (1 << (1*BigDigit::bits)) - 1);
check(BigUint::new(vec!( 0, 1 )), (1 << (1*BigDigit::bits)));
check(BigUint::new(vec!(-1, -1 )), (1 << (2*BigDigit::bits)) - 1);
check(BigUint::new(vec!( 0, 0, 1 )), (1 << (2*BigDigit::bits)));
check(BigUint::new(vec!(-1, -1, -1 )), (1 << (3*BigDigit::bits)) - 1);
check(BigUint::new(vec!( 0, 0, 0, 1)), (1 << (3*BigDigit::bits)));
check(BigUint::new(vec!(-1, -1, -1, -1)), u64::MAX);

assert_eq!(BigUint::new(vec!( 0, 0, 0, 0, 1)).to_u64(), None);
assert_eq!(BigUint::new(vec!(-1, -1, -1, -1, -1)).to_u64(), None);
}

#[cfg(target_word_size = "64")]
// `DoubleBigDigit` size dependent
#[test]
fn test_convert_u64() {
fn check(b1: BigUint, u: u64) {
Expand Down

5 comments on commit 593f6a4

@bors
Copy link
Contributor

@bors bors commented on 593f6a4 May 3, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from pcwalton
at TeXitoi@593f6a4

@bors
Copy link
Contributor

@bors bors commented on 593f6a4 May 3, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging TeXitoi/rust/biguint-always-use-u64 = 593f6a4 into auto

@bors
Copy link
Contributor

@bors bors commented on 593f6a4 May 3, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TeXitoi/rust/biguint-always-use-u64 = 593f6a4 merged ok, testing candidate = 3a321b0

@bors
Copy link
Contributor

@bors bors commented on 593f6a4 May 3, 2014

@bors
Copy link
Contributor

@bors bors commented on 593f6a4 May 3, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 3a321b0

Please sign in to comment.