use super::int::BigInt;
use super::uint::BigUint;
use oxinum_core::Sign;
impl BigInt {
pub fn to_signed_bytes_be(&self) -> Vec<u8> {
let mut bytes = self.to_signed_bytes_le();
bytes.reverse();
bytes
}
pub fn to_signed_bytes_le(&self) -> Vec<u8> {
if self.is_zero() {
return vec![0u8];
}
if self.sign() == Sign::Positive {
let mut bytes = self.magnitude().to_bytes_le();
let last_idx = bytes.len().saturating_sub(1);
if bytes[last_idx] & 0x80 != 0 {
bytes.push(0x00);
}
bytes
} else {
let mag_minus_one = self
.magnitude()
.checked_sub(&BigUint::one())
.unwrap_or_else(BigUint::zero);
let mut bytes = mag_minus_one.to_bytes_le();
for b in bytes.iter_mut() {
*b = !*b;
}
let need_pad = match bytes.last() {
None => true, Some(&top) => (top & 0x80) == 0, };
if need_pad {
bytes.push(0xFFu8);
}
bytes
}
}
pub fn from_signed_bytes_be(bytes: &[u8]) -> BigInt {
if bytes.is_empty() {
return BigInt::zero();
}
let top = bytes[0];
if top & 0x80 == 0 {
let mag = BigUint::from_bytes_be(bytes);
BigInt::from_parts(Sign::Positive, mag)
} else {
let mut inv: Vec<u8> = bytes.iter().map(|b| !*b).collect();
inv.reverse();
let inv_uint = BigUint::from_bytes_le(&inv);
let mag = &inv_uint + &BigUint::one();
BigInt::from_parts(Sign::Negative, mag)
}
}
pub fn from_signed_bytes_le(bytes: &[u8]) -> BigInt {
if bytes.is_empty() {
return BigInt::zero();
}
let mut be: Vec<u8> = bytes.to_vec();
be.reverse();
Self::from_signed_bytes_be(&be)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_encodes_as_single_zero_byte() {
assert_eq!(BigInt::zero().to_signed_bytes_be(), vec![0u8]);
assert_eq!(BigInt::zero().to_signed_bytes_le(), vec![0u8]);
}
#[test]
fn empty_decodes_as_zero() {
assert_eq!(BigInt::from_signed_bytes_be(&[]), BigInt::zero());
assert_eq!(BigInt::from_signed_bytes_le(&[]), BigInt::zero());
}
#[test]
fn single_zero_byte_decodes_as_zero() {
assert_eq!(BigInt::from_signed_bytes_be(&[0x00]), BigInt::zero());
assert_eq!(BigInt::from_signed_bytes_le(&[0x00]), BigInt::zero());
}
#[test]
fn small_positive_minimal_encoding() {
assert_eq!(BigInt::from(1i64).to_signed_bytes_be(), vec![0x01u8]);
assert_eq!(BigInt::from(127i64).to_signed_bytes_be(), vec![0x7Fu8]);
assert_eq!(
BigInt::from(128i64).to_signed_bytes_be(),
vec![0x00u8, 0x80]
);
assert_eq!(
BigInt::from(129i64).to_signed_bytes_be(),
vec![0x00u8, 0x81]
);
}
#[test]
fn small_negative_minimal_encoding() {
assert_eq!(BigInt::from(-1i64).to_signed_bytes_be(), vec![0xFFu8]);
assert_eq!(BigInt::from(-128i64).to_signed_bytes_be(), vec![0x80u8]);
assert_eq!(
BigInt::from(-129i64).to_signed_bytes_be(),
vec![0xFFu8, 0x7F]
);
}
}