From 16605901255a48ddc8b74328bac0ed959d279c11 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Fri, 16 Jun 2017 17:58:04 +0100 Subject: [PATCH 1/3] 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 --- bigint/src/bigint.rs | 123 ++++++++++++++++++++++++++++++++++++- bigint/src/tests/bigint.rs | 37 +++++++++++ 2 files changed, 157 insertions(+), 3 deletions(-) diff --git a/bigint/src/bigint.rs b/bigint/src/bigint.rs index 32a4441..0379591 100644 --- a/bigint/src/bigint.rs +++ b/bigint/src/bigint.rs @@ -1,5 +1,5 @@ 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::fmt; use std::cmp::Ordering::{self, Less, Greater, Equal}; @@ -16,8 +16,8 @@ use serde; use rand::Rng; use integer::Integer; -use traits::{ToPrimitive, FromPrimitive, Num, CheckedAdd, CheckedSub, CheckedMul, - CheckedDiv, Signed, Zero, One}; +use traits::{ToPrimitive, FromPrimitive, Num, WrappingAdd, CheckedAdd, CheckedSub, + CheckedMul, CheckedDiv, Signed, Zero, One}; use self::Sign::{Minus, NoSign, Plus}; @@ -926,6 +926,56 @@ impl BigInt { 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) -> 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) -> 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`. /// /// The digits are in little-endian base 2^32. @@ -994,6 +1044,23 @@ impl BigInt { pub fn to_bytes_le(&self) -> (Sign, Vec) { (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 { + 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. /// @@ -1010,6 +1077,23 @@ impl BigInt { (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 { + 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. /// `radix` must be in the range `[2, 36]`. /// @@ -1105,3 +1189,36 @@ impl BigInt { 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(digits: &mut [T]) + where T: Clone + Not + 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(digits: &mut [T]) + where T: Clone + Not + 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(); + } + } +} + diff --git a/bigint/src/tests/bigint.rs b/bigint/src/tests/bigint.rs index bb8899e..ead3452 100644 --- a/bigint/src/tests/bigint.rs +++ b/bigint/src/tests/bigint.rs @@ -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])); } +#[test] +fn test_from_twos_complement_bytes_le() { + fn check(s: Vec, 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, 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] fn test_cmp() { let vs: [&[BigDigit]; 4] = [&[2 as BigDigit], &[1, 1], &[2, 1], &[1, 1, 1]]; From 61cfdc37b3c9c7672ce844cead7e61b5eff5a2c0 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Sun, 18 Jun 2017 01:42:56 +0100 Subject: [PATCH 2/3] BigInt two's complement adjustments - rename `_twos_complement_` methods to just `_signed_` - make `from_` variants take &[u8] - refactor helper functions twos_complement (they take byte slice but use a generic function underneath) - fix issues in `to_signed_` functions (only two's complement negative numbers; perform byte extension where needed) - add tests to `to_signed_` methods --- bigint/src/bigint.rs | 108 +++++++++++++++++++------------------ bigint/src/tests/bigint.rs | 83 +++++++++++++++++++--------- 2 files changed, 115 insertions(+), 76 deletions(-) diff --git a/bigint/src/bigint.rs b/bigint/src/bigint.rs index 0379591..7c5dce5 100644 --- a/bigint/src/bigint.rs +++ b/bigint/src/bigint.rs @@ -1,5 +1,5 @@ use std::default::Default; -use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Not}; +use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Not, DerefMut}; use std::str::{self, FromStr}; use std::fmt; use std::cmp::Ordering::{self, Less, Greater, Equal}; @@ -930,50 +930,43 @@ impl BigInt { /// /// The digits are in little-endian base 2^8. #[inline] - pub fn from_twos_complement_bytes_le(mut digits: Vec) -> 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() + pub fn from_signed_bytes_le(digits: &[u8]) -> BigInt { + let sign = match digits.last() { + Some(v) if *v > 0x7f => Sign::Minus, + Some(_) => Sign::Plus, + None => return BigInt::zero(), }; if sign == Sign::Minus { // two's-complement the content to retrieve the magnitude + let mut digits = Vec::from(digits); twos_complement_le(&mut digits); + BigInt::from_biguint(sign, BigUint::from_bytes_le(&*digits)) + } else { + BigInt::from_biguint(sign, BigUint::from_bytes_le(digits)) } - BigInt::from_biguint(sign, BigUint::from_bytes_le(&*digits)) } - /// Creates and initializes a `BigInt` from an array of bytes in two's complement. + /// Creates and initializes a `BigInt` from an array of bytes in + /// two's complement binary representation. /// /// The digits are in big-endian base 2^8. #[inline] - pub fn from_twos_complement_bytes_be(mut digits: Vec) -> 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() + pub fn from_signed_bytes_be(digits: &[u8]) -> BigInt { + let sign = match digits.first() { + Some(v) if *v > 0x7f => Sign::Minus, + Some(_) => Sign::Plus, + None => return BigInt::zero(), }; if sign == Sign::Minus { // two's-complement the content to retrieve the magnitude + let mut digits = Vec::from(digits); twos_complement_be(&mut digits); + BigInt::from_biguint(sign, BigUint::from_bytes_be(&*digits)) + } else { + BigInt::from_biguint(sign, BigUint::from_bytes_be(digits)) } - BigInt::from_biguint(sign, BigUint::from_bytes_be(&*digits)) } /// Creates and initializes a `BigInt`. @@ -1053,12 +1046,19 @@ impl BigInt { /// use num_bigint::ToBigInt; /// /// let i = -1125.to_bigint().unwrap(); - /// assert_eq!(i.to_twos_complement_bytes_le(), vec![155, 251]); + /// assert_eq!(i.to_signed_bytes_le(), vec![155, 251]); /// ``` #[inline] - pub fn to_twos_complement_bytes_le(&self) -> Vec { + pub fn to_signed_bytes_le(&self) -> Vec { let mut bytes = self.data.to_bytes_le(); - twos_complement_le(&mut bytes); + let last_byte = bytes.last().map(|v| *v).unwrap_or(0); + if last_byte > 0x7f && !(last_byte == 0x80 && bytes.iter().rev().skip(1).all(Zero::is_zero)) { + // msb used by magnitude, extend by 1 byte + bytes.push(0); + } + if self.sign == Sign::Minus { + twos_complement_le(&mut bytes); + } bytes } @@ -1085,12 +1085,19 @@ impl BigInt { /// use num_bigint::ToBigInt; /// /// let i = -1125.to_bigint().unwrap(); - /// assert_eq!(i.to_twos_complement_bytes_be(), vec![251, 155]); + /// assert_eq!(i.to_signed_bytes_be(), vec![251, 155]); /// ``` #[inline] - pub fn to_twos_complement_bytes_be(&self) -> Vec { + pub fn to_signed_bytes_be(&self) -> Vec { let mut bytes = self.data.to_bytes_be(); - twos_complement_be(&mut bytes); + let first_byte = bytes.first().map(|v| *v).unwrap_or(0); + if first_byte > 0x7f && !(first_byte == 0x80 && bytes.iter().skip(1).all(Zero::is_zero)) { + // msb used by magnitude, extend by 1 byte + bytes.insert(0, 0); + } + if self.sign == Sign::Minus { + twos_complement_be(&mut bytes); + } bytes } @@ -1190,30 +1197,30 @@ impl BigInt { } } -/// Perform in-place two's complement of the given digit range, +/// Perform in-place two's complement of the given binary representation, /// in little-endian byte order. #[inline] -fn twos_complement_le(digits: &mut [T]) - where T: Clone + Not + 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(); - } - } +fn twos_complement_le(digits: &mut [u8]) { + twos_complement(digits) } -/// Perform in-place two's complement of the given digit range +/// Perform in-place two's complement of the given binary representation /// in big-endian byte order. #[inline] -fn twos_complement_be(digits: &mut [T]) - where T: Clone + Not + WrappingAdd + Zero + One +fn twos_complement_be(digits: &mut [u8]) { + twos_complement(digits.iter_mut().rev()) +} + +/// Perform in-place two's complement of the given digit iterator +/// starting from the least significant byte. +#[inline] +fn twos_complement(digits: I) + where I: IntoIterator, + R: DerefMut, + T: Clone + Not + WrappingAdd + Zero + One { let mut carry = true; - for d in digits.iter_mut().rev() { + for mut d in digits { *d = d.clone().not(); if carry { *d = d.wrapping_add(&T::one()); @@ -1221,4 +1228,3 @@ fn twos_complement_be(digits: &mut [T]) } } } - diff --git a/bigint/src/tests/bigint.rs b/bigint/src/tests/bigint.rs index ead3452..aa4319d 100644 --- a/bigint/src/tests/bigint.rs +++ b/bigint/src/tests/bigint.rs @@ -107,41 +107,74 @@ fn test_to_bytes_le() { } #[test] -fn test_from_twos_complement_bytes_le() { - fn check(s: Vec, result: &str) { - assert_eq!(BigInt::from_twos_complement_bytes_le(s), - BigInt::parse_bytes(result.as_bytes(), 10).unwrap()); +fn test_to_signed_bytes_le() { + fn check(s: &str, result: Vec) { + assert_eq!(BigInt::parse_bytes(s.as_bytes(), 10).unwrap().to_signed_bytes_le(), + result); } - 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"); + check("0", vec![0]); + check("32767", vec![0xff, 0x7f]); + check("-1", vec![0xff]); + check("16777216", vec![0, 0, 0, 1]); + check("-100", vec![156]); + check("-8388608", vec![0, 0, 0x80]); + check("-192", vec![0x40, 0xff]); } #[test] -fn test_from_twos_complement_bytes_be() { - fn check(s: Vec, result: &str) { - assert_eq!(BigInt::from_twos_complement_bytes_be(s), +fn test_from_signed_bytes_le() { + fn check(s: &[u8], result: &str) { + assert_eq!(BigInt::from_signed_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![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"); + check(&[], "0"); + check(&[0], "0"); + check(&[0; 10], "0"); + check(&[0xff, 0x7f], "32767"); + check(&[0xff], "-1"); + check(&[0, 0, 0, 1], "16777216"); + check(&[156], "-100"); + check(&[0, 0, 0x80], "-8388608"); + check(&[0xff; 10], "-1"); + check(&[0x40, 0xff], "-192"); } +#[test] +fn test_to_signed_bytes_be() { + fn check(s: &str, result: Vec) { + assert_eq!(BigInt::parse_bytes(s.as_bytes(), 10).unwrap().to_signed_bytes_be(), + result); + } + + check("0", vec![0]); + check("32767", vec![0x7f, 0xff]); + check("-1", vec![255]); + check("16777216", vec![1, 0, 0, 0]); + check("-100", vec![156]); + check("-8388608", vec![128, 0, 0]); + check("-192", vec![0xff, 0x40]); +} + +#[test] +fn test_from_signed_bytes_be() { + fn check(s: &[u8], result: &str) { + assert_eq!(BigInt::from_signed_bytes_be(s), + BigInt::parse_bytes(result.as_bytes(), 10).unwrap()); + } + + check(&[], "0"); + check(&[0], "0"); + check(&[0; 10], "0"); + check(&[127, 255], "32767"); + check(&[255], "-1"); + check(&[1, 0, 0, 0], "16777216"); + check(&[156], "-100"); + check(&[128, 0, 0], "-8388608"); + check(&[255; 10], "-1"); + check(&[0xff, 0x40], "-192"); +} #[test] fn test_cmp() { From 15f933495559e0b9e284ca9ffbd6f26ff98b4a92 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Tue, 20 Jun 2017 01:31:49 +0100 Subject: [PATCH 3/3] De-generalize `twos_complement' helper function --- bigint/src/bigint.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bigint/src/bigint.rs b/bigint/src/bigint.rs index 7c5dce5..780aaeb 100644 --- a/bigint/src/bigint.rs +++ b/bigint/src/bigint.rs @@ -1,5 +1,5 @@ use std::default::Default; -use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Not, DerefMut}; +use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Not}; use std::str::{self, FromStr}; use std::fmt; use std::cmp::Ordering::{self, Less, Greater, Equal}; @@ -16,7 +16,7 @@ use serde; use rand::Rng; use integer::Integer; -use traits::{ToPrimitive, FromPrimitive, Num, WrappingAdd, CheckedAdd, CheckedSub, +use traits::{ToPrimitive, FromPrimitive, Num, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, Signed, Zero, One}; use self::Sign::{Minus, NoSign, Plus}; @@ -1214,16 +1214,14 @@ fn twos_complement_be(digits: &mut [u8]) { /// Perform in-place two's complement of the given digit iterator /// starting from the least significant byte. #[inline] -fn twos_complement(digits: I) - where I: IntoIterator, - R: DerefMut, - T: Clone + Not + WrappingAdd + Zero + One +fn twos_complement<'a, I>(digits: I) + where I: IntoIterator { let mut carry = true; for mut d in digits { - *d = d.clone().not(); + *d = d.not(); if carry { - *d = d.wrapping_add(&T::one()); + *d = d.wrapping_add(1); carry = d.is_zero(); } }