use rug::Integer;
use rug::integer::Order;
use crate::{
Error, Result, Tag, Value,
integer::IntegerBytes,
util::{trim_leading_zeros, u64_from_slice},
};
impl From<Integer> for Value {
fn from(value: Integer) -> Self {
from_rug_integer(&value)
}
}
impl From<&Integer> for Value {
fn from(value: &Integer) -> Self {
from_rug_integer(value)
}
}
fn from_rug_integer(value: &Integer) -> Value {
use std::cmp::Ordering;
match value.cmp0() {
Ordering::Equal => Value::Unsigned(0),
Ordering::Greater => {
let bytes: Vec<u8> = value.to_digits(Order::MsfBe);
let trimmed = trim_leading_zeros(&bytes);
if let Ok(number) = u64_from_slice(trimmed) {
Value::Unsigned(number)
} else if bytes.len() == trimmed.len() {
Value::tag(Tag::POS_BIG_INT, bytes)
} else {
Value::tag(Tag::POS_BIG_INT, trimmed)
}
}
Ordering::Less => {
let magnitude = Integer::from(-value);
let payload = magnitude - 1_u32;
let bytes: Vec<u8> = payload.to_digits(Order::MsfBe);
let trimmed = trim_leading_zeros(&bytes);
if let Ok(number) = u64_from_slice(trimmed) {
Value::Negative(number)
} else if bytes.len() == trimmed.len() {
Value::tag(Tag::NEG_BIG_INT, bytes)
} else {
Value::tag(Tag::NEG_BIG_INT, trimmed)
}
}
}
}
impl TryFrom<Value> for Integer {
type Error = Error;
fn try_from(value: Value) -> Result<Self> {
to_rug_integer(&value)
}
}
impl TryFrom<&Value> for Integer {
type Error = Error;
fn try_from(value: &Value) -> Result<Self> {
to_rug_integer(value)
}
}
fn to_rug_integer(value: &Value) -> Result<Integer> {
match value.as_integer_bytes()? {
IntegerBytes::UnsignedOwned(bytes) => Ok(Integer::from(u64::from_be_bytes(bytes))),
IntegerBytes::NegativeOwned(bytes) => Ok(Integer::from(!u64::from_be_bytes(bytes) as i64)),
IntegerBytes::UnsignedBorrowed(bytes) => Ok(Integer::from_digits(bytes, Order::MsfBe)),
IntegerBytes::NegativeBorrowed(bytes) => {
let payload = Integer::from_digits(bytes, Order::MsfBe);
Ok(-(payload + 1_u32))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::DataType;
fn roundtrip(n: Integer) -> Integer {
let encoded = Value::from(&n).encode();
let decoded = Value::decode(encoded).unwrap();
Integer::try_from(decoded).unwrap()
}
#[test]
fn zero() {
assert_eq!(roundtrip(Integer::ZERO), Integer::ZERO);
}
#[test]
fn small_positive() {
let n = Integer::from(42);
assert_eq!(roundtrip(n.clone()), n);
}
#[test]
fn u64_max() {
let n = Integer::from(u64::MAX);
let v = Value::from(&n);
assert!(matches!(v, Value::Unsigned(_)));
assert_eq!(Integer::try_from(v).unwrap(), n);
}
#[test]
fn u128_max() {
let n = Integer::from(u128::MAX);
let v = Value::from(&n);
assert!(matches!(&v, Value::Tag(2, _)));
assert_eq!(Integer::try_from(v).unwrap(), n);
}
#[test]
fn from_u128_roundtrip() {
for x in [0_u128, 1, 42, u64::MAX as u128, u64::MAX as u128 + 1, u128::MAX] {
let expected = Integer::from(x);
let via_value = Value::from(x);
assert_eq!(Integer::try_from(via_value).unwrap(), expected, "u128={x}");
}
}
#[test]
fn negative_one() {
let n = Integer::from(-1);
assert_eq!(roundtrip(n.clone()), n);
}
#[test]
fn i64_min() {
let n = Integer::from(i64::MIN);
assert_eq!(roundtrip(n.clone()), n);
}
#[test]
fn i128_min() {
let n = Integer::from(i128::MIN);
let v = Value::from(&n);
assert!(matches!(&v, Value::Tag(3, _)));
assert_eq!(Integer::try_from(v).unwrap(), n);
}
#[test]
fn i128_max() {
let n = Integer::from(i128::MAX);
let v = Value::from(&n);
assert_eq!(Integer::try_from(v).unwrap(), n);
}
#[test]
fn from_i128_roundtrip() {
for x in [
0_i128,
1,
-1,
42,
-42,
i64::MIN as i128,
i64::MAX as i128,
i128::MIN,
i128::MAX,
] {
let expected = Integer::from(x);
let via_value = Value::from(x);
assert_eq!(Integer::try_from(via_value).unwrap(), expected, "i128={x}");
}
}
#[test]
fn large_negative() {
let n = -Integer::from(u128::MAX);
let v = Value::from(&n);
assert!(matches!(&v, Value::Tag(3, _)));
assert_eq!(Integer::try_from(v).unwrap(), n);
}
#[test]
fn non_integer_errors() {
assert_eq!(
Integer::try_from(Value::from(0.5)),
Err(Error::IncompatibleType(DataType::Float16))
);
assert_eq!(
Integer::try_from(Value::null()),
Err(Error::IncompatibleType(DataType::Null))
);
}
}