Skip to content

Convert between BigInts, BigUints, ints, and uints #9014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 130 additions & 42 deletions src/libextra/num/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ impl Integer for BigUint {
impl IntConvertible for BigUint {
#[inline]
fn to_int(&self) -> int {
num::min(self.to_uint(), int::max_value as uint) as int
self.to_int_opt().expect("BigUint conversion would overflow int")
}

#[inline]
Expand Down Expand Up @@ -577,18 +577,43 @@ impl BigUint {
}


/// Converts this big integer into a uint, returning the uint::max_value if
/// it's too large to fit in a uint.
/// Converts this BigUint into a uint, failing if the conversion
/// would overflow.
#[inline]
pub fn to_uint(&self) -> uint {
self.to_uint_opt().expect("BigUint conversion would overflow uint")
}

/// Converts this BigUint into a uint, unless it would overflow.
#[inline]
pub fn to_uint_opt(&self) -> Option<uint> {
match self.data.len() {
0 => 0,
1 => self.data[0] as uint,
2 => BigDigit::to_uint(self.data[1], self.data[0]),
_ => uint::max_value
0 => Some(0),
1 => Some(self.data[0] as uint),
2 => Some(BigDigit::to_uint(self.data[1], self.data[0])),
_ => None
}
}

// Converts this BigUint into an int, unless it would overflow.
pub fn to_int_opt(&self) -> Option<int> {
self.to_uint_opt().chain(|n| {
// If top bit of uint is set, it's too large to convert to
// int.
if (n >> (2*BigDigit::bits - 1) != 0) {
None
} else {
Some(n as int)
}
})
}

/// Converts this BigUint into a BigInt.
#[inline]
pub fn to_bigint(&self) -> BigInt {
BigInt::from_biguint(Plus, self.clone())
}

#[inline]
fn shl_unit(&self, n_unit: uint) -> BigUint {
if n_unit == 0 || self.is_zero() { return (*self).clone(); }
Expand Down Expand Up @@ -1010,12 +1035,7 @@ impl Integer for BigInt {
impl IntConvertible for BigInt {
#[inline]
fn to_int(&self) -> int {
match self.sign {
Plus => num::min(self.to_uint(), int::max_value as uint) as int,
Zero => 0,
Minus => num::min((-self).to_uint(),
(int::max_value as uint) + 1) as int
}
self.to_int_opt().expect("BigInt conversion would overflow int")
}

#[inline]
Expand Down Expand Up @@ -1094,12 +1114,55 @@ impl BigInt {
.map_move(|bu| BigInt::from_biguint(sign, bu));
}

/// Converts this BigInt into a uint, failing if the conversion
/// would overflow.
#[inline]
pub fn to_uint(&self) -> uint {
self.to_uint_opt().expect("BigInt conversion would overflow uint")
}

/// Converts this BigInt into a uint, unless it would overflow.
#[inline]
pub fn to_uint_opt(&self) -> Option<uint> {
match self.sign {
Plus => self.data.to_uint_opt(),
Zero => Some(0),
Minus => None
}
}

/// Converts this BigInt into an int, unless it would overflow.
pub fn to_int_opt(&self) -> Option<int> {
match self.sign {
Plus => self.data.to_int_opt(),
Zero => Some(0),
Minus => self.data.to_uint_opt().chain(|n| {
let m: uint = 1 << (2*BigDigit::bits-1);
if (n > m) {
None
} else if (n == m) {
Some(int::min_value)
} else {
Some(-(n as int))
}
})
}
}

/// Converts this BigInt into a BigUint, failing if BigInt is
/// negative.
#[inline]
pub fn to_biguint(&self) -> BigUint {
self.to_biguint_opt().expect("negative BigInt cannot convert to BigUint")
}

/// Converts this BigInt into a BigUint, if it's not negative.
#[inline]
pub fn to_biguint_opt(&self) -> Option<BigUint> {
match self.sign {
Plus => self.data.to_uint(),
Zero => 0,
Minus => 0
Plus => Some(self.data.clone()),
Zero => Some(Zero::zero()),
Minus => None
}
}
}
Expand Down Expand Up @@ -1257,9 +1320,9 @@ mod biguint_tests {
check(~[ 0, 1], ((uint::max_value >> BigDigit::bits) + 1) as int);
check(~[-1, -1 >> 1], int::max_value);

assert_eq!(BigUint::new(~[0, -1]).to_int(), int::max_value);
assert_eq!(BigUint::new(~[0, 0, 1]).to_int(), int::max_value);
assert_eq!(BigUint::new(~[0, 0, -1]).to_int(), int::max_value);
assert_eq!(BigUint::new(~[0, -1]).to_int_opt(), None);
assert_eq!(BigUint::new(~[0, 0, 1]).to_int_opt(), None);
assert_eq!(BigUint::new(~[0, 0, -1]).to_int_opt(), None);
}

#[test]
Expand All @@ -1277,8 +1340,19 @@ mod biguint_tests {
check(~[ 0, -1], uint::max_value << BigDigit::bits);
check(~[-1, -1], uint::max_value);

assert_eq!(BigUint::new(~[0, 0, 1]).to_uint(), uint::max_value);
assert_eq!(BigUint::new(~[0, 0, -1]).to_uint(), uint::max_value);
assert_eq!(BigUint::new(~[0, 0, 1]).to_uint_opt(), None);
assert_eq!(BigUint::new(~[0, 0, -1]).to_uint_opt(), None);
}

#[test]
fn test_convert_to_bigint() {
fn check(n: BigUint, ans: BigInt) {
assert_eq!(n.to_bigint(), ans);
assert_eq!(n.to_bigint().to_biguint(), n);
}
check(Zero::zero(), Zero::zero());
check(BigUint::new(~[1,2,3]),
BigInt::from_biguint(Plus, BigUint::new(~[1,2,3])));
}

static sum_triples: &'static [(&'static [BigDigit],
Expand Down Expand Up @@ -1657,22 +1731,21 @@ mod bigint_tests {
Plus, BigUint::from_uint(int::max_value as uint)
), int::max_value);

assert!(BigInt::from_biguint(
assert_eq!(BigInt::from_biguint(
Plus, BigUint::from_uint(int::max_value as uint + 1)
).to_int() == int::max_value);
assert!(BigInt::from_biguint(
).to_int_opt(), None);
assert_eq!(BigInt::from_biguint(
Plus, BigUint::new(~[1, 2, 3])
).to_int() == int::max_value);
).to_int_opt(), None);

check(BigInt::from_biguint(
Minus, BigUint::from_uint(-int::min_value as uint)
Minus, BigUint::new(~[0, 1<<(BigDigit::bits-1)])
), int::min_value);
assert!(BigInt::from_biguint(
Minus, BigUint::from_uint(-int::min_value as uint + 1)
).to_int() == int::min_value);
assert!(BigInt::from_biguint(
Minus, BigUint::new(~[1, 2, 3])
).to_int() == int::min_value);
assert_eq!(BigInt::from_biguint(
Minus, BigUint::new(~[1, 1<<(BigDigit::bits-1)])
).to_int_opt(), None);
assert_eq!(BigInt::from_biguint(
Minus, BigUint::new(~[1, 2, 3])).to_int_opt(), None);
}

#[test]
Expand All @@ -1688,16 +1761,31 @@ mod bigint_tests {
check(
BigInt::from_biguint(Plus, BigUint::from_uint(uint::max_value)),
uint::max_value);
assert!(BigInt::from_biguint(
Plus, BigUint::new(~[1, 2, 3])
).to_uint() == uint::max_value);

assert!(BigInt::from_biguint(
Minus, BigUint::from_uint(uint::max_value)
).to_uint() == 0);
assert!(BigInt::from_biguint(
Minus, BigUint::new(~[1, 2, 3])
).to_uint() == 0);
assert_eq!(BigInt::from_biguint(
Plus, BigUint::new(~[1, 2, 3])).to_uint_opt(), None);

assert_eq!(BigInt::from_biguint(
Minus, BigUint::from_uint(uint::max_value)).to_uint_opt(), None);
assert_eq!(BigInt::from_biguint(
Minus, BigUint::new(~[1, 2, 3])).to_uint_opt(), None);
}

#[test]
fn test_convert_to_biguint() {
fn check(n: BigInt, ans_1: BigUint) {
assert_eq!(n.to_biguint(), ans_1);
assert_eq!(n.to_biguint().to_bigint(), n);
}
let zero: BigInt = Zero::zero();
let unsigned_zero: BigUint = Zero::zero();
let positive = BigInt::from_biguint(
Plus, BigUint::new(~[1,2,3]));
let negative = -positive;

check(zero, unsigned_zero);
check(positive, BigUint::new(~[1,2,3]));

assert_eq!(negative.to_biguint_opt(), None);
}

static sum_triples: &'static [(&'static [BigDigit],
Expand Down