#[cfg(test)]
mod arithmetic_tests {
use crate::arithmetic::U256;
use crate::decimal::Decimal;
use crate::error::ArithmeticError;
fn d(m: i128, s: u8) -> Decimal {
Decimal::new(m, s).unwrap()
}
#[test]
fn add_same_scale() {
assert_eq!(d(1, 2).checked_add(d(2, 2)).unwrap(), d(3, 2));
}
#[test]
fn add_different_scales() {
assert_eq!(d(10, 1).checked_add(d(5, 2)).unwrap(), d(105, 2));
}
#[test]
fn add_negative() {
assert_eq!(d(-5, 1).checked_add(d(3, 1)).unwrap(), d(-2, 1));
}
#[test]
fn add_zero_identity() {
let x = d(12345, 4);
assert_eq!(x.checked_add(Decimal::ZERO).unwrap(), x);
}
#[test]
fn add_overflow() {
assert_eq!(
Decimal::MAX.checked_add(d(1, 0)),
Err(ArithmeticError::Overflow)
);
}
#[test]
fn add_opposite_signs_cancel() {
let x = d(999_999, 6);
let neg = x.checked_neg().unwrap();
assert_eq!(x.checked_add(neg).unwrap().mantissa(), 0);
}
#[test]
fn sub_same_scale() {
assert_eq!(d(10, 1).checked_sub(d(3, 1)).unwrap(), d(7, 1));
}
#[test]
fn sub_different_scales() {
assert_eq!(d(10, 1).checked_sub(d(5, 2)).unwrap(), d(95, 2));
}
#[test]
fn sub_goes_negative() {
assert_eq!(d(3, 1).checked_sub(d(10, 1)).unwrap(), d(-7, 1));
}
#[test]
fn sub_self_is_zero() {
let x = d(12345, 4);
assert_eq!(x.checked_sub(x).unwrap(), d(0, 4));
}
#[test]
fn sub_underflow() {
assert_eq!(
Decimal::MIN.checked_sub(d(1, 0)),
Err(ArithmeticError::Overflow)
);
}
#[test]
fn mul_basic() {
assert_eq!(d(15, 1).checked_mul(d(20, 1)).unwrap(), d(300, 2));
}
#[test]
fn mul_by_zero() {
assert_eq!(d(999, 3).checked_mul(Decimal::ZERO).unwrap(), d(0, 3));
}
#[test]
fn mul_by_one() {
let x = d(12345, 4);
assert_eq!(x.checked_mul(Decimal::ONE).unwrap(), x);
}
#[test]
fn mul_negative_positive() {
assert_eq!(d(-3, 0).checked_mul(d(4, 0)).unwrap(), d(-12, 0));
}
#[test]
fn mul_negative_negative() {
assert_eq!(d(-3, 0).checked_mul(d(-4, 0)).unwrap(), d(12, 0));
}
#[test]
fn mul_scale_overflow() {
assert_eq!(
d(1, 20).checked_mul(d(1, 20)),
Err(ArithmeticError::ScaleExceeded)
);
}
#[test]
fn mul_mantissa_overflow() {
assert_eq!(
Decimal::MAX.checked_mul(d(2, 0)),
Err(ArithmeticError::Overflow)
);
}
#[test]
fn div_basic() {
let result = d(10, 0).checked_div(d(4, 0)).unwrap();
assert!(result.is_positive());
assert_eq!(result.to_i128_truncated(), 2);
}
#[test]
fn div_by_zero() {
assert_eq!(
d(1, 0).checked_div(Decimal::ZERO),
Err(ArithmeticError::DivisionByZero)
);
}
#[test]
fn div_self_is_near_one() {
let x = d(12345, 4);
let result = x.checked_div(x).unwrap();
assert_eq!(result.to_i128_truncated(), 1);
}
#[test]
fn div_negative_by_positive() {
let result = d(-10, 0).checked_div(d(4, 0)).unwrap();
assert!(result.is_negative());
assert_eq!(result.to_i128_truncated(), -2);
}
#[test]
fn neg_positive() {
assert_eq!(d(5, 2).checked_neg().unwrap(), d(-5, 2));
}
#[test]
fn neg_negative() {
assert_eq!(d(-5, 2).checked_neg().unwrap(), d(5, 2));
}
#[test]
fn neg_zero() {
assert_eq!(Decimal::ZERO.checked_neg().unwrap(), Decimal::ZERO);
}
#[test]
fn neg_min_overflows() {
assert_eq!(Decimal::MIN.checked_neg(), Err(ArithmeticError::Overflow));
}
#[test]
fn abs_positive() {
assert_eq!(d(5, 2).checked_abs().unwrap(), d(5, 2));
}
#[test]
fn abs_negative() {
assert_eq!(d(-5, 2).checked_abs().unwrap(), d(5, 2));
}
#[test]
fn abs_min_overflows() {
assert_eq!(Decimal::MIN.checked_abs(), Err(ArithmeticError::Overflow));
}
#[test]
fn mul_div_basic() {
let result = d(10, 0)
.checked_mul_div(d(3, 0), d(5, 0))
.unwrap();
assert_eq!(result, d(6, 0));
}
#[test]
fn mul_div_zero_numerator() {
let result = Decimal::ZERO.checked_mul_div(d(999, 3), d(1, 0)).unwrap();
assert_eq!(result.mantissa(), 0);
assert_eq!(result.scale(), 3);
}
#[test]
fn mul_div_div_by_zero() {
assert_eq!(
d(1, 0).checked_mul_div(d(1, 0), Decimal::ZERO),
Err(ArithmeticError::DivisionByZero)
);
}
#[test]
fn mul_div_prevents_naive_overflow() {
let a = Decimal::new(i128::MAX, 0).unwrap();
let two = d(2, 0);
let result = a.checked_mul_div(two, two).unwrap();
assert_eq!(result, a);
}
#[test]
fn mul_div_negative_result() {
assert_eq!(
d(-6, 0).checked_mul_div(d(3, 0), d(9, 0)).unwrap(),
d(-2, 0)
);
}
#[test]
fn mul_div_three_negatives() {
assert_eq!(
d(-6, 0).checked_mul_div(d(-3, 0), d(-9, 0)).unwrap(),
d(-2, 0)
);
}
#[test]
fn u256_zero_times_max() {
let r = U256::mul(0, u128::MAX);
assert_eq!(r, U256::ZERO);
}
#[test]
fn u256_one_times_max() {
let r = U256::mul(1, u128::MAX);
assert_eq!(r, U256 { lo: u128::MAX, hi: 0 });
}
#[test]
fn u256_large_div_exact() {
let n = U256 { lo: 0, hi: 1 };
let (q, r) = n.checked_div(2).unwrap();
assert_eq!(r, 0);
assert_eq!(q, 1u128 << 127);
}
#[test]
fn u256_div_remainder() {
let n = U256 { lo: 10, hi: 0 };
assert_eq!(n.checked_div(3), Some((3, 1)));
}
#[test]
fn u256_div_large_divisor() {
let n = U256::mul(u128::MAX, u128::MAX);
let (q, r) = n.checked_div(u128::MAX).unwrap();
assert_eq!(q, u128::MAX);
assert_eq!(r, 0);
}
#[test]
fn add_at_scale_28() {
let x = Decimal::new(1, 28).unwrap();
let y = Decimal::new(2, 28).unwrap();
assert_eq!(x.checked_add(y).unwrap(), Decimal::new(3, 28).unwrap());
}
#[test]
fn mul_scale_exactly_max() {
let x = Decimal::new(1, 14).unwrap();
assert!(x.checked_mul(x).is_ok());
}
#[test]
fn mul_scale_one_over_max() {
let x = Decimal::new(1, 14).unwrap();
let y = Decimal::new(1, 15).unwrap();
assert_eq!(x.checked_mul(y), Err(ArithmeticError::ScaleExceeded));
}
#[test]
fn new_scale_exceeded() {
assert_eq!(Decimal::new(1, 29), Err(ArithmeticError::ScaleExceeded));
}
}