]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #9014 : dcrewi/rust/convert-between-bigints, r=anasazi
authorbors <bors@rust-lang.org>
Wed, 11 Sep 2013 23:46:00 +0000 (16:46 -0700)
committerbors <bors@rust-lang.org>
Wed, 11 Sep 2013 23:46:00 +0000 (16:46 -0700)
src/libextra/num/bigint.rs

index c46652b6c4f211185c21920b94f67dc577638174..8975ed7fd9617050fa684e573f3d0249a2f054d4 100644 (file)
@@ -503,7 +503,7 @@ fn is_odd(&self) -> bool { !self.is_even() }
 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]
@@ -615,18 +615,43 @@ pub fn parse_bytes(buf: &[u8], radix: uint)
     }
 
 
-    /// 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(); }
@@ -1048,12 +1073,7 @@ fn is_odd(&self) -> bool { self.data.is_odd() }
 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]
@@ -1179,12 +1199,55 @@ pub fn parse_bytes(buf: &[u8], radix: uint)
             .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
         }
     }
 }
@@ -1385,9 +1448,9 @@ fn check(v: ~[BigDigit], i: int) {
         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]
@@ -1405,8 +1468,19 @@ fn check(v: ~[BigDigit], u: uint) {
         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],
@@ -1793,22 +1867,21 @@ fn check(b: BigInt, i: int) {
             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]
@@ -1824,16 +1897,31 @@ fn check(b: BigInt, u: uint) {
         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],