Add BigInt 2's complement conversions

- add methods `from_twos_complement_bytes_le`, `from_twos_complement_bytes_be`, `to_twos_complement_bytes_le`, and `to_twos_complement_bytes_be` to `BigInt`.
- add respective tests
This commit is contained in:
Eduardo Pinho 2017-06-16 17:58:04 +01:00
parent b1b034f438
commit 1660590125
2 changed files with 157 additions and 3 deletions

View File

@ -1,5 +1,5 @@
use std::default::Default; use std::default::Default;
use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub}; use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Not};
use std::str::{self, FromStr}; use std::str::{self, FromStr};
use std::fmt; use std::fmt;
use std::cmp::Ordering::{self, Less, Greater, Equal}; use std::cmp::Ordering::{self, Less, Greater, Equal};
@ -16,8 +16,8 @@ use serde;
use rand::Rng; use rand::Rng;
use integer::Integer; use integer::Integer;
use traits::{ToPrimitive, FromPrimitive, Num, CheckedAdd, CheckedSub, CheckedMul, use traits::{ToPrimitive, FromPrimitive, Num, WrappingAdd, CheckedAdd, CheckedSub,
CheckedDiv, Signed, Zero, One}; CheckedMul, CheckedDiv, Signed, Zero, One};
use self::Sign::{Minus, NoSign, Plus}; use self::Sign::{Minus, NoSign, Plus};
@ -926,6 +926,56 @@ impl BigInt {
BigInt::from_biguint(sign, BigUint::new(digits)) BigInt::from_biguint(sign, BigUint::new(digits))
} }
/// Creates and initializes a `BigInt` from an array of bytes in two's complement.
///
/// The digits are in little-endian base 2^8.
#[inline]
pub fn from_twos_complement_bytes_le(mut digits: Vec<u8>) -> BigInt {
let sign = if digits.iter().all(Zero::is_zero) {
Sign::NoSign
} else {
digits.iter().rev().next()
.map(|v| if *v > 127 {
Sign::Minus
} else {
Sign::Plus
})
// we have at least one non-zero digit
.unwrap()
};
if sign == Sign::Minus {
// two's-complement the content to retrieve the magnitude
twos_complement_le(&mut digits);
}
BigInt::from_biguint(sign, BigUint::from_bytes_le(&*digits))
}
/// Creates and initializes a `BigInt` from an array of bytes in two's complement.
///
/// The digits are in big-endian base 2^8.
#[inline]
pub fn from_twos_complement_bytes_be(mut digits: Vec<u8>) -> BigInt {
let sign = if digits.iter().all(Zero::is_zero) {
Sign::NoSign
} else {
digits.iter().next()
.map(|v| if *v > 127 {
Sign::Minus
} else {
Sign::Plus
})
// we have at least one non-zero digit
.unwrap()
};
if sign == Sign::Minus {
// two's-complement the content to retrieve the magnitude
twos_complement_be(&mut digits);
}
BigInt::from_biguint(sign, BigUint::from_bytes_be(&*digits))
}
/// Creates and initializes a `BigInt`. /// Creates and initializes a `BigInt`.
/// ///
/// The digits are in little-endian base 2^32. /// The digits are in little-endian base 2^32.
@ -994,6 +1044,23 @@ impl BigInt {
pub fn to_bytes_le(&self) -> (Sign, Vec<u8>) { pub fn to_bytes_le(&self) -> (Sign, Vec<u8>) {
(self.sign, self.data.to_bytes_le()) (self.sign, self.data.to_bytes_le())
} }
/// Returns the two's complement byte representation of the `BigInt` in little-endian byte order.
///
/// # Examples
///
/// ```
/// use num_bigint::ToBigInt;
///
/// let i = -1125.to_bigint().unwrap();
/// assert_eq!(i.to_twos_complement_bytes_le(), vec![155, 251]);
/// ```
#[inline]
pub fn to_twos_complement_bytes_le(&self) -> Vec<u8> {
let mut bytes = self.data.to_bytes_le();
twos_complement_le(&mut bytes);
bytes
}
/// Returns the sign and the byte representation of the `BigInt` in big-endian byte order. /// Returns the sign and the byte representation of the `BigInt` in big-endian byte order.
/// ///
@ -1010,6 +1077,23 @@ impl BigInt {
(self.sign, self.data.to_bytes_be()) (self.sign, self.data.to_bytes_be())
} }
/// Returns the two's complement byte representation of the `BigInt` in big-endian byte order.
///
/// # Examples
///
/// ```
/// use num_bigint::ToBigInt;
///
/// let i = -1125.to_bigint().unwrap();
/// assert_eq!(i.to_twos_complement_bytes_be(), vec![251, 155]);
/// ```
#[inline]
pub fn to_twos_complement_bytes_be(&self) -> Vec<u8> {
let mut bytes = self.data.to_bytes_be();
twos_complement_be(&mut bytes);
bytes
}
/// Returns the integer formatted as a string in the given radix. /// Returns the integer formatted as a string in the given radix.
/// `radix` must be in the range `[2, 36]`. /// `radix` must be in the range `[2, 36]`.
/// ///
@ -1105,3 +1189,36 @@ impl BigInt {
return Some(self.div(v)); return Some(self.div(v));
} }
} }
/// Perform in-place two's complement of the given digit range,
/// in little-endian byte order.
#[inline]
fn twos_complement_le<T>(digits: &mut [T])
where T: Clone + Not<Output = T> + WrappingAdd + Zero + One
{
let mut carry = true;
for d in digits {
*d = d.clone().not();
if carry {
*d = d.wrapping_add(&T::one());
carry = d.is_zero();
}
}
}
/// Perform in-place two's complement of the given digit range
/// in big-endian byte order.
#[inline]
fn twos_complement_be<T>(digits: &mut [T])
where T: Clone + Not<Output = T> + WrappingAdd + Zero + One
{
let mut carry = true;
for d in digits.iter_mut().rev() {
*d = d.clone().not();
if carry {
*d = d.wrapping_add(&T::one());
carry = d.is_zero();
}
}
}

View File

@ -106,6 +106,43 @@ fn test_to_bytes_le() {
assert_eq!(b.to_bytes_le(), (Plus, vec![0, 2, 0, 0, 0, 0, 0, 0, 1])); assert_eq!(b.to_bytes_le(), (Plus, vec![0, 2, 0, 0, 0, 0, 0, 0, 1]));
} }
#[test]
fn test_from_twos_complement_bytes_le() {
fn check(s: Vec<u8>, result: &str) {
assert_eq!(BigInt::from_twos_complement_bytes_le(s),
BigInt::parse_bytes(result.as_bytes(), 10).unwrap());
}
check(vec![], "0");
check(vec![0], "0");
check(vec![0; 10], "0");
check(vec![255, 127], "32767");
check(vec![255], "-1");
check(vec![0, 0, 0, 1], "16777216");
check(vec![156], "-100");
check(vec![0, 0, 128], "-8388608");
check(vec![255; 10], "-1");
}
#[test]
fn test_from_twos_complement_bytes_be() {
fn check(s: Vec<u8>, result: &str) {
assert_eq!(BigInt::from_twos_complement_bytes_be(s),
BigInt::parse_bytes(result.as_bytes(), 10).unwrap());
}
check(vec![], "0");
check(vec![0], "0");
check(vec![0; 10], "0");
check(vec![127, 255], "32767");
check(vec![255], "-1");
check(vec![1, 0, 0, 0], "16777216");
check(vec![156], "-100");
check(vec![128, 0, 0], "-8388608");
check(vec![255; 10], "-1");
}
#[test] #[test]
fn test_cmp() { fn test_cmp() {
let vs: [&[BigDigit]; 4] = [&[2 as BigDigit], &[1, 1], &[2, 1], &[1, 1, 1]]; let vs: [&[BigDigit]; 4] = [&[2 as BigDigit], &[1, 1], &[2, 1], &[1, 1, 1]];