#[cfg(feature = "radix_engine_fuzzing")]
use arbitrary::Arbitrary;
use core::cmp::Ordering;
use num_bigint::BigInt;
use num_traits::{Pow, Zero};
use sbor::rust::convert::{TryFrom, TryInto};
use sbor::rust::fmt;
use sbor::rust::format;
use sbor::rust::ops::*;
use sbor::rust::str::FromStr;
use sbor::rust::string::String;
use sbor::rust::string::ToString;
use sbor::rust::vec::Vec;
use sbor::*;
use crate::data::manifest::ManifestCustomValueKind;
use crate::data::scrypto::*;
use crate::math::bnum_integer::*;
use crate::math::decimal::*;
use crate::math::rounding_mode::*;
use crate::math::traits::*;
use crate::well_known_scrypto_custom_type;
use crate::*;
#[cfg_attr(feature = "radix_engine_fuzzing", derive(Arbitrary))]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PreciseDecimal(pub I256);
impl Default for PreciseDecimal {
fn default() -> Self {
Self::zero()
}
}
macro_rules! fmt_remainder {
() => {
"{:036}"
};
}
impl PreciseDecimal {
pub const MIN: Self = Self(I256::MIN);
pub const MAX: Self = Self(I256::MAX);
pub const BITS: usize = I256::BITS as usize;
pub const SCALE: u32 = 36;
pub const ZERO: Self = Self(I256::ZERO);
pub const ONE_HUNDREDTH: Self = Self(I256::from_digits([
4003012203950112768,
542101086242752,
0,
0,
]));
pub const ONE_TENTH: Self = Self(I256::from_digits([
3136633892082024448,
5421010862427522,
0,
0,
]));
pub const ONE: Self = Self(I256::from_digits([
12919594847110692864,
54210108624275221,
0,
0,
]));
pub const TEN: Self = Self(I256::from_digits([
68739955140067328,
542101086242752217,
0,
0,
]));
pub const ONE_HUNDRED: Self = Self(I256::from_digits([
687399551400673280,
5421010862427522170,
0,
0,
]));
pub fn zero() -> Self {
Self::ZERO
}
pub fn one() -> Self {
Self::ONE
}
pub fn is_zero(&self) -> bool {
self.0 == I256::zero()
}
pub fn is_positive(&self) -> bool {
self.0 > I256::zero()
}
pub fn is_negative(&self) -> bool {
self.0 < I256::zero()
}
pub fn checked_abs(&self) -> Option<Self> {
if *self != Self::MIN {
Some(Self(self.0.abs()))
} else {
None
}
}
pub fn checked_floor(&self) -> Option<Self> {
self.checked_round(0, RoundingMode::ToNegativeInfinity)
}
pub fn checked_ceiling(&self) -> Option<Self> {
self.checked_round(0, RoundingMode::ToPositiveInfinity)
}
pub fn checked_round<T: Into<i32>>(
&self,
decimal_places: T,
mode: RoundingMode,
) -> Option<Self> {
let decimal_places = decimal_places.into();
assert!(decimal_places <= Self::SCALE as i32);
assert!(decimal_places >= 0);
let n = Self::SCALE - decimal_places as u32;
let divisor: I256 = I256::TEN.pow(n);
let positive_remainder = {
let remainder = self.0 % divisor;
match remainder.cmp(&I256::ZERO) {
Ordering::Less => divisor + remainder,
Ordering::Equal => return Some(*self),
Ordering::Greater => remainder,
}
};
let resolved_strategy =
ResolvedRoundingStrategy::from_mode(mode, self.is_positive(), || {
let midpoint = divisor >> 1; positive_remainder.cmp(&midpoint)
});
let rounded_subunits = match resolved_strategy {
ResolvedRoundingStrategy::RoundUp => {
let to_add = divisor
.checked_sub(positive_remainder)
.expect("Always safe");
self.0.checked_add(to_add)?
}
ResolvedRoundingStrategy::RoundDown => self.0.checked_sub(positive_remainder)?,
ResolvedRoundingStrategy::RoundToEven => {
let double_divisor = divisor << 1; if self.is_positive() {
let rounded_down = self.0.checked_sub(positive_remainder)?;
if rounded_down % double_divisor == I256::ZERO {
rounded_down
} else {
rounded_down.checked_add(divisor)?
}
} else {
let to_add = divisor
.checked_sub(positive_remainder)
.expect("Always safe");
let rounded_up = self.0.checked_add(to_add)?;
if rounded_up % double_divisor == I256::ZERO {
rounded_up
} else {
rounded_up.checked_sub(divisor)?
}
}
}
};
Some(Self(rounded_subunits))
}
pub fn checked_powi(&self, exp: i64) -> Option<Self> {
let one_384 = I384::from(Self::ONE.0);
let base_384 = I384::from(self.0);
let div = |x: i64, y: i64| x.checked_div(y);
let sub = |x: i64, y: i64| x.checked_sub(y);
let mul = |x: i64, y: i64| x.checked_mul(y);
if exp < 0 {
let sub_384 = (one_384 * one_384).checked_div(base_384)?;
let sub_256 = I256::try_from(sub_384).ok()?;
let exp = mul(exp, -1)?;
return Self(sub_256).checked_powi(exp);
}
if exp == 0 {
return Some(Self::ONE);
}
if exp == 1 {
return Some(*self);
}
if exp % 2 == 0 {
let sub_384 = base_384.checked_mul(base_384)? / one_384;
let sub_256 = I256::try_from(sub_384).ok()?;
let exp = div(exp, 2)?;
Self(sub_256).checked_powi(exp)
} else {
let sub_384 = base_384.checked_mul(base_384)? / one_384;
let sub_256 = I256::try_from(sub_384).ok()?;
let sub_pdec = Self(sub_256);
let exp = div(sub(exp, 1)?, 2)?;
let b = sub_pdec.checked_powi(exp)?;
self.checked_mul(b)
}
}
pub fn checked_sqrt(&self) -> Option<Self> {
if self.is_negative() {
return None;
}
if self.is_zero() {
return Some(Self::ZERO);
}
let self_384 = I384::from(self.0);
let correct_nb = self_384 * I384::from(Self::ONE.0);
let sqrt = I256::try_from(correct_nb.sqrt()).ok()?;
Some(Self(sqrt))
}
pub fn checked_cbrt(&self) -> Option<Self> {
if self.is_zero() {
return Some(Self::ZERO);
}
let self_bigint = BigInt::from(self.0);
let correct_nb: BigInt = self_bigint * BigInt::from(Self::ONE.0).pow(2_u32);
let cbrt = I256::try_from(correct_nb.cbrt()).ok()?;
Some(Self(cbrt))
}
pub fn checked_nth_root(&self, n: u32) -> Option<Self> {
if (self.is_negative() && n % 2 == 0) || n == 0 {
None
} else if n == 1 {
Some(*self)
} else {
if self.is_zero() {
return Some(Self::ZERO);
}
let self_integer = BigInt::from(self.0);
let correct_nb = self_integer * BigInt::from(Self::ONE.0).pow(n - 1);
let nth_root = I256::try_from(correct_nb.nth_root(n)).unwrap();
Some(Self(nth_root))
}
}
}
macro_rules! from_primitive_type {
($($type:ident),*) => {
$(
impl From<$type> for PreciseDecimal {
fn from(val: $type) -> Self {
Self(I256::from(val) * Self::ONE.0)
}
}
)*
};
}
macro_rules! to_primitive_type {
($($type:ident),*) => {
$(
impl TryFrom<PreciseDecimal> for $type {
type Error = ParsePreciseDecimalError;
fn try_from(val: PreciseDecimal) -> Result<Self, Self::Error> {
let rounded = val.checked_round(0, RoundingMode::ToZero).ok_or(ParsePreciseDecimalError::Overflow)?;
let fraction = val.checked_sub(rounded).ok_or(Self::Error::Overflow)?;
if !fraction.is_zero() {
Err(Self::Error::InvalidDigit)
}
else {
let i_256 = rounded.0 / I256::TEN.pow(PreciseDecimal::SCALE);
$type::try_from(i_256)
.map_err(|_| Self::Error::Overflow)
}
}
}
impl TryFrom<&PreciseDecimal> for $type {
type Error = ParsePreciseDecimalError;
fn try_from(val: &PreciseDecimal) -> Result<Self, Self::Error> {
$type::try_from(*val)
}
}
)*
}
}
from_primitive_type!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
to_primitive_type!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
impl TryFrom<&str> for PreciseDecimal {
type Error = ParsePreciseDecimalError;
fn try_from(val: &str) -> Result<Self, Self::Error> {
Self::from_str(val)
}
}
impl TryFrom<String> for PreciseDecimal {
type Error = ParsePreciseDecimalError;
fn try_from(val: String) -> Result<Self, Self::Error> {
Self::from_str(&val)
}
}
impl From<bool> for PreciseDecimal {
fn from(val: bool) -> Self {
if val {
Self::ONE
} else {
Self::ZERO
}
}
}
impl CheckedNeg<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn checked_neg(self) -> Option<Self::Output> {
let c = self.0.checked_neg();
c.map(Self)
}
}
impl CheckedAdd<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn checked_add(self, other: Self) -> Option<Self::Output> {
let a = self.0;
let b = other.0;
let c = a.checked_add(b);
c.map(Self)
}
}
impl CheckedSub<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn checked_sub(self, other: Self) -> Option<Self::Output> {
let a = self.0;
let b = other.0;
let c = a.checked_sub(b);
c.map(Self)
}
}
impl CheckedMul<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn checked_mul(self, other: Self) -> Option<Self> {
let a = I384::from(self.0);
let b = I384::from(other.0);
let c = a.checked_mul(b)?;
let c = c.checked_div(I384::from(Self::ONE.0))?;
let c_256 = I256::try_from(c).ok();
c_256.map(Self)
}
}
impl CheckedDiv<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn checked_div(self, other: Self) -> Option<Self> {
let a = I384::from(self.0);
let b = I384::from(other.0);
let c = a.checked_mul(I384::from(Self::ONE.0))?;
let c = c.checked_div(b)?;
let c_256 = I256::try_from(c).ok();
c_256.map(Self)
}
}
impl Neg for PreciseDecimal {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
self.checked_neg().expect("Overflow")
}
}
impl Add<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self::Output {
self.checked_add(other).expect("Overflow")
}
}
impl Sub<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self::Output {
self.checked_sub(other).expect("Overflow")
}
}
impl Mul<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self::Output {
self.checked_mul(other).expect("Overflow")
}
}
impl Div<PreciseDecimal> for PreciseDecimal {
type Output = Self;
#[inline]
fn div(self, other: Self) -> Self::Output {
self.checked_div(other)
.expect("Overflow or division by zero")
}
}
impl AddAssign<PreciseDecimal> for PreciseDecimal {
#[inline]
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl SubAssign<PreciseDecimal> for PreciseDecimal {
#[inline]
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl MulAssign<PreciseDecimal> for PreciseDecimal {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = *self * other;
}
}
impl DivAssign<PreciseDecimal> for PreciseDecimal {
#[inline]
fn div_assign(&mut self, other: Self) {
*self = *self / other;
}
}
macro_rules! impl_arith_ops {
($type:ident) => {
impl CheckedAdd<$type> for PreciseDecimal {
type Output = Self;
fn checked_add(self, other: $type) -> Option<Self::Output> {
self.checked_add(Self::try_from(other).ok()?)
}
}
impl CheckedSub<$type> for PreciseDecimal {
type Output = Self;
fn checked_sub(self, other: $type) -> Option<Self::Output> {
self.checked_sub(Self::try_from(other).ok()?)
}
}
impl CheckedMul<$type> for PreciseDecimal {
type Output = Self;
fn checked_mul(self, other: $type) -> Option<Self::Output> {
self.checked_mul(Self::try_from(other).ok()?)
}
}
impl CheckedDiv<$type> for PreciseDecimal {
type Output = Self;
fn checked_div(self, other: $type) -> Option<Self::Output> {
self.checked_div(Self::try_from(other).ok()?)
}
}
impl Add<$type> for PreciseDecimal {
type Output = Self;
#[inline]
fn add(self, other: $type) -> Self::Output {
self.checked_add(other).expect("Overflow")
}
}
impl Sub<$type> for PreciseDecimal {
type Output = Self;
#[inline]
fn sub(self, other: $type) -> Self::Output {
self.checked_sub(other).expect("Overflow")
}
}
impl Mul<$type> for PreciseDecimal {
type Output = Self;
#[inline]
fn mul(self, other: $type) -> Self::Output {
self.checked_mul(other).expect("Overflow")
}
}
impl Div<$type> for PreciseDecimal {
type Output = Self;
#[inline]
fn div(self, other: $type) -> Self::Output {
self.checked_div(other)
.expect("Overflow or division by zero")
}
}
impl Add<PreciseDecimal> for $type {
type Output = PreciseDecimal;
#[inline]
fn add(self, other: PreciseDecimal) -> Self::Output {
other + self
}
}
impl Sub<PreciseDecimal> for $type {
type Output = PreciseDecimal;
#[inline]
fn sub(self, other: PreciseDecimal) -> Self::Output {
PreciseDecimal::try_from(self)
.expect("Overflow")
.checked_sub(other)
.expect("Overflow")
}
}
impl Mul<PreciseDecimal> for $type {
type Output = PreciseDecimal;
#[inline]
fn mul(self, other: PreciseDecimal) -> Self::Output {
other * self
}
}
impl Div<PreciseDecimal> for $type {
type Output = PreciseDecimal;
#[inline]
fn div(self, other: PreciseDecimal) -> Self::Output {
PreciseDecimal::try_from(self)
.expect("Overflow")
.checked_div(other)
.expect("Overflow or division by zero")
}
}
impl AddAssign<$type> for PreciseDecimal {
#[inline]
fn add_assign(&mut self, other: $type) {
*self = *self + other;
}
}
impl SubAssign<$type> for PreciseDecimal {
#[inline]
fn sub_assign(&mut self, other: $type) {
*self = *self - other;
}
}
impl MulAssign<$type> for PreciseDecimal {
#[inline]
fn mul_assign(&mut self, other: $type) {
*self = *self * other;
}
}
impl DivAssign<$type> for PreciseDecimal {
#[inline]
fn div_assign(&mut self, other: $type) {
*self = *self / other;
}
}
};
}
impl_arith_ops!(u8);
impl_arith_ops!(u16);
impl_arith_ops!(u32);
impl_arith_ops!(u64);
impl_arith_ops!(u128);
impl_arith_ops!(usize);
impl_arith_ops!(i8);
impl_arith_ops!(i16);
impl_arith_ops!(i32);
impl_arith_ops!(i64);
impl_arith_ops!(i128);
impl_arith_ops!(isize);
impl_arith_ops!(Decimal);
impl_arith_ops!(I192);
impl_arith_ops!(I256);
impl_arith_ops!(I320);
impl_arith_ops!(I448);
impl_arith_ops!(I512);
impl_arith_ops!(U192);
impl_arith_ops!(U256);
impl_arith_ops!(U320);
impl_arith_ops!(U448);
impl_arith_ops!(U512);
macro_rules! impl_arith_ops_non_primitives {
($type:ident) => {
impl CheckedAdd<PreciseDecimal> for $type {
type Output = PreciseDecimal;
#[inline]
fn checked_add(self, other: PreciseDecimal) -> Option<Self::Output> {
other.checked_add(self)
}
}
impl CheckedSub<PreciseDecimal> for $type {
type Output = PreciseDecimal;
fn checked_sub(self, other: PreciseDecimal) -> Option<Self::Output> {
PreciseDecimal::try_from(self).ok()?.checked_sub(other)
}
}
impl CheckedMul<PreciseDecimal> for $type {
type Output = PreciseDecimal;
#[inline]
fn checked_mul(self, other: PreciseDecimal) -> Option<Self::Output> {
other.checked_mul(self)
}
}
impl CheckedDiv<PreciseDecimal> for $type {
type Output = PreciseDecimal;
fn checked_div(self, other: PreciseDecimal) -> Option<Self::Output> {
PreciseDecimal::try_from(self).ok()?.checked_div(other)
}
}
};
}
impl_arith_ops_non_primitives!(Decimal);
impl_arith_ops_non_primitives!(I192);
impl_arith_ops_non_primitives!(I256);
impl_arith_ops_non_primitives!(I320);
impl_arith_ops_non_primitives!(I448);
impl_arith_ops_non_primitives!(I512);
impl_arith_ops_non_primitives!(U192);
impl_arith_ops_non_primitives!(U256);
impl_arith_ops_non_primitives!(U320);
impl_arith_ops_non_primitives!(U448);
impl_arith_ops_non_primitives!(U512);
impl TryFrom<&[u8]> for PreciseDecimal {
type Error = ParsePreciseDecimalError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() == Self::BITS / 8 {
let val = I256::try_from(slice).expect("Length should have already been checked.");
Ok(Self(val))
} else {
Err(ParsePreciseDecimalError::InvalidLength(slice.len()))
}
}
}
impl PreciseDecimal {
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_le_bytes().to_vec()
}
}
well_known_scrypto_custom_type!(
PreciseDecimal,
ScryptoCustomValueKind::PreciseDecimal,
Type::PreciseDecimal,
PreciseDecimal::BITS / 8,
PRECISE_DECIMAL_TYPE,
precise_decimal_type_data
);
manifest_type!(
PreciseDecimal,
ManifestCustomValueKind::PreciseDecimal,
PreciseDecimal::BITS / 8
);
impl FromStr for PreciseDecimal {
type Err = ParsePreciseDecimalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let v: Vec<&str> = s.split('.').collect();
if v.len() > 2 {
return Err(ParsePreciseDecimalError::MoreThanOneDecimalPoint);
}
let integer_part = match I256::from_str(v[0]) {
Ok(val) => val,
Err(err) => match err {
ParseI256Error::NegativeToUnsigned => {
unreachable!("NegativeToUnsigned is only for parsing unsigned types, not I256")
}
ParseI256Error::Overflow => return Err(ParsePreciseDecimalError::Overflow),
ParseI256Error::InvalidLength => {
unreachable!("InvalidLength is only for parsing &[u8], not &str")
}
ParseI256Error::InvalidDigit => return Err(ParsePreciseDecimalError::InvalidDigit),
ParseI256Error::Empty => return Err(ParsePreciseDecimalError::EmptyIntegralPart),
},
};
let mut subunits = integer_part
.checked_mul(Self::ONE.0)
.ok_or(ParsePreciseDecimalError::Overflow)?;
if v.len() == 2 {
let scale = if let Some(scale) = Self::SCALE.checked_sub(v[1].len() as u32) {
Ok(scale)
} else {
Err(Self::Err::MoreThanThirtySixDecimalPlaces)
}?;
let fractional_part = match I256::from_str(v[1]) {
Ok(val) => val,
Err(err) => match err {
ParseI256Error::NegativeToUnsigned => {
unreachable!(
"NegativeToUnsigned is only for parsing unsigned types, not I256"
)
}
ParseI256Error::Overflow => return Err(ParsePreciseDecimalError::Overflow),
ParseI256Error::InvalidLength => {
unreachable!("InvalidLength is only for parsing &[u8], not &str")
}
ParseI256Error::InvalidDigit => {
return Err(ParsePreciseDecimalError::InvalidDigit)
}
ParseI256Error::Empty => {
return Err(ParsePreciseDecimalError::EmptyFractionalPart)
}
},
};
let fractional_subunits = fractional_part
.checked_mul(I256::TEN.pow(scale))
.expect("No overflow possible");
if integer_part.is_negative() || v[0].starts_with('-') {
subunits = subunits
.checked_sub(fractional_subunits)
.ok_or(ParsePreciseDecimalError::Overflow)?;
} else {
subunits = subunits
.checked_add(fractional_subunits)
.ok_or(ParsePreciseDecimalError::Overflow)?;
}
}
Ok(Self(subunits))
}
}
impl fmt::Display for PreciseDecimal {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
const MULTIPLIER: I256 = PreciseDecimal::ONE.0;
let quotient = self.0 / MULTIPLIER;
let remainder = self.0 % MULTIPLIER;
if !remainder.is_zero() {
let mut sign = "".to_string();
if remainder < I256::ZERO && quotient == I256::ZERO {
sign.push('-');
}
let rem_str = format!(fmt_remainder!(), remainder.abs());
write!(f, "{}{}.{}", sign, quotient, &rem_str.trim_end_matches('0'))
} else {
write!(f, "{}", quotient)
}
}
}
impl fmt::Debug for PreciseDecimal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParsePreciseDecimalError {
InvalidDigit,
Overflow,
EmptyIntegralPart,
EmptyFractionalPart,
MoreThanThirtySixDecimalPlaces,
MoreThanOneDecimalPoint,
InvalidLength(usize),
}
#[cfg(not(feature = "alloc"))]
impl std::error::Error for ParsePreciseDecimalError {}
#[cfg(not(feature = "alloc"))]
impl fmt::Display for ParsePreciseDecimalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<Decimal> for PreciseDecimal {
fn from(val: Decimal) -> Self {
Self(I256::try_from(val.0).unwrap() * I256::TEN.pow(Self::SCALE - Decimal::SCALE))
}
}
pub trait CheckedTruncate<T> {
type Output;
fn checked_truncate(self, mode: RoundingMode) -> Option<Self::Output>;
}
impl CheckedTruncate<Decimal> for PreciseDecimal {
type Output = Decimal;
fn checked_truncate(self, mode: RoundingMode) -> Option<Self::Output> {
let rounded = self.checked_round(Decimal::SCALE as i32, mode)?;
let a_256 = rounded
.0
.checked_div(I256::TEN.pow(Self::SCALE - Decimal::SCALE))?;
Some(Decimal(a_256.try_into().ok()?))
}
}
macro_rules! try_from_integer {
($($t:ident),*) => {
$(
impl TryFrom<$t> for PreciseDecimal {
type Error = ParsePreciseDecimalError;
fn try_from(val: $t) -> Result<Self, Self::Error> {
match I256::try_from(val) {
Ok(val) => {
match val.checked_mul(Self::ONE.0) {
Some(mul) => Ok(Self(mul)),
None => Err(ParsePreciseDecimalError::Overflow),
}
},
Err(_) => Err(ParsePreciseDecimalError::Overflow),
}
}
}
)*
};
}
try_from_integer!(I192, I256, I320, I384, I448, I512);
try_from_integer!(U192, U256, U320, U384, U448, U512);
#[cfg(test)]
mod tests {
use super::*;
use crate::math::precise_decimal::RoundingMode;
use paste::paste;
macro_rules! test_dec {
($x:literal) => {
$crate::math::Decimal::try_from($x).unwrap()
};
}
macro_rules! test_pdec {
($x:literal) => {
$crate::math::PreciseDecimal::try_from($x).unwrap()
};
}
#[test]
fn test_format_precise_decimal() {
assert_eq!(
PreciseDecimal(1i128.into()).to_string(),
"0.000000000000000000000000000000000001"
);
assert_eq!(
PreciseDecimal(123456789123456789i128.into()).to_string(),
"0.000000000000000000123456789123456789"
);
assert_eq!(
PreciseDecimal(I256::from(10).pow(PreciseDecimal::SCALE)).to_string(),
"1"
);
assert_eq!(
PreciseDecimal(I256::from(10).pow(PreciseDecimal::SCALE) * I256::from(123)).to_string(),
"123"
);
assert_eq!(
PreciseDecimal(
I256::from_str("123456789000000000000000000000000000000000000").unwrap()
)
.to_string(),
"123456789"
);
assert_eq!(
PreciseDecimal::MAX.to_string(),
"57896044618658097711785492504343953926634.992332820282019728792003956564819967"
);
assert_eq!(PreciseDecimal::MIN.is_negative(), true);
assert_eq!(
PreciseDecimal::MIN.to_string(),
"-57896044618658097711785492504343953926634.992332820282019728792003956564819968"
);
}
#[test]
fn test_parse_precise_decimal() {
assert_eq!(
PreciseDecimal::from_str("0.000000000000000001").unwrap(),
PreciseDecimal(I256::from(10).pow(18)),
);
assert_eq!(
PreciseDecimal::from_str("0.0000000000000000000000000000000000001"),
Err(ParsePreciseDecimalError::MoreThanThirtySixDecimalPlaces),
);
assert_eq!(
PreciseDecimal::from_str("0.123456789123456789").unwrap(),
PreciseDecimal(I256::from(123456789123456789i128) * I256::from(10i8).pow(18)),
);
assert_eq!(
PreciseDecimal::from_str("1").unwrap(),
PreciseDecimal(I256::from(10).pow(PreciseDecimal::SCALE)),
);
assert_eq!(
PreciseDecimal::from_str("123456789123456789").unwrap(),
PreciseDecimal(
I256::from(123456789123456789i128) * I256::from(10).pow(PreciseDecimal::SCALE)
),
);
assert_eq!(
PreciseDecimal::from_str(
"57896044618658097711785492504343953926634.992332820282019728792003956564819967"
)
.unwrap(),
PreciseDecimal::MAX,
);
assert_eq!(
PreciseDecimal::from_str(
"57896044618658097711785492504343953926634.992332820282019728792003956564819968"
),
Err(ParsePreciseDecimalError::Overflow),
);
assert_eq!(
PreciseDecimal::from_str("157896044618658097711785492504343953926634"),
Err(ParsePreciseDecimalError::Overflow),
);
assert_eq!(
PreciseDecimal::from_str(
"-57896044618658097711785492504343953926634.992332820282019728792003956564819968"
)
.unwrap(),
PreciseDecimal::MIN,
);
assert_eq!(
PreciseDecimal::from_str(
"-57896044618658097711785492504343953926634.992332820282019728792003956564819969"
),
Err(ParsePreciseDecimalError::Overflow),
);
assert_eq!(
PreciseDecimal::from_str(".000000000000000231"),
Err(ParsePreciseDecimalError::EmptyIntegralPart),
);
assert_eq!(
PreciseDecimal::from_str("231."),
Err(ParsePreciseDecimalError::EmptyFractionalPart),
);
assert_eq!(test_pdec!("0"), PreciseDecimal::ZERO);
assert_eq!(test_pdec!("1"), PreciseDecimal::ONE);
assert_eq!(test_pdec!("0.1"), PreciseDecimal::ONE_TENTH);
assert_eq!(test_pdec!("10"), PreciseDecimal::TEN);
assert_eq!(test_pdec!("100"), PreciseDecimal::ONE_HUNDRED);
assert_eq!(test_pdec!("0.01"), PreciseDecimal::ONE_HUNDREDTH);
assert_eq!("0", PreciseDecimal::ZERO.to_string());
assert_eq!("1", PreciseDecimal::ONE.to_string());
assert_eq!("0.1", PreciseDecimal::ONE_TENTH.to_string());
assert_eq!("10", PreciseDecimal::TEN.to_string());
assert_eq!("100", PreciseDecimal::ONE_HUNDRED.to_string());
assert_eq!("0.01", PreciseDecimal::ONE_HUNDREDTH.to_string());
}
#[test]
fn test_add_precise_decimal() {
let a = PreciseDecimal::from(5u32);
let b = PreciseDecimal::from(7u32);
assert_eq!(a.checked_add(b).unwrap().to_string(), "12");
}
#[test]
fn test_add_overflow_precise_decimal() {
assert!(PreciseDecimal::MAX
.checked_add(PreciseDecimal::ONE)
.is_none());
}
#[test]
fn test_sub_precise_decimal() {
let a = PreciseDecimal::from(5u32);
let b = PreciseDecimal::from(7u32);
assert_eq!(a.checked_sub(b).unwrap().to_string(), "-2");
assert_eq!(b.checked_sub(a).unwrap().to_string(), "2");
}
#[test]
fn test_sub_overflow_precise_decimal() {
assert!(PreciseDecimal::MIN
.checked_sub(PreciseDecimal::ONE)
.is_none());
}
#[test]
fn test_mul_precise_decimal() {
let a = PreciseDecimal::from(5u32);
let b = PreciseDecimal::from(7u32);
assert_eq!(a.checked_mul(b).unwrap().to_string(), "35");
let a = PreciseDecimal::from_str("1000000000").unwrap();
let b = PreciseDecimal::from_str("1000000000").unwrap();
assert_eq!(a.checked_mul(b).unwrap().to_string(), "1000000000000000000");
let a = PreciseDecimal::MAX.checked_div(test_pdec!(2)).unwrap();
let b = PreciseDecimal::from(2);
assert_eq!(
a.checked_mul(b).unwrap(),
test_pdec!(
"57896044618658097711785492504343953926634.992332820282019728792003956564819966"
)
);
}
#[test]
fn test_mul_to_max_precise_decimal() {
let a = PreciseDecimal::MAX.checked_sqrt().unwrap();
a.checked_mul(a).unwrap();
}
#[test]
fn test_mul_to_minimum_overflow_decimal() {
let a = PreciseDecimal::MAX.checked_sqrt().unwrap();
assert!(a.checked_mul(a + PreciseDecimal(I256::ONE)).is_none());
}
#[test]
fn test_mul_overflow_by_small_precise_decimal() {
assert!(PreciseDecimal::MAX
.checked_mul(test_pdec!("1.000000000000000000000000000000000001"))
.is_none());
}
#[test]
fn test_mul_overflow_by_a_lot_precise_decimal() {
assert!(PreciseDecimal::MAX.checked_mul(test_pdec!("1.1")).is_none());
}
#[test]
fn test_mul_neg_overflow_precise_decimal() {
assert!(PreciseDecimal::MAX
.checked_neg()
.unwrap()
.checked_mul(test_pdec!("-1.000000000000000000000000000000000001"))
.is_none());
}
#[test]
fn test_div_by_zero_precise_decimal() {
let a = PreciseDecimal::from(5u32);
let b = PreciseDecimal::from(0u32);
assert!(a.checked_div(b).is_none());
}
#[test]
fn test_powi_exp_overflow_precise_decimal() {
let a = PreciseDecimal::from(5u32);
let b = i64::MIN;
assert!(a.checked_powi(b).is_none());
}
#[test]
fn test_1_powi_max_precise_decimal() {
let a = PreciseDecimal::from(1u32);
let b = i64::MAX;
assert_eq!(a.checked_powi(b).unwrap().to_string(), "1");
}
#[test]
fn test_1_powi_min_precise_decimal() {
let a = PreciseDecimal::from(1u32);
let b = i64::MAX - 1;
assert_eq!(a.checked_powi(b).unwrap().to_string(), "1");
}
#[test]
fn test_powi_max_precise_decimal() {
let _max = PreciseDecimal::MAX.checked_powi(1).unwrap();
let _max_sqrt = PreciseDecimal::MAX.checked_sqrt().unwrap();
let _max_cbrt = PreciseDecimal::MAX.checked_cbrt().unwrap();
let _max_dec_2 = _max_sqrt.checked_powi(2).unwrap();
let _max_dec_3 = _max_cbrt.checked_powi(3).unwrap();
}
#[test]
fn test_div_precise_decimal() {
let a = PreciseDecimal::from(5u32);
let b = PreciseDecimal::from(7u32);
assert_eq!(
a.checked_div(b).unwrap().to_string(),
"0.714285714285714285714285714285714285"
);
assert_eq!(b.checked_div(a).unwrap().to_string(), "1.4");
let a = PreciseDecimal::MAX;
let b = PreciseDecimal::from(2);
assert_eq!(
a.checked_div(b).unwrap(),
test_pdec!(
"28948022309329048855892746252171976963317.496166410141009864396001978282409983"
)
);
}
#[test]
fn test_div_negative_precise_decimal() {
let a = PreciseDecimal::from(-42);
let b = PreciseDecimal::from(2);
assert_eq!(a.checked_div(b).unwrap().to_string(), "-21");
}
#[test]
fn test_0_pow_0_precise_decimal() {
let a = test_pdec!("0");
assert_eq!(a.checked_powi(0).unwrap().to_string(), "1");
}
#[test]
fn test_0_powi_1_precise_decimal() {
let a = test_pdec!("0");
assert_eq!(a.checked_powi(1).unwrap().to_string(), "0");
}
#[test]
fn test_0_powi_10_precise_decimal() {
let a = test_pdec!("0");
assert_eq!(a.checked_powi(10).unwrap().to_string(), "0");
}
#[test]
fn test_1_powi_0_precise_decimal() {
let a = test_pdec!(1);
assert_eq!(a.checked_powi(0).unwrap().to_string(), "1");
}
#[test]
fn test_1_powi_1_precise_decimal() {
let a = test_pdec!(1);
assert_eq!(a.checked_powi(1).unwrap().to_string(), "1");
}
#[test]
fn test_1_powi_10_precise_decimal() {
let a = test_pdec!(1);
assert_eq!(a.checked_powi(10).unwrap().to_string(), "1");
}
#[test]
fn test_2_powi_0_precise_decimal() {
let a = test_pdec!("2");
assert_eq!(a.checked_powi(0).unwrap().to_string(), "1");
}
#[test]
fn test_2_powi_3724_precise_decimal() {
let a = test_pdec!("1.000234891009084238");
assert_eq!(
a.checked_powi(3724).unwrap().to_string(),
"2.3979912322546748642222795591580985"
);
}
#[test]
fn test_2_powi_2_precise_decimal() {
let a = test_pdec!("2");
assert_eq!(a.checked_powi(2).unwrap().to_string(), "4");
}
#[test]
fn test_2_powi_3_precise_decimal() {
let a = test_pdec!("2");
assert_eq!(a.checked_powi(3).unwrap().to_string(), "8");
}
#[test]
fn test_10_powi_3_precise_decimal() {
let a = test_pdec!("10");
assert_eq!(a.checked_powi(3).unwrap().to_string(), "1000");
}
#[test]
fn test_5_powi_2_precise_decimal() {
let a = test_pdec!("5");
assert_eq!(a.checked_powi(2).unwrap().to_string(), "25");
}
#[test]
fn test_5_powi_minus2_precise_decimal() {
let a = test_pdec!("5");
assert_eq!(a.checked_powi(-2).unwrap().to_string(), "0.04");
}
#[test]
fn test_10_powi_minus3_precise_decimal() {
let a = test_pdec!("10");
assert_eq!(a.checked_powi(-3).unwrap().to_string(), "0.001");
}
#[test]
fn test_minus10_powi_minus3_precise_decimal() {
let a = test_pdec!("-10");
assert_eq!(a.checked_powi(-3).unwrap().to_string(), "-0.001");
}
#[test]
fn test_minus10_powi_minus2_precise_decimal() {
let a = test_pdec!("-10");
assert_eq!(a.checked_powi(-2).unwrap().to_string(), "0.01");
}
#[test]
fn test_minus05_powi_minus2_precise_decimal() {
let a = test_pdec!("-0.5");
assert_eq!(a.checked_powi(-2).unwrap().to_string(), "4");
}
#[test]
fn test_minus05_powi_minus3_precise_decimal() {
let a = test_pdec!("-0.5");
assert_eq!(a.checked_powi(-3).unwrap().to_string(), "-8");
}
#[test]
fn test_10_powi_15_precise_decimal() {
let a = test_pdec!(10i128);
assert_eq!(a.checked_powi(15).unwrap().to_string(), "1000000000000000");
}
#[test]
fn test_10_powi_16_precise_decimal() {
let a = PreciseDecimal(10i128.into());
assert_eq!(a.checked_powi(16).unwrap().to_string(), "0");
}
#[test]
fn test_one_and_zero_precise_decimal() {
assert_eq!(PreciseDecimal::one().to_string(), "1");
assert_eq!(PreciseDecimal::zero().to_string(), "0");
}
#[test]
fn test_dec_string_decimal_precise_decimal() {
assert_eq!(
test_pdec!("1.123456789012345678").to_string(),
"1.123456789012345678"
);
assert_eq!(test_pdec!("-5.6").to_string(), "-5.6");
}
#[test]
fn test_dec_string_precise_decimal() {
assert_eq!(test_pdec!(1).to_string(), "1");
assert_eq!(test_pdec!("0").to_string(), "0");
}
#[test]
fn test_dec_int_precise_decimal() {
assert_eq!(test_pdec!(1).to_string(), "1");
assert_eq!(test_pdec!(5).to_string(), "5");
}
#[test]
fn test_dec_bool_precise_decimal() {
assert_eq!((test_pdec!(false)).to_string(), "0");
}
#[test]
fn test_floor_precise_decimal() {
assert_eq!(
PreciseDecimal::MAX.checked_floor().unwrap(),
test_pdec!("57896044618658097711785492504343953926634")
);
assert_eq!(test_pdec!("1.2").checked_floor().unwrap(), test_pdec!("1"));
assert_eq!(test_pdec!("1.0").checked_floor().unwrap(), test_pdec!("1"));
assert_eq!(test_pdec!("0.9").checked_floor().unwrap(), test_pdec!("0"));
assert_eq!(test_pdec!("0").checked_floor().unwrap(), test_pdec!("0"));
assert_eq!(
test_pdec!("-0.1").checked_floor().unwrap(),
test_pdec!("-1")
);
assert_eq!(test_pdec!("-1").checked_floor().unwrap(), test_pdec!("-1"));
assert_eq!(
test_pdec!("-5.2").checked_floor().unwrap(),
test_pdec!("-6")
);
assert_eq!(
test_pdec!(
"-57896044618658097711785492504343953926633.992332820282019728792003956564819968"
) .checked_floor()
.unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
assert_eq!(
test_pdec!(
"-57896044618658097711785492504343953926633.000000000000000000000000000000000001"
)
.checked_floor()
.unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
assert_eq!(
test_pdec!(
"-57896044618658097711785492504343953926634.000000000000000000000000000000000000"
)
.checked_floor()
.unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
assert!(PreciseDecimal::MIN.checked_floor().is_none());
assert!(test_pdec!(
"-57896044618658097711785492504343953926634.000000000000000000000000000000000001"
)
.checked_floor()
.is_none());
}
#[test]
fn test_abs_precise_decimal() {
assert_eq!(test_pdec!(-2).checked_abs().unwrap(), test_pdec!(2));
assert_eq!(test_pdec!(2).checked_abs().unwrap(), test_pdec!(2));
assert_eq!(test_pdec!(0).checked_abs().unwrap(), test_pdec!(0));
assert_eq!(
PreciseDecimal::MAX.checked_abs().unwrap(),
PreciseDecimal::MAX
);
assert!(PreciseDecimal::MIN.checked_abs().is_none());
}
#[test]
fn test_ceiling_precise_decimal() {
assert_eq!(
test_pdec!("1.2").checked_ceiling().unwrap(),
test_pdec!("2")
);
assert_eq!(
test_pdec!("1.0").checked_ceiling().unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("0.9").checked_ceiling().unwrap(),
test_pdec!("1")
);
assert_eq!(test_pdec!("0").checked_ceiling().unwrap(), test_pdec!("0"));
assert_eq!(
test_pdec!("-0.1").checked_ceiling().unwrap(),
test_pdec!("0")
);
assert_eq!(
test_pdec!("-1").checked_ceiling().unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-5.2").checked_ceiling().unwrap(),
test_pdec!("-5")
);
assert_eq!(
PreciseDecimal::MIN.checked_ceiling().unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
assert_eq!(
test_pdec!(
"57896044618658097711785492504343953926633.992332820282019728792003956564819967"
) .checked_ceiling()
.unwrap(),
test_pdec!("57896044618658097711785492504343953926634")
);
assert_eq!(
test_pdec!(
"57896044618658097711785492504343953926633.000000000000000000000000000000000000"
)
.checked_ceiling()
.unwrap(),
test_pdec!("57896044618658097711785492504343953926633")
);
assert!(PreciseDecimal::MAX.checked_ceiling().is_none());
assert!(test_pdec!(
"57896044618658097711785492504343953926634.000000000000000000000000000000000001"
)
.checked_ceiling()
.is_none());
}
#[test]
fn test_rounding_to_zero_precise_decimal() {
let mode = RoundingMode::ToZero;
assert_eq!(
test_pdec!("1.2").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("1.0").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("0.9").checked_round(0, mode).unwrap(),
test_pdec!("0")
);
assert_eq!(
test_pdec!("0").checked_round(0, mode).unwrap(),
test_pdec!("0")
);
assert_eq!(
test_pdec!("-0.1").checked_round(0, mode).unwrap(),
test_pdec!("0")
);
assert_eq!(
test_pdec!("-1").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-5.2").checked_round(0, mode).unwrap(),
test_pdec!("-5")
);
assert_eq!(
PreciseDecimal::MAX.checked_round(0, mode).unwrap(),
test_pdec!("57896044618658097711785492504343953926634")
);
assert_eq!(
PreciseDecimal::MIN.checked_round(0, mode).unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
}
#[test]
fn test_rounding_away_from_zero_precise_decimal() {
let mode = RoundingMode::AwayFromZero;
assert_eq!(
test_pdec!("1.2").checked_round(0, mode).unwrap(),
test_pdec!("2")
);
assert_eq!(
test_pdec!("1.0").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("0.9").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("0").checked_round(0, mode).unwrap(),
test_pdec!("0")
);
assert_eq!(
test_pdec!("-0.1").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-1").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-5.2").checked_round(0, mode).unwrap(),
test_pdec!("-6")
);
assert!(PreciseDecimal::MIN.checked_round(0, mode).is_none());
assert!(test_pdec!("-57896044618658097711785492504343953926634.1")
.checked_round(0, mode)
.is_none());
assert!(PreciseDecimal::MAX.checked_round(0, mode).is_none());
assert!(test_pdec!("57896044618658097711785492504343953926634.1")
.checked_round(0, mode)
.is_none());
}
#[test]
fn test_rounding_midpoint_toward_zero_precise_decimal() {
let mode = RoundingMode::ToNearestMidpointTowardZero;
assert_eq!(
test_pdec!("5.5").checked_round(0, mode).unwrap(),
test_pdec!("5")
);
assert_eq!(
test_pdec!("2.5").checked_round(0, mode).unwrap(),
test_pdec!("2")
);
assert_eq!(
test_pdec!("1.6").checked_round(0, mode).unwrap(),
test_pdec!("2")
);
assert_eq!(
test_pdec!("1.1").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("1.0").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("-1.0").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-1.1").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-1.6").checked_round(0, mode).unwrap(),
test_pdec!("-2")
);
assert_eq!(
test_pdec!("-2.5").checked_round(0, mode).unwrap(),
test_pdec!("-2")
);
assert_eq!(
test_pdec!("-5.5").checked_round(0, mode).unwrap(),
test_pdec!("-5")
);
assert_eq!(
test_pdec!("-57896044618658097711785492504343953926634.5")
.checked_round(0, mode)
.unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
assert_eq!(
test_pdec!("57896044618658097711785492504343953926634.5")
.checked_round(0, mode)
.unwrap(),
test_pdec!("57896044618658097711785492504343953926634")
);
assert!(PreciseDecimal::MIN.checked_round(0, mode).is_none());
assert!(test_pdec!("-57896044618658097711785492504343953926634.6")
.checked_round(0, mode)
.is_none());
assert!(PreciseDecimal::MAX.checked_round(0, mode).is_none());
assert!(test_pdec!("57896044618658097711785492504343953926634.6")
.checked_round(0, mode)
.is_none());
}
#[test]
fn test_rounding_midpoint_away_from_zero_precise_decimal() {
let mode = RoundingMode::ToNearestMidpointAwayFromZero;
assert_eq!(
test_pdec!("5.5").checked_round(0, mode).unwrap(),
test_pdec!("6")
);
assert_eq!(
test_pdec!("2.5").checked_round(0, mode).unwrap(),
test_pdec!("3")
);
assert_eq!(
test_pdec!("1.6").checked_round(0, mode).unwrap(),
test_pdec!("2")
);
assert_eq!(
test_pdec!("1.1").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("1.0").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("-1.0").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-1.1").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-1.6").checked_round(0, mode).unwrap(),
test_pdec!("-2")
);
assert_eq!(
test_pdec!("-2.5").checked_round(0, mode).unwrap(),
test_pdec!("-3")
);
assert_eq!(
test_pdec!("-5.5").checked_round(0, mode).unwrap(),
test_pdec!("-6")
);
assert_eq!(
test_pdec!("-57896044618658097711785492504343953926634.4")
.checked_round(0, mode)
.unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
assert_eq!(
test_pdec!("57896044618658097711785492504343953926634.4")
.checked_round(0, mode)
.unwrap(),
test_pdec!("57896044618658097711785492504343953926634")
);
assert!(PreciseDecimal::MIN.checked_round(0, mode).is_none());
assert!(test_pdec!("-57896044618658097711785492504343953926634.5")
.checked_round(0, mode)
.is_none());
assert!(PreciseDecimal::MAX.checked_round(0, mode).is_none());
assert!(test_pdec!("57896044618658097711785492504343953926634.5")
.checked_round(0, mode)
.is_none());
}
#[test]
fn test_rounding_midpoint_nearest_even_precise_decimal() {
let mode = RoundingMode::ToNearestMidpointToEven;
assert_eq!(
test_pdec!("5.5").checked_round(0, mode).unwrap(),
test_pdec!("6")
);
assert_eq!(
test_pdec!("2.5").checked_round(0, mode).unwrap(),
test_pdec!("2")
);
assert_eq!(
test_pdec!("1.6").checked_round(0, mode).unwrap(),
test_pdec!("2")
);
assert_eq!(
test_pdec!("1.1").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("1.0").checked_round(0, mode).unwrap(),
test_pdec!("1")
);
assert_eq!(
test_pdec!("-1.0").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-1.1").checked_round(0, mode).unwrap(),
test_pdec!("-1")
);
assert_eq!(
test_pdec!("-1.6").checked_round(0, mode).unwrap(),
test_pdec!("-2")
);
assert_eq!(
test_pdec!("-2.5").checked_round(0, mode).unwrap(),
test_pdec!("-2")
);
assert_eq!(
test_pdec!("-5.5").checked_round(0, mode).unwrap(),
test_pdec!("-6")
);
assert_eq!(
test_pdec!("-57896044618658097711785492504343953926634.5")
.checked_round(0, mode)
.unwrap(),
test_pdec!("-57896044618658097711785492504343953926634")
);
assert_eq!(
test_pdec!("57896044618658097711785492504343953926634.5")
.checked_round(0, mode)
.unwrap(),
test_pdec!("57896044618658097711785492504343953926634")
);
assert!(PreciseDecimal::MIN.checked_round(0, mode).is_none());
assert!(test_pdec!("-57896044618658097711785492504343953926634.6")
.checked_round(0, mode)
.is_none());
assert!(PreciseDecimal::MAX.checked_round(0, mode).is_none());
assert!(test_pdec!("57896044618658097711785492504343953926634.6")
.checked_round(0, mode)
.is_none());
}
#[test]
fn test_various_decimal_places_precise_decimal() {
let num = test_pdec!("2.4595");
let mode = RoundingMode::AwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("2.46"));
assert_eq!(
test_pdec!(
"57896044618658097711785492504343953926633.992332820282019728792003956564819967"
)
.checked_round(1, mode)
.unwrap(),
test_pdec!("57896044618658097711785492504343953926634.0")
);
assert_eq!(
test_pdec!(
"-57896044618658097711785492504343953926633.992332820282019728792003956564819967"
)
.checked_round(1, mode)
.unwrap(),
test_pdec!("-57896044618658097711785492504343953926634.0")
);
let mode = RoundingMode::ToZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("2.459"));
let mode = RoundingMode::ToPositiveInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("2.46"));
let mode = RoundingMode::ToNegativeInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("2.459"));
let mode = RoundingMode::ToNearestMidpointAwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("2.46"));
let mode = RoundingMode::ToNearestMidpointTowardZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("2.459"));
let mode = RoundingMode::ToNearestMidpointToEven;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("2.46"));
let num = test_pdec!("-2.4595");
let mode = RoundingMode::AwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("-3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("-2.46"));
let mode = RoundingMode::ToZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("-2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("-2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("-2.459"));
let mode = RoundingMode::ToPositiveInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("-2.4"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("-2.45"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("-2.459"));
let mode = RoundingMode::ToNegativeInfinity;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("-3"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("-2.46"));
let mode = RoundingMode::ToNearestMidpointAwayFromZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("-2.46"));
let mode = RoundingMode::ToNearestMidpointTowardZero;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("-2.459"));
let mode = RoundingMode::ToNearestMidpointToEven;
assert_eq!(num.checked_round(0, mode).unwrap(), test_pdec!("-2"));
assert_eq!(num.checked_round(1, mode).unwrap(), test_pdec!("-2.5"));
assert_eq!(num.checked_round(2, mode).unwrap(), test_pdec!("-2.46"));
assert_eq!(num.checked_round(3, mode).unwrap(), test_pdec!("-2.46"));
}
#[test]
fn test_encode_decimal_value_precise_decimal() {
let pdec = test_pdec!("0");
let bytes = scrypto_encode(&pdec).unwrap();
assert_eq!(bytes, {
let mut a = [0; 34];
a[0] = SCRYPTO_SBOR_V1_PAYLOAD_PREFIX;
a[1] = ScryptoValueKind::Custom(ScryptoCustomValueKind::PreciseDecimal).as_u8();
a
});
}
#[test]
fn test_decode_decimal_value_precise_decimal() {
let pdec = test_pdec!("1.23456789");
let bytes = scrypto_encode(&pdec).unwrap();
let decoded: PreciseDecimal = scrypto_decode(&bytes).unwrap();
assert_eq!(decoded, test_pdec!("1.23456789"));
}
#[test]
fn test_from_str_precise_decimal() {
let pdec = PreciseDecimal::from_str("5.0").unwrap();
assert_eq!(pdec.to_string(), "5");
}
#[test]
fn test_from_str_failure_precise_decimal() {
let pdec = PreciseDecimal::from_str("non_decimal_value");
assert_eq!(pdec, Err(ParsePreciseDecimalError::InvalidDigit));
}
macro_rules! test_from_into_decimal_precise_decimal {
($(($from:expr, $expected:expr, $suffix:expr)),*) => {
paste!{
$(
#[test]
fn [<test_from_into_decimal_precise_decimal_ $suffix>]() {
let dec = test_dec!($from);
let pdec = PreciseDecimal::from(dec);
assert_eq!(pdec.to_string(), $expected);
let pdec: PreciseDecimal = dec.into();
assert_eq!(pdec.to_string(), $expected);
}
)*
}
};
}
test_from_into_decimal_precise_decimal! {
("12345678.123456789012345678", "12345678.123456789012345678", 1),
("0.000000000000000001", "0.000000000000000001", 2),
("-0.000000000000000001", "-0.000000000000000001", 3),
("5", "5", 4),
("12345678.1", "12345678.1", 5)
}
macro_rules! test_try_from_integer_overflow {
($(($from:expr, $suffix:expr)),*) => {
paste!{
$(
#[test]
fn [<test_try_from_integer_overflow_ $suffix>]() {
let err = PreciseDecimal::try_from($from).unwrap_err();
assert_eq!(err, ParsePreciseDecimalError::Overflow)
}
)*
}
};
}
test_try_from_integer_overflow! {
(I192::MAX, 1),
(I192::MIN, 2),
(I256::MAX, 3),
(I256::MIN, 4),
(I320::MAX, 5),
(I320::MIN, 6),
(I448::MAX, 7),
(I448::MIN, 8),
(I512::MAX, 9),
(I512::MIN, 10),
(I256::MAX/(I256::from(10).pow(PreciseDecimal::SCALE)) + I256::ONE, 11),
(I256::MIN/(I256::from(10).pow(PreciseDecimal::SCALE)) - I256::ONE, 12),
(U192::MAX, 13),
(U256::MAX, 14),
(U320::MAX, 15),
(U448::MAX, 16),
(U512::MAX, 17)
}
macro_rules! test_try_from_integer {
($(($from:expr, $expected:expr, $suffix:expr)),*) => {
paste!{
$(
#[test]
fn [<test_try_from_integer_ $suffix>]() {
let dec = PreciseDecimal::try_from($from).unwrap();
assert_eq!(dec.to_string(), $expected)
}
)*
}
};
}
test_try_from_integer! {
(I192::ONE, "1", 1),
(-I192::ONE, "-1", 2),
(I256::ONE, "1", 3),
(-I256::ONE, "-1", 4),
(I320::ONE, "1", 5),
(-I320::ONE, "-1", 6),
(I448::ONE, "1", 7),
(-I448::ONE, "-1", 8),
(I512::ONE, "1", 9),
(-I512::ONE, "-1", 10),
(I256::MAX/(I256::from(10).pow(PreciseDecimal::SCALE)), "57896044618658097711785492504343953926634", 11),
(I256::MIN/(I256::from(10).pow(PreciseDecimal::SCALE)), "-57896044618658097711785492504343953926634", 12),
(U192::MIN, "0", 13),
(U256::MIN, "0", 14),
(U320::MIN, "0", 15),
(U448::MIN, "0", 16),
(U512::MIN, "0", 17)
}
#[test]
fn test_truncate_precise_decimal_towards_zero() {
for (pdec, dec) in [
(
test_pdec!("12345678.123456789012345678901234567890123456"),
test_dec!("12345678.123456789012345678"),
),
(test_pdec!(1), test_dec!(1)),
(test_pdec!("123.5"), test_dec!("123.5")),
(
test_pdec!("-12345678.123456789012345678901234567890123456"),
test_dec!("-12345678.123456789012345678"),
),
(
test_pdec!("-12345678.123456789012345678101234567890123456"),
test_dec!("-12345678.123456789012345678"),
),
] {
assert_eq!(pdec.checked_truncate(RoundingMode::ToZero).unwrap(), dec);
}
}
#[test]
fn test_truncate_precise_decimal_away_from_zero() {
for (pdec, dec) in [
(
test_pdec!("12345678.123456789012345678901234567890123456"),
test_dec!("12345678.123456789012345679"),
),
(test_pdec!(1), test_dec!(1)),
(test_pdec!("123.5"), test_dec!("123.5")),
(
test_pdec!("-12345678.123456789012345678901234567890123456"),
test_dec!("-12345678.123456789012345679"),
),
(
test_pdec!("-12345678.123456789012345678101234567890123456"),
test_dec!("-12345678.123456789012345679"),
),
] {
assert_eq!(
pdec.checked_truncate(RoundingMode::AwayFromZero).unwrap(),
dec
);
}
}
#[test]
fn test_sqrt() {
let sqrt_of_42 = test_pdec!(42).checked_sqrt();
let sqrt_of_0 = test_pdec!(0).checked_sqrt();
let sqrt_of_negative = test_pdec!("-1").checked_sqrt();
assert_eq!(
sqrt_of_42.unwrap(),
test_pdec!("6.480740698407860230965967436087996657")
);
assert_eq!(sqrt_of_0.unwrap(), test_pdec!(0));
assert_eq!(sqrt_of_negative, None);
}
#[test]
fn test_cbrt() {
let cbrt_of_42 = test_pdec!(42).checked_cbrt().unwrap();
let cbrt_of_0 = test_pdec!(0).checked_cbrt().unwrap();
let cbrt_of_negative_42 = test_pdec!("-42").checked_cbrt().unwrap();
assert_eq!(
cbrt_of_42,
test_pdec!("3.476026644886449786739865219004537434")
);
assert_eq!(cbrt_of_0, test_pdec!("0"));
assert_eq!(
cbrt_of_negative_42,
test_pdec!("-3.476026644886449786739865219004537434")
);
}
#[test]
fn test_nth_root() {
let root_4_42 = test_pdec!(42).checked_nth_root(4);
let root_5_42 = test_pdec!(42).checked_nth_root(5);
let root_42_42 = test_pdec!(42).checked_nth_root(42);
let root_neg_4_42 = test_pdec!("-42").checked_nth_root(4);
let root_neg_5_42 = test_pdec!("-42").checked_nth_root(5);
let root_0 = test_pdec!(42).checked_nth_root(0);
assert_eq!(
root_4_42.unwrap(),
test_pdec!("2.545729895021830518269788960576288685")
);
assert_eq!(
root_5_42.unwrap(),
test_pdec!("2.111785764966753912732567330550233486")
);
assert_eq!(
root_42_42.unwrap(),
test_pdec!("1.093072057934823618682784731855625786")
);
assert_eq!(root_neg_4_42, None);
assert_eq!(
root_neg_5_42.unwrap(),
test_pdec!("-2.111785764966753912732567330550233486")
);
assert_eq!(root_0, None);
}
#[test]
fn no_panic_with_36_decimal_places() {
let string = "1.111111111111111111111111111111111111";
let decimal = PreciseDecimal::from_str(string);
assert!(decimal.is_ok())
}
#[test]
fn no_panic_with_37_decimal_places() {
let string = "1.1111111111111111111111111111111111111";
let decimal = PreciseDecimal::from_str(string);
assert!(matches!(
decimal,
Err(ParsePreciseDecimalError::MoreThanThirtySixDecimalPlaces)
))
}
#[test]
fn test_neg_precise_decimal() {
let d = PreciseDecimal::ONE;
assert_eq!(-d, test_pdec!("-1"));
let d = PreciseDecimal::MAX;
assert_eq!(-d, PreciseDecimal(I256::MIN + I256::ONE));
}
#[test]
#[should_panic(expected = "Overflow")]
fn test_neg_precise_decimal_panic() {
let d = PreciseDecimal::MIN;
let _ = -d;
}
#[test]
fn test_arith_precise_decimal_decimal() {
let p1 = PreciseDecimal::from(Decimal::MAX);
let d1 = Decimal::from(2);
let d2 = Decimal::MAX;
let p2 = PreciseDecimal::from(2);
assert_eq!(p1.checked_mul(d1).unwrap(), d2.checked_mul(p2).unwrap());
assert_eq!(p1.checked_div(d1).unwrap(), d2.checked_div(p2).unwrap());
assert_eq!(p1.checked_add(d1).unwrap(), d2.checked_add(p2).unwrap());
assert_eq!(p1.checked_sub(d1).unwrap(), d2.checked_sub(p2).unwrap());
let p1 = PreciseDecimal::from(Decimal::MIN);
let d1 = Decimal::from(2);
let d2 = Decimal::MIN;
let p2 = PreciseDecimal::from(2);
assert_eq!(p1.checked_mul(d1).unwrap(), d2.checked_mul(p2).unwrap());
assert_eq!(p1.checked_div(d1).unwrap(), d2.checked_div(p2).unwrap());
assert_eq!(p1.checked_add(d1).unwrap(), d2.checked_add(p2).unwrap());
assert_eq!(p1.checked_sub(d1).unwrap(), d2.checked_sub(p2).unwrap());
let p1 = test_pdec!("0.000001");
let d1 = test_dec!("0.001");
let d2 = test_dec!("0.000001");
let p2 = test_pdec!("0.001");
assert_eq!(p1.checked_mul(d1).unwrap(), d2.checked_mul(p2).unwrap());
assert_eq!(p1.checked_div(d1).unwrap(), d2.checked_div(p2).unwrap());
assert_eq!(p1.checked_add(d1).unwrap(), d2.checked_add(p2).unwrap());
assert_eq!(p1.checked_sub(d1).unwrap(), d2.checked_sub(p2).unwrap());
let p1 = test_pdec!("0.000000000000000001");
let d1 = Decimal::MIN;
let d2 = test_dec!("0.000000000000000001");
let p2 = PreciseDecimal::from(Decimal::MIN);
assert_eq!(p1.checked_mul(d1).unwrap(), d2.checked_mul(p2).unwrap());
assert_eq!(p1.checked_div(d1).unwrap(), d2.checked_div(p2).unwrap());
assert_eq!(p1.checked_add(d1).unwrap(), d2.checked_add(p2).unwrap());
assert_eq!(p1.checked_sub(d1).unwrap(), d2.checked_sub(p2).unwrap());
let p1 = PreciseDecimal::ZERO;
let d1 = Decimal::ONE;
let d2 = Decimal::ZERO;
let p2 = PreciseDecimal::ONE;
assert_eq!(p1.checked_mul(d1).unwrap(), d2.checked_mul(p2).unwrap());
assert_eq!(p1.checked_div(d1).unwrap(), d2.checked_div(p2).unwrap());
assert_eq!(p1.checked_add(d1).unwrap(), d2.checked_add(p2).unwrap());
assert_eq!(p1.checked_sub(d1).unwrap(), d2.checked_sub(p2).unwrap());
}
macro_rules! test_arith_precise_decimal_primitive {
($type:ident) => {
paste! {
#[test]
fn [<test_arith_precise_decimal_$type>]() {
let p1 = test_pdec!("2");
let u1 = 4 as $type;
assert_eq!(p1.checked_add(u1).unwrap(), test_pdec!("6"));
assert_eq!(p1.checked_sub(u1).unwrap(), test_pdec!("-2"));
assert_eq!(p1.checked_mul(u1).unwrap(), test_pdec!("8"));
assert_eq!(p1.checked_div(u1).unwrap(), test_pdec!("0.5"));
let p1 = test_pdec!("2");
let u1 = $type::MAX;
let p2 = PreciseDecimal::from($type::MAX);
assert_eq!(p1.checked_add(u1).unwrap(), p1.checked_add(p2).unwrap());
assert_eq!(p1.checked_sub(u1).unwrap(), p1.checked_sub(p2).unwrap());
assert_eq!(p1.checked_mul(u1).unwrap(), p1.checked_mul(p2).unwrap());
assert_eq!(p1.checked_div(u1).unwrap(), p1.checked_div(p2).unwrap());
let p1 = PreciseDecimal::from($type::MIN);
let u1 = 2 as $type;
let p2 = test_pdec!("2");
assert_eq!(p1.checked_add(u1).unwrap(), p1.checked_add(p2).unwrap());
assert_eq!(p1.checked_sub(u1).unwrap(), p1.checked_sub(p2).unwrap());
assert_eq!(p1.checked_mul(u1).unwrap(), p1.checked_mul(p2).unwrap());
assert_eq!(p1.checked_div(u1).unwrap(), p1.checked_div(p2).unwrap());
}
}
};
}
test_arith_precise_decimal_primitive!(u8);
test_arith_precise_decimal_primitive!(u16);
test_arith_precise_decimal_primitive!(u32);
test_arith_precise_decimal_primitive!(u64);
test_arith_precise_decimal_primitive!(u128);
test_arith_precise_decimal_primitive!(usize);
test_arith_precise_decimal_primitive!(i8);
test_arith_precise_decimal_primitive!(i16);
test_arith_precise_decimal_primitive!(i32);
test_arith_precise_decimal_primitive!(i64);
test_arith_precise_decimal_primitive!(i128);
test_arith_precise_decimal_primitive!(isize);
macro_rules! test_arith_precise_decimal_integer {
($type:ident) => {
paste! {
#[test]
fn [<test_arith_precise_decimal_$type:lower>]() {
let d1 = test_pdec!("2");
let u1 = $type::try_from(4).unwrap();
let u2 = $type::try_from(2).unwrap();
let d2 = test_pdec!("4");
assert_eq!(d1.checked_add(u1).unwrap(), u2.checked_add(d2).unwrap());
assert_eq!(d1.checked_sub(u1).unwrap(), u2.checked_sub(d2).unwrap());
assert_eq!(d1.checked_mul(u1).unwrap(), u2.checked_mul(d2).unwrap());
assert_eq!(d1.checked_div(u1).unwrap(), u2.checked_div(d2).unwrap());
let d1 = test_pdec!("2");
let u1 = $type::MAX;
assert!(d1.checked_add(u1).is_none());
assert!(d1.checked_sub(u1).is_none());
assert!(d1.checked_mul(u1).is_none());
assert!(d1.checked_div(u1).is_none());
let d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(2).unwrap();
assert_eq!(d1.checked_add(u1), None);
assert_eq!(d1.checked_sub(u1).unwrap(), PreciseDecimal::MAX - test_dec!("2"));
assert_eq!(d1.checked_mul(u1), None);
assert_eq!(d1.checked_div(u1).unwrap(), PreciseDecimal::MAX / test_dec!("2"));
}
}
};
}
test_arith_precise_decimal_integer!(I192);
test_arith_precise_decimal_integer!(I256);
test_arith_precise_decimal_integer!(I320);
test_arith_precise_decimal_integer!(I448);
test_arith_precise_decimal_integer!(I512);
test_arith_precise_decimal_integer!(U192);
test_arith_precise_decimal_integer!(U256);
test_arith_precise_decimal_integer!(U320);
test_arith_precise_decimal_integer!(U448);
test_arith_precise_decimal_integer!(U512);
macro_rules! test_math_operands_decimal {
($type:ident) => {
paste! {
#[test]
fn [<test_math_operands_precise_decimal_$type:lower>]() {
let d1 = test_pdec!("2");
let u1 = $type::try_from(4).unwrap();
assert_eq!(d1 + u1, test_pdec!("6"));
assert_eq!(d1 - u1, test_pdec!("-2"));
assert_eq!(d1 * u1, test_pdec!("8"));
assert_eq!(d1 / u1, test_pdec!("0.5"));
let u1 = $type::try_from(2).unwrap();
let d1 = test_pdec!("4");
assert_eq!(u1 + d1, test_pdec!("6"));
assert_eq!(u1 - d1, test_pdec!("-2"));
assert_eq!(u1 * d1, test_pdec!("8"));
assert_eq!(u1 / d1, test_pdec!("0.5"));
let u1 = $type::try_from(4).unwrap();
let mut d1 = test_pdec!("2");
d1 += u1;
assert_eq!(d1, test_pdec!("6"));
let mut d1 = test_pdec!("2");
d1 -= u1;
assert_eq!(d1, test_pdec!("-2"));
let mut d1 = test_pdec!("2");
d1 *= u1;
assert_eq!(d1, test_pdec!("8"));
let mut d1 = test_pdec!("2");
d1 /= u1;
assert_eq!(d1, test_pdec!("0.5"));
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_add_precise_decimal_$type:lower _panic>]() {
let d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(1).unwrap();
let _ = d1 + u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_add_$type:lower _xprecise_decimal_panic>]() {
let d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(1).unwrap();
let _ = u1 + d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_sub_precise_decimal_$type:lower _panic>]() {
let d1 = PreciseDecimal::MIN;
let u1 = $type::try_from(1).unwrap();
let _ = d1 - u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_sub_$type:lower _xprecise_precise_decimal_panic>]() {
let d1 = PreciseDecimal::MIN;
let u1 = $type::try_from(1).unwrap();
let _ = u1 - d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_mul_precise_decimal_$type:lower _panic>]() {
let d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(2).unwrap();
let _ = d1 * u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_mul_$type:lower _xprecise_decimal_panic>]() {
let d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(2).unwrap();
let _ = u1 * d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_div_zero_precise_decimal_$type:lower _panic>]() {
let d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(0).unwrap();
let _ = d1 / u1;
}
#[test]
#[should_panic(expected = "Overflow or division by zero")]
fn [<test_math_div_zero_$type:lower _xdecimal_panic>]() {
let d1 = PreciseDecimal::ZERO;
let u1 = $type::try_from(1).unwrap();
let _ = u1 / d1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_add_assign_precise_decimal_$type:lower _panic>]() {
let mut d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(1).unwrap();
d1 += u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_sub_assign_precise_decimal_$type:lower _panic>]() {
let mut d1 = PreciseDecimal::MIN;
let u1 = $type::try_from(1).unwrap();
d1 -= u1;
}
#[test]
#[should_panic(expected = "Overflow")]
fn [<test_math_mul_assign_precise_decimal_$type:lower _panic>]() {
let mut d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(2).unwrap();
d1 *= u1;
}
#[test]
#[should_panic(expected = "Overflow or division by zero")]
fn [<test_math_div_assign_precise_decimal_$type:lower _panic>]() {
let mut d1 = PreciseDecimal::MAX;
let u1 = $type::try_from(0).unwrap();
d1 /= u1;
}
}
};
}
test_math_operands_decimal!(PreciseDecimal);
test_math_operands_decimal!(u8);
test_math_operands_decimal!(u16);
test_math_operands_decimal!(u32);
test_math_operands_decimal!(u64);
test_math_operands_decimal!(u128);
test_math_operands_decimal!(usize);
test_math_operands_decimal!(i8);
test_math_operands_decimal!(i16);
test_math_operands_decimal!(i32);
test_math_operands_decimal!(i64);
test_math_operands_decimal!(i128);
test_math_operands_decimal!(isize);
test_math_operands_decimal!(I192);
test_math_operands_decimal!(I256);
test_math_operands_decimal!(I320);
test_math_operands_decimal!(I448);
test_math_operands_decimal!(I512);
test_math_operands_decimal!(U192);
test_math_operands_decimal!(U256);
test_math_operands_decimal!(U320);
test_math_operands_decimal!(U448);
test_math_operands_decimal!(U512);
macro_rules! test_from_primitive_type {
($type:ident) => {
paste! {
#[test]
fn [<test_precise_decimal_from_primitive_$type>]() {
let v = $type::try_from(1).unwrap();
assert_eq!(PreciseDecimal::from(v), test_pdec!(1));
if $type::MIN != 0 {
let v = $type::try_from(-1).unwrap();
assert_eq!(PreciseDecimal::from(v), test_pdec!(-1));
}
let v = $type::MAX;
assert_eq!(PreciseDecimal::from(v), PreciseDecimal::from_str(&v.to_string()).unwrap());
let v = $type::MIN;
assert_eq!(PreciseDecimal::from(v), PreciseDecimal::from_str(&v.to_string()).unwrap());
}
}
};
}
test_from_primitive_type!(u8);
test_from_primitive_type!(u16);
test_from_primitive_type!(u32);
test_from_primitive_type!(u64);
test_from_primitive_type!(u128);
test_from_primitive_type!(usize);
test_from_primitive_type!(i8);
test_from_primitive_type!(i16);
test_from_primitive_type!(i32);
test_from_primitive_type!(i64);
test_from_primitive_type!(i128);
test_from_primitive_type!(isize);
macro_rules! test_to_primitive_type {
($type:ident) => {
paste! {
#[test]
fn [<test_precise_decimal_to_primitive_$type>]() {
let d = test_pdec!(1);
let v = $type::try_from(1).unwrap();
assert_eq!($type::try_from(d).unwrap(), v);
if $type::MIN != 0 {
let d = test_pdec!(-1);
let v = $type::try_from(-1).unwrap();
assert_eq!($type::try_from(d).unwrap(), v);
}
let v = $type::MAX;
let d = PreciseDecimal::from(v);
assert_eq!($type::try_from(d).unwrap(), v);
let v = $type::MIN;
let d = PreciseDecimal::from(v);
assert_eq!($type::try_from(d).unwrap(), v);
let d = PreciseDecimal::MAX;
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParsePreciseDecimalError::InvalidDigit);
let v = $type::MAX;
let d = PreciseDecimal::from(v).checked_add(1).unwrap();
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParsePreciseDecimalError::Overflow);
let v = $type::MIN;
let d = PreciseDecimal::from(v).checked_sub(1).unwrap();
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParsePreciseDecimalError::Overflow);
let d = test_pdec!("1.1");
let err = $type::try_from(d).unwrap_err();
assert_eq!(err, ParsePreciseDecimalError::InvalidDigit);
}
}
};
}
test_to_primitive_type!(u8);
test_to_primitive_type!(u16);
test_to_primitive_type!(u32);
test_to_primitive_type!(u64);
test_to_primitive_type!(u128);
test_to_primitive_type!(usize);
test_to_primitive_type!(i8);
test_to_primitive_type!(i16);
test_to_primitive_type!(i32);
test_to_primitive_type!(i64);
test_to_primitive_type!(i128);
test_to_primitive_type!(isize);
}