about summary refs log tree commit diff
diff options
context:
space:
mode:
authorErick Tryzelaar <erick.tryzelaar@gmail.com>2013-09-25 20:17:58 -0700
committerErick Tryzelaar <erick.tryzelaar@gmail.com>2013-10-05 13:59:05 -0700
commite3237493977abb17de584ba5065afaf2699e5b8c (patch)
tree1ad06b0d793b41050ee6df518d5e77b2ed3e2c35
parentcb240197441723fed70e4076d9eba460d8f209ba (diff)
downloadrust-e3237493977abb17de584ba5065afaf2699e5b8c.tar.gz
rust-e3237493977abb17de584ba5065afaf2699e5b8c.zip
extra: Don't truncate {u64,i64} when converting to BigInts
-rw-r--r--src/libextra/num/bigint.rs140
1 files changed, 121 insertions, 19 deletions
diff --git a/src/libextra/num/bigint.rs b/src/libextra/num/bigint.rs
index 925fe9da3e4..8aaac486076 100644
--- a/src/libextra/num/bigint.rs
+++ b/src/libextra/num/bigint.rs
@@ -504,9 +504,8 @@ impl Integer for BigUint {
 impl ToPrimitive for BigUint {
     #[inline]
     fn to_i64(&self) -> Option<i64> {
-        do self.to_uint().and_then |n| {
-            // If top bit of u64 is set, it's too large to convert to
-            // int.
+        do self.to_u64().and_then |n| {
+            // If top bit of u64 is set, it's too large to convert to i64.
             if (n >> (2*BigDigit::bits - 1) != 0) {
                 None
             } else {
@@ -515,12 +514,46 @@ 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[0] as u64),
-            2 => Some(BigDigit::to_uint(self.data[1], self.data[0]) as u64),
+        match self.data {
+            [] => {
+                Some(0)
+            }
+            [n0] => {
+                Some(n0 as u64)
+            }
+            [n0, n1] => {
+                Some(BigDigit::to_uint(n1, n0) as u64)
+            }
+            [n0, n1, n2] => {
+                let n_lo = BigDigit::to_uint(n1, n0) as u64;
+                let n_hi = n2 as u64;
+                Some((n_hi << 32) + n_lo)
+            }
+            [n0, n1, n2, n3] => {
+                let n_lo = BigDigit::to_uint(n1, n0) as u64;
+                let n_hi = BigDigit::to_uint(n3, n2) as u64;
+                Some((n_hi << 32) + n_lo)
+            }
+            _ => None
+        }
+    }
+
+    #[cfg(target_word_size = "64")]
+    #[inline]
+    fn to_u64(&self) -> Option<u64> {
+        match self.data {
+            [] => {
+                Some(0)
+            }
+            [n0] => {
+                Some(n0 as u64)
+            }
+            [n0, n1] => {
+                Some(BigDigit::to_uint(n1, n0) as u64)
+            }
             _ => None
         }
     }
@@ -536,6 +569,23 @@ 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(~[n0]),
+            ((0,  0),  (n1, n0)) => BigUint::new(~[n0, n1]),
+            ((0,  n2), (n1, n0)) => BigUint::new(~[n0, n1, n2]),
+            ((n3, n2), (n1, n0)) => BigUint::new(~[n0, n1, n2, n3]),
+        };
+        Some(n)
+    }
+
+    #[cfg(target_word_size = "64")]
     #[inline]
     fn from_u64(n: u64) -> Option<BigUint> {
         let n = match BigDigit::from_uint(n as uint) {
@@ -547,7 +597,9 @@ impl FromPrimitive for BigUint {
     }
 }
 
+/// A generic trait for converting a value to a `BigUint`.
 pub trait ToBigUint {
+    /// Converts the value of `self` to a `BigUint`.
     fn to_biguint(&self) -> Option<BigUint>;
 }
 
@@ -696,12 +748,6 @@ impl BigUint {
         }
     }
 
-    /// 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(); }
@@ -1186,7 +1232,9 @@ impl FromPrimitive for BigInt {
     }
 }
 
+/// A generic trait for converting a value to a `BigInt`.
 pub trait ToBigInt {
+    /// Converts the value of `self` to a `BigInt`.
     fn to_bigint(&self) -> Option<BigInt>;
 }
 
@@ -1330,7 +1378,7 @@ impl<R: Rng> RandBigInt for R {
                         -> BigInt {
         assert!(*lbound < *ubound);
         let delta = (*ubound - *lbound).to_biguint().unwrap();
-        return *lbound + self.gen_biguint_below(&delta).to_bigint();
+        return *lbound + self.gen_biguint_below(&delta).to_bigint().unwrap();
     }
 }
 
@@ -1607,8 +1655,8 @@ mod biguint_tests {
     #[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().unwrap(), n);
+            assert_eq!(n.to_bigint().unwrap(), ans);
+            assert_eq!(n.to_bigint().unwrap().to_biguint().unwrap(), n);
         }
         check(Zero::zero(), Zero::zero());
         check(BigUint::new(~[1,2,3]),
@@ -1977,11 +2025,10 @@ mod bigint_tests {
     use super::*;
 
     use std::cmp::{Less, Equal, Greater};
-    use std::int;
+    use std::{int, i64, uint, u64};
     use std::num::{Zero, One, FromStrRadix};
     use std::num::{ToPrimitive, FromPrimitive};
     use std::rand::{task_rng};
-    use std::uint;
 
     #[test]
     fn test_from_biguint() {
@@ -2093,10 +2140,65 @@ mod bigint_tests {
     }
 
     #[test]
+    fn test_convert_i64() {
+        fn check(b1: BigInt, i: i64) {
+            let b2: BigInt = FromPrimitive::from_i64(i).unwrap();
+            assert!(b1 == b2);
+            assert!(b1.to_i64().unwrap() == i);
+        }
+
+        check(Zero::zero(), 0);
+        check(One::one(), 1);
+        check(i64::min_value.to_bigint().unwrap(), i64::min_value);
+        check(i64::max_value.to_bigint().unwrap(), i64::max_value);
+
+        assert_eq!(
+            (i64::max_value as uint + 1).to_bigint().unwrap().to_i64(),
+            None);
+
+        assert_eq!(
+            BigInt::from_biguint(Plus, BigUint::new(~[1, 2, 3, 4, 5])).to_i64(),
+            None);
+
+        check(
+            BigInt::from_biguint(Minus, BigUint::new(~[0, 1<<(BigDigit::bits-1)])),
+            i64::min_value);
+
+        assert_eq!(
+            BigInt::from_biguint(Minus, BigUint::new(~[1, 1<<(BigDigit::bits-1)])).to_i64(),
+            None);
+
+        assert_eq!(
+            BigInt::from_biguint(Minus, BigUint::new(~[1, 2, 3, 4, 5])).to_i64(),
+            None);
+    }
+
+    #[test]
+    fn test_convert_u64() {
+        fn check(b1: BigInt, u: u64) {
+            let b2: BigInt = FromPrimitive::from_u64(u).unwrap();
+            assert!(b1 == b2);
+            assert!(b1.to_u64().unwrap() == u);
+        }
+
+        check(Zero::zero(), 0);
+        check(One::one(), 1);
+        check(u64::max_value.to_bigint().unwrap(), u64::max_value);
+
+        assert_eq!(
+            BigInt::from_biguint(Plus, BigUint::new(~[1, 2, 3, 4, 5])).to_uint(),
+            None);
+
+        let max_value: BigUint = FromPrimitive::from_uint(uint::max_value).unwrap();
+        assert_eq!(BigInt::from_biguint(Minus, max_value).to_u64(), None);
+        assert_eq!(BigInt::from_biguint(Minus, BigUint::new(~[1, 2, 3])).to_u64(), None);
+    }
+
+    #[test]
     fn test_convert_to_biguint() {
         fn check(n: BigInt, ans_1: BigUint) {
             assert_eq!(n.to_biguint().unwrap(), ans_1);
-            assert_eq!(n.to_biguint().unwrap().to_bigint(), n);
+            assert_eq!(n.to_biguint().unwrap().to_bigint().unwrap(), n);
         }
         let zero: BigInt = Zero::zero();
         let unsigned_zero: BigUint = Zero::zero();