Skip to content

Commit 593f6a4

Browse files
committed
BigUint always use u64 as the internal machine unsigned integer
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]
1 parent 239557d commit 593f6a4

File tree

1 file changed

+41
-166
lines changed

1 file changed

+41
-166
lines changed

src/libnum/bigint.rs

+41-166
Original file line numberDiff line numberDiff line change
@@ -31,50 +31,43 @@ use std::{i64, u64};
3131

3232
/**
3333
A `BigDigit` is a `BigUint`'s composing element.
34-
35-
A `BigDigit` is half the size of machine word size.
3634
*/
37-
#[cfg(target_word_size = "32")]
38-
pub type BigDigit = u16;
35+
pub type BigDigit = u32;
3936

4037
/**
41-
A `BigDigit` is a `BigUint`'s composing element.
42-
43-
A `BigDigit` is half the size of machine word size.
38+
A `DoubleBigDigit` is the internal type used to do the computations. Its
39+
size is the double of the size of `BigDigit`.
4440
*/
45-
#[cfg(target_word_size = "64")]
46-
pub type BigDigit = u32;
41+
pub type DoubleBigDigit = u64;
4742

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

5146
pub mod BigDigit {
5247
use super::BigDigit;
48+
use super::DoubleBigDigit;
5349

54-
#[cfg(target_word_size = "32")]
55-
pub static bits: uint = 16;
56-
57-
#[cfg(target_word_size = "64")]
50+
// `DoubleBigDigit` size dependent
5851
pub static bits: uint = 32;
5952

60-
pub static base: uint = 1 << bits;
61-
static lo_mask: uint = (-1 as uint) >> bits;
53+
pub static base: DoubleBigDigit = 1 << bits;
54+
static lo_mask: DoubleBigDigit = (-1 as DoubleBigDigit) >> bits;
6255

6356
#[inline]
64-
fn get_hi(n: uint) -> BigDigit { (n >> bits) as BigDigit }
57+
fn get_hi(n: DoubleBigDigit) -> BigDigit { (n >> bits) as BigDigit }
6558
#[inline]
66-
fn get_lo(n: uint) -> BigDigit { (n & lo_mask) as BigDigit }
59+
fn get_lo(n: DoubleBigDigit) -> BigDigit { (n & lo_mask) as BigDigit }
6760

68-
/// Split one machine sized unsigned integer into two `BigDigit`s.
61+
/// Split one `DoubleBigDigit` into two `BigDigit`s.
6962
#[inline]
70-
pub fn from_uint(n: uint) -> (BigDigit, BigDigit) {
63+
pub fn from_doublebigdigit(n: DoubleBigDigit) -> (BigDigit, BigDigit) {
7164
(get_hi(n), get_lo(n))
7265
}
7366

74-
/// Join two `BigDigit`s into one machine sized unsigned integer
67+
/// Join two `BigDigit`s into one `DoubleBigDigit`
7568
#[inline]
76-
pub fn to_uint(hi: BigDigit, lo: BigDigit) -> uint {
77-
(lo as uint) | ((hi as uint) << bits)
69+
pub fn to_doublebigdigit(hi: BigDigit, lo: BigDigit) -> DoubleBigDigit {
70+
(lo as DoubleBigDigit) | ((hi as DoubleBigDigit) << bits)
7871
}
7972
}
8073

@@ -202,7 +195,8 @@ impl Add<BigUint, BigUint> for BigUint {
202195

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

220214
let mut borrow = 0;
221215
let diff: Vec<BigDigit> = a.take(new_len).zip(b).map(|(ai, bi)| {
222-
let (hi, lo) = BigDigit::from_uint(
223-
BigDigit::base + (*ai as uint) - (*bi as uint) - (borrow as uint)
216+
let (hi, lo) = BigDigit::from_doublebigdigit(
217+
BigDigit::base
218+
+ (*ai as DoubleBigDigit)
219+
- (*bi as DoubleBigDigit)
220+
- (borrow as DoubleBigDigit)
224221
);
225222
/*
226223
hi * (base) + lo == 1*(base) + ai - bi - borrow
@@ -274,8 +271,8 @@ impl Mul<BigUint, BigUint> for BigUint {
274271

275272
let mut carry = 0;
276273
let mut prod: Vec<BigDigit> = a.data.iter().map(|ai| {
277-
let (hi, lo) = BigDigit::from_uint(
278-
(*ai as uint) * (n as uint) + (carry as uint)
274+
let (hi, lo) = BigDigit::from_doublebigdigit(
275+
(*ai as DoubleBigDigit) * (n as DoubleBigDigit) + (carry as DoubleBigDigit)
279276
);
280277
carry = hi;
281278
lo
@@ -440,10 +437,10 @@ impl Integer for BigUint {
440437
let mut d = Vec::with_capacity(an.len());
441438
let mut carry = 0;
442439
for elt in an.iter().rev() {
443-
let ai = BigDigit::to_uint(carry, *elt);
444-
let di = ai / (bn as uint);
440+
let ai = BigDigit::to_doublebigdigit(carry, *elt);
441+
let di = ai / (bn as DoubleBigDigit);
445442
assert!(di < BigDigit::base);
446-
carry = (ai % (bn as uint)) as BigDigit;
443+
carry = (ai % (bn as DoubleBigDigit)) as BigDigit;
447444
d.push(di as BigDigit)
448445
}
449446
d.reverse();
@@ -515,39 +512,14 @@ impl ToPrimitive for BigUint {
515512
})
516513
}
517514

518-
#[cfg(target_word_size = "32")]
519-
#[inline]
520-
fn to_u64(&self) -> Option<u64> {
521-
match self.data.len() {
522-
0 => Some(0),
523-
1 => Some(self.data.as_slice()[0] as u64),
524-
2 => {
525-
Some(BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0]) as u64)
526-
}
527-
3 => {
528-
let n_lo = BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0]) as
529-
u64;
530-
let n_hi = self.data.as_slice()[2] as u64;
531-
Some((n_hi << 32) + n_lo)
532-
}
533-
4 => {
534-
let n_lo = BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0])
535-
as u64;
536-
let n_hi = BigDigit::to_uint(self.data.as_slice()[3], self.data.as_slice()[2])
537-
as u64;
538-
Some((n_hi << 32) + n_lo)
539-
}
540-
_ => None
541-
}
542-
}
543-
544-
#[cfg(target_word_size = "64")]
515+
// `DoubleBigDigit` size dependent
545516
#[inline]
546517
fn to_u64(&self) -> Option<u64> {
547518
match self.data.len() {
548519
0 => Some(0),
549520
1 => Some(self.data.as_slice()[0] as u64),
550-
2 => Some(BigDigit::to_uint(self.data.as_slice()[1], self.data.as_slice()[0]) as u64),
521+
2 => Some(BigDigit::to_doublebigdigit(self.data.as_slice()[1], self.data.as_slice()[0])
522+
as u64),
551523
_ => None
552524
}
553525
}
@@ -565,26 +537,10 @@ impl FromPrimitive for BigUint {
565537
}
566538
}
567539

568-
#[cfg(target_word_size = "32")]
569-
#[inline]
570-
fn from_u64(n: u64) -> Option<BigUint> {
571-
let n_lo = (n & 0x0000_0000_FFFF_FFFF) as uint;
572-
let n_hi = (n >> 32) as uint;
573-
574-
let n = match (BigDigit::from_uint(n_hi), BigDigit::from_uint(n_lo)) {
575-
((0, 0), (0, 0)) => Zero::zero(),
576-
((0, 0), (0, n0)) => BigUint::new(vec!(n0)),
577-
((0, 0), (n1, n0)) => BigUint::new(vec!(n0, n1)),
578-
((0, n2), (n1, n0)) => BigUint::new(vec!(n0, n1, n2)),
579-
((n3, n2), (n1, n0)) => BigUint::new(vec!(n0, n1, n2, n3)),
580-
};
581-
Some(n)
582-
}
583-
584-
#[cfg(target_word_size = "64")]
540+
// `DoubleBigDigit` size dependent
585541
#[inline]
586542
fn from_u64(n: u64) -> Option<BigUint> {
587-
let n = match BigDigit::from_uint(n as uint) {
543+
let n = match BigDigit::from_doublebigdigit(n) {
588544
(0, 0) => Zero::zero(),
589545
(0, n0) => BigUint::new(vec!(n0)),
590546
(n1, n0) => BigUint::new(vec!(n0, n1))
@@ -650,8 +606,8 @@ impl ToStrRadix for BigUint {
650606
}
651607
return fill_concat(convert_base(self, base).as_slice(), radix, max_len);
652608

653-
fn convert_base(n: &BigUint, base: uint) -> Vec<BigDigit> {
654-
let divider = FromPrimitive::from_uint(base).unwrap();
609+
fn convert_base(n: &BigUint, base: DoubleBigDigit) -> Vec<BigDigit> {
610+
let divider = base.to_biguint().unwrap();
655611
let mut result = Vec::new();
656612
let mut m = n.clone();
657613
while m >= divider {
@@ -709,7 +665,7 @@ impl BigUint {
709665
/// Creates and initializes a `BigUint`.
710666
pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<BigUint> {
711667
let (base, unit_len) = get_radix_base(radix);
712-
let base_num = match FromPrimitive::from_uint(base) {
668+
let base_num = match base.to_biguint() {
713669
Some(base_num) => base_num,
714670
None => { return None; }
715671
};
@@ -756,8 +712,8 @@ impl BigUint {
756712

757713
let mut carry = 0;
758714
let mut shifted: Vec<BigDigit> = self.data.iter().map(|elem| {
759-
let (hi, lo) = BigDigit::from_uint(
760-
(*elem as uint) << n_bits | (carry as uint)
715+
let (hi, lo) = BigDigit::from_doublebigdigit(
716+
(*elem as DoubleBigDigit) << n_bits | (carry as DoubleBigDigit)
761717
);
762718
carry = hi;
763719
lo
@@ -797,33 +753,9 @@ impl BigUint {
797753
}
798754
}
799755

800-
#[cfg(target_word_size = "32")]
756+
// `DoubleBigDigit` size dependent
801757
#[inline]
802-
fn get_radix_base(radix: uint) -> (uint, uint) {
803-
assert!(1 < radix && radix <= 16);
804-
match radix {
805-
2 => (65536, 16),
806-
3 => (59049, 10),
807-
4 => (65536, 8),
808-
5 => (15625, 6),
809-
6 => (46656, 6),
810-
7 => (16807, 5),
811-
8 => (32768, 5),
812-
9 => (59049, 5),
813-
10 => (10000, 4),
814-
11 => (14641, 4),
815-
12 => (20736, 4),
816-
13 => (28561, 4),
817-
14 => (38416, 4),
818-
15 => (50625, 4),
819-
16 => (65536, 4),
820-
_ => fail!()
821-
}
822-
}
823-
824-
#[cfg(target_word_size = "64")]
825-
#[inline]
826-
fn get_radix_base(radix: uint) -> (uint, uint) {
758+
fn get_radix_base(radix: uint) -> (DoubleBigDigit, uint) {
827759
assert!(1 < radix && radix <= 16);
828760
match radix {
829761
2 => (4294967296, 32),
@@ -1599,36 +1531,7 @@ mod biguint_tests {
15991531
"88887777666655554444333322221111");
16001532
}
16011533

1602-
#[cfg(target_word_size = "32")]
1603-
#[test]
1604-
fn test_convert_i64() {
1605-
fn check(b1: BigUint, i: i64) {
1606-
let b2: BigUint = FromPrimitive::from_i64(i).unwrap();
1607-
assert!(b1 == b2);
1608-
assert!(b1.to_i64().unwrap() == i);
1609-
}
1610-
1611-
check(Zero::zero(), 0);
1612-
check(One::one(), 1);
1613-
check(i64::MAX.to_biguint().unwrap(), i64::MAX);
1614-
1615-
check(BigUint::new(vec!( )), 0);
1616-
check(BigUint::new(vec!( 1 )), (1 << (0*BigDigit::bits)));
1617-
check(BigUint::new(vec!(-1 )), (1 << (1*BigDigit::bits)) - 1);
1618-
check(BigUint::new(vec!( 0, 1 )), (1 << (1*BigDigit::bits)));
1619-
check(BigUint::new(vec!(-1, -1 )), (1 << (2*BigDigit::bits)) - 1);
1620-
check(BigUint::new(vec!( 0, 0, 1 )), (1 << (2*BigDigit::bits)));
1621-
check(BigUint::new(vec!(-1, -1, -1 )), (1 << (3*BigDigit::bits)) - 1);
1622-
check(BigUint::new(vec!( 0, 0, 0, 1 )), (1 << (3*BigDigit::bits)));
1623-
check(BigUint::new(vec!(-1, -1, -1, -1 >> 1)), i64::MAX);
1624-
1625-
assert_eq!(i64::MIN.to_biguint(), None);
1626-
assert_eq!(BigUint::new(vec!(-1, -1, -1, -1 )).to_i64(), None);
1627-
assert_eq!(BigUint::new(vec!( 0, 0, 0, 0, 1)).to_i64(), None);
1628-
assert_eq!(BigUint::new(vec!(-1, -1, -1, -1, -1)).to_i64(), None);
1629-
}
1630-
1631-
#[cfg(target_word_size = "64")]
1534+
// `DoubleBigDigit` size dependent
16321535
#[test]
16331536
fn test_convert_i64() {
16341537
fn check(b1: BigUint, i: i64) {
@@ -1653,35 +1556,7 @@ mod biguint_tests {
16531556
assert_eq!(BigUint::new(vec!(-1, -1, -1)).to_i64(), None);
16541557
}
16551558

1656-
#[cfg(target_word_size = "32")]
1657-
#[test]
1658-
fn test_convert_u64() {
1659-
fn check(b1: BigUint, u: u64) {
1660-
let b2: BigUint = FromPrimitive::from_u64(u).unwrap();
1661-
assert!(b1 == b2);
1662-
assert!(b1.to_u64().unwrap() == u);
1663-
}
1664-
1665-
check(Zero::zero(), 0);
1666-
check(One::one(), 1);
1667-
check(u64::MIN.to_biguint().unwrap(), u64::MIN);
1668-
check(u64::MAX.to_biguint().unwrap(), u64::MAX);
1669-
1670-
check(BigUint::new(vec!( )), 0);
1671-
check(BigUint::new(vec!( 1 )), (1 << (0*BigDigit::bits)));
1672-
check(BigUint::new(vec!(-1 )), (1 << (1*BigDigit::bits)) - 1);
1673-
check(BigUint::new(vec!( 0, 1 )), (1 << (1*BigDigit::bits)));
1674-
check(BigUint::new(vec!(-1, -1 )), (1 << (2*BigDigit::bits)) - 1);
1675-
check(BigUint::new(vec!( 0, 0, 1 )), (1 << (2*BigDigit::bits)));
1676-
check(BigUint::new(vec!(-1, -1, -1 )), (1 << (3*BigDigit::bits)) - 1);
1677-
check(BigUint::new(vec!( 0, 0, 0, 1)), (1 << (3*BigDigit::bits)));
1678-
check(BigUint::new(vec!(-1, -1, -1, -1)), u64::MAX);
1679-
1680-
assert_eq!(BigUint::new(vec!( 0, 0, 0, 0, 1)).to_u64(), None);
1681-
assert_eq!(BigUint::new(vec!(-1, -1, -1, -1, -1)).to_u64(), None);
1682-
}
1683-
1684-
#[cfg(target_word_size = "64")]
1559+
// `DoubleBigDigit` size dependent
16851560
#[test]
16861561
fn test_convert_u64() {
16871562
fn check(b1: BigUint, u: u64) {

0 commit comments

Comments
 (0)