Skip to content

Commit e323749

Browse files
committed
extra: Don't truncate {u64,i64} when converting to BigInts
1 parent cb24019 commit e323749

File tree

1 file changed

+121
-19
lines changed

1 file changed

+121
-19
lines changed

src/libextra/num/bigint.rs

Lines changed: 121 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,8 @@ impl Integer for BigUint {
504504
impl ToPrimitive for BigUint {
505505
#[inline]
506506
fn to_i64(&self) -> Option<i64> {
507-
do self.to_uint().and_then |n| {
508-
// If top bit of u64 is set, it's too large to convert to
509-
// int.
507+
do self.to_u64().and_then |n| {
508+
// If top bit of u64 is set, it's too large to convert to i64.
510509
if (n >> (2*BigDigit::bits - 1) != 0) {
511510
None
512511
} else {
@@ -515,12 +514,46 @@ impl ToPrimitive for BigUint {
515514
}
516515
}
517516

517+
#[cfg(target_word_size = "32")]
518518
#[inline]
519519
fn to_u64(&self) -> Option<u64> {
520-
match self.data.len() {
521-
0 => Some(0),
522-
1 => Some(self.data[0] as u64),
523-
2 => Some(BigDigit::to_uint(self.data[1], self.data[0]) as u64),
520+
match self.data {
521+
[] => {
522+
Some(0)
523+
}
524+
[n0] => {
525+
Some(n0 as u64)
526+
}
527+
[n0, n1] => {
528+
Some(BigDigit::to_uint(n1, n0) as u64)
529+
}
530+
[n0, n1, n2] => {
531+
let n_lo = BigDigit::to_uint(n1, n0) as u64;
532+
let n_hi = n2 as u64;
533+
Some((n_hi << 32) + n_lo)
534+
}
535+
[n0, n1, n2, n3] => {
536+
let n_lo = BigDigit::to_uint(n1, n0) as u64;
537+
let n_hi = BigDigit::to_uint(n3, n2) as u64;
538+
Some((n_hi << 32) + n_lo)
539+
}
540+
_ => None
541+
}
542+
}
543+
544+
#[cfg(target_word_size = "64")]
545+
#[inline]
546+
fn to_u64(&self) -> Option<u64> {
547+
match self.data {
548+
[] => {
549+
Some(0)
550+
}
551+
[n0] => {
552+
Some(n0 as u64)
553+
}
554+
[n0, n1] => {
555+
Some(BigDigit::to_uint(n1, n0) as u64)
556+
}
524557
_ => None
525558
}
526559
}
@@ -536,6 +569,23 @@ impl FromPrimitive for BigUint {
536569
}
537570
}
538571

572+
#[cfg(target_word_size = "32")]
573+
#[inline]
574+
fn from_u64(n: u64) -> Option<BigUint> {
575+
let n_lo = (n & 0x0000_0000_FFFF_FFFF) as uint;
576+
let n_hi = (n >> 32) as uint;
577+
578+
let n = match (BigDigit::from_uint(n_hi), BigDigit::from_uint(n_lo)) {
579+
((0, 0), (0, 0)) => Zero::zero(),
580+
((0, 0), (0, n0)) => BigUint::new(~[n0]),
581+
((0, 0), (n1, n0)) => BigUint::new(~[n0, n1]),
582+
((0, n2), (n1, n0)) => BigUint::new(~[n0, n1, n2]),
583+
((n3, n2), (n1, n0)) => BigUint::new(~[n0, n1, n2, n3]),
584+
};
585+
Some(n)
586+
}
587+
588+
#[cfg(target_word_size = "64")]
539589
#[inline]
540590
fn from_u64(n: u64) -> Option<BigUint> {
541591
let n = match BigDigit::from_uint(n as uint) {
@@ -547,7 +597,9 @@ impl FromPrimitive for BigUint {
547597
}
548598
}
549599

600+
/// A generic trait for converting a value to a `BigUint`.
550601
pub trait ToBigUint {
602+
/// Converts the value of `self` to a `BigUint`.
551603
fn to_biguint(&self) -> Option<BigUint>;
552604
}
553605

@@ -696,12 +748,6 @@ impl BigUint {
696748
}
697749
}
698750

699-
/// Converts this `BigUint` into a `BigInt.
700-
#[inline]
701-
pub fn to_bigint(&self) -> BigInt {
702-
BigInt::from_biguint(Plus, self.clone())
703-
}
704-
705751
#[inline]
706752
fn shl_unit(&self, n_unit: uint) -> BigUint {
707753
if n_unit == 0 || self.is_zero() { return (*self).clone(); }
@@ -1186,7 +1232,9 @@ impl FromPrimitive for BigInt {
11861232
}
11871233
}
11881234

1235+
/// A generic trait for converting a value to a `BigInt`.
11891236
pub trait ToBigInt {
1237+
/// Converts the value of `self` to a `BigInt`.
11901238
fn to_bigint(&self) -> Option<BigInt>;
11911239
}
11921240

@@ -1330,7 +1378,7 @@ impl<R: Rng> RandBigInt for R {
13301378
-> BigInt {
13311379
assert!(*lbound < *ubound);
13321380
let delta = (*ubound - *lbound).to_biguint().unwrap();
1333-
return *lbound + self.gen_biguint_below(&delta).to_bigint();
1381+
return *lbound + self.gen_biguint_below(&delta).to_bigint().unwrap();
13341382
}
13351383
}
13361384
@@ -1607,8 +1655,8 @@ mod biguint_tests {
16071655
#[test]
16081656
fn test_convert_to_bigint() {
16091657
fn check(n: BigUint, ans: BigInt) {
1610-
assert_eq!(n.to_bigint(), ans);
1611-
assert_eq!(n.to_bigint().to_biguint().unwrap(), n);
1658+
assert_eq!(n.to_bigint().unwrap(), ans);
1659+
assert_eq!(n.to_bigint().unwrap().to_biguint().unwrap(), n);
16121660
}
16131661
check(Zero::zero(), Zero::zero());
16141662
check(BigUint::new(~[1,2,3]),
@@ -1977,11 +2025,10 @@ mod bigint_tests {
19772025
use super::*;
19782026

19792027
use std::cmp::{Less, Equal, Greater};
1980-
use std::int;
2028+
use std::{int, i64, uint, u64};
19812029
use std::num::{Zero, One, FromStrRadix};
19822030
use std::num::{ToPrimitive, FromPrimitive};
19832031
use std::rand::{task_rng};
1984-
use std::uint;
19852032

19862033
#[test]
19872034
fn test_from_biguint() {
@@ -2092,11 +2139,66 @@ mod bigint_tests {
20922139
assert_eq!(BigInt::from_biguint(Minus, BigUint::new(~[1, 2, 3])).to_uint(), None);
20932140
}
20942141

2142+
#[test]
2143+
fn test_convert_i64() {
2144+
fn check(b1: BigInt, i: i64) {
2145+
let b2: BigInt = FromPrimitive::from_i64(i).unwrap();
2146+
assert!(b1 == b2);
2147+
assert!(b1.to_i64().unwrap() == i);
2148+
}
2149+
2150+
check(Zero::zero(), 0);
2151+
check(One::one(), 1);
2152+
check(i64::min_value.to_bigint().unwrap(), i64::min_value);
2153+
check(i64::max_value.to_bigint().unwrap(), i64::max_value);
2154+
2155+
assert_eq!(
2156+
(i64::max_value as uint + 1).to_bigint().unwrap().to_i64(),
2157+
None);
2158+
2159+
assert_eq!(
2160+
BigInt::from_biguint(Plus, BigUint::new(~[1, 2, 3, 4, 5])).to_i64(),
2161+
None);
2162+
2163+
check(
2164+
BigInt::from_biguint(Minus, BigUint::new(~[0, 1<<(BigDigit::bits-1)])),
2165+
i64::min_value);
2166+
2167+
assert_eq!(
2168+
BigInt::from_biguint(Minus, BigUint::new(~[1, 1<<(BigDigit::bits-1)])).to_i64(),
2169+
None);
2170+
2171+
assert_eq!(
2172+
BigInt::from_biguint(Minus, BigUint::new(~[1, 2, 3, 4, 5])).to_i64(),
2173+
None);
2174+
}
2175+
2176+
#[test]
2177+
fn test_convert_u64() {
2178+
fn check(b1: BigInt, u: u64) {
2179+
let b2: BigInt = FromPrimitive::from_u64(u).unwrap();
2180+
assert!(b1 == b2);
2181+
assert!(b1.to_u64().unwrap() == u);
2182+
}
2183+
2184+
check(Zero::zero(), 0);
2185+
check(One::one(), 1);
2186+
check(u64::max_value.to_bigint().unwrap(), u64::max_value);
2187+
2188+
assert_eq!(
2189+
BigInt::from_biguint(Plus, BigUint::new(~[1, 2, 3, 4, 5])).to_uint(),
2190+
None);
2191+
2192+
let max_value: BigUint = FromPrimitive::from_uint(uint::max_value).unwrap();
2193+
assert_eq!(BigInt::from_biguint(Minus, max_value).to_u64(), None);
2194+
assert_eq!(BigInt::from_biguint(Minus, BigUint::new(~[1, 2, 3])).to_u64(), None);
2195+
}
2196+
20952197
#[test]
20962198
fn test_convert_to_biguint() {
20972199
fn check(n: BigInt, ans_1: BigUint) {
20982200
assert_eq!(n.to_biguint().unwrap(), ans_1);
2099-
assert_eq!(n.to_biguint().unwrap().to_bigint(), n);
2201+
assert_eq!(n.to_biguint().unwrap().to_bigint().unwrap(), n);
21002202
}
21012203
let zero: BigInt = Zero::zero();
21022204
let unsigned_zero: BigUint = Zero::zero();

0 commit comments

Comments
 (0)