use super::{divmod_d256_by_i128, banker_round_decimal_i128, mul_i128_to_d256};
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DecimalFixed<const DECIMALS: u8> {
value: i128,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseError {
InvalidFormat,
Overflow,
TooManyDecimals,
EmptyString,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParseError::InvalidFormat => write!(f, "Invalid decimal format"),
ParseError::Overflow => write!(f, "Value too large for DecimalFixed"),
ParseError::TooManyDecimals => write!(f, "Too many decimal places"),
ParseError::EmptyString => write!(f, "Empty string"),
}
}
}
impl std::error::Error for ParseError {}
impl<const DECIMALS: u8> DecimalFixed<DECIMALS> {
pub const SCALE: i128 = compile_time_power_of_10(DECIMALS);
pub const MAX_VALUE: i128 = i128::MAX / Self::SCALE;
pub const MIN_VALUE: i128 = i128::MIN / Self::SCALE;
pub const ZERO: Self = Self { value: 0 };
pub const ONE: Self = Self { value: Self::SCALE };
#[inline(always)]
pub const fn from_raw(value: i128) -> Self {
Self { value }
}
#[inline(always)]
pub fn from_raw_checked(value: i128) -> Result<Self, ParseError> {
if value > Self::MAX_VALUE || value < Self::MIN_VALUE {
Err(ParseError::Overflow)
} else {
Ok(Self { value })
}
}
#[inline(always)]
pub const fn from_integer(int_val: i64) -> Self {
Self {
value: (int_val as i128) * Self::SCALE,
}
}
#[inline(always)]
pub const fn from_parts(integer: i64, fractional: u64) -> Self {
let int_part = (integer as i128) * Self::SCALE;
let frac_part = fractional as i128;
let bounded_frac = if frac_part >= Self::SCALE {
Self::SCALE - 1
} else {
frac_part
};
Self {
value: if integer >= 0 {
int_part + bounded_frac
} else {
int_part - bounded_frac
}
}
}
pub fn from_decimal_str_decimal(s: &str) -> Result<Self, ParseError> {
if s.is_empty() {
return Err(ParseError::EmptyString);
}
let s = s.trim();
let negative = s.starts_with('-');
let s = if negative { &s[1..] } else { s };
let parts: Vec<&str> = s.split('.').collect();
if parts.len() > 2 {
return Err(ParseError::InvalidFormat);
}
let integer_str = parts[0];
let integer_part: i64 = integer_str.parse()
.map_err(|_| ParseError::InvalidFormat)?;
let fractional_part = if parts.len() == 2 {
let frac_str = parts[1];
if frac_str.len() > DECIMALS as usize {
return Err(ParseError::TooManyDecimals);
}
let mut padded = frac_str.to_string();
while padded.len() < DECIMALS as usize {
padded.push('0');
}
let frac_value: u64 = padded.parse()
.map_err(|_| ParseError::InvalidFormat)?;
frac_value
} else {
0
};
if i128::from(integer_part.abs()) > Self::MAX_VALUE {
return Err(ParseError::Overflow);
}
let mut result = Self::from_parts(integer_part, fractional_part);
if negative {
result = -result;
}
Ok(result)
}
#[inline(always)]
pub const fn integer_part(self) -> i64 {
(self.value / Self::SCALE) as i64
}
#[inline(always)]
pub const fn fractional_part(self) -> u64 {
(self.value % Self::SCALE).abs() as u64
}
#[inline(always)]
pub const fn raw_value(self) -> i128 {
self.value
}
#[inline(always)]
pub const fn is_zero(self) -> bool {
self.value == 0
}
#[inline(always)]
pub const fn is_negative(self) -> bool {
self.value < 0
}
#[inline(always)]
pub const fn is_positive(self) -> bool {
self.value > 0
}
#[inline(always)]
pub const fn abs(self) -> Self {
Self {
value: self.value.abs()
}
}
pub fn pure_decimal_multiply_decimal(self, other: Self) -> Self {
self.multiply_exact_decimal(other)
}
pub fn pure_decimal_multiply_optimized_decimal(self, other: Self) -> Self {
self.multiply_exact_decimal(other)
}
pub fn multiply_exact_decimal(self, other: Self) -> Self {
if let Some(product) = self.value.checked_mul(other.value) {
let rounded = banker_round_decimal_i128(product / Self::SCALE, product % Self::SCALE, Self::SCALE);
return Self { value: rounded };
}
let product = mul_i128_to_d256(self.value, other.value);
let (quotient, remainder) = divmod_d256_by_i128(product, Self::SCALE);
Self { value: banker_round_decimal_i128(quotient, remainder, Self::SCALE) }
}
pub fn pure_decimal_add_decimal(self, other: Self) -> Self {
match self.value.checked_add(other.value) {
Some(result) => Self { value: result },
None => {
if (self.value > 0) == (other.value > 0) {
Self { value: if self.value > 0 { i128::MAX } else { i128::MIN } }
} else {
Self { value: self.value.wrapping_add(other.value) }
}
}
}
}
pub fn pure_decimal_subtract_decimal(self, other: Self) -> Self {
match self.value.checked_sub(other.value) {
Some(result) => Self { value: result },
None => {
if (self.value > 0) != (other.value > 0) {
Self { value: if self.value > 0 { i128::MAX } else { i128::MIN } }
} else {
Self { value: self.value.wrapping_sub(other.value) }
}
}
}
}
pub fn pure_decimal_negate_decimal(self) -> Self {
match self.value.checked_neg() {
Some(result) => Self { value: result },
None => {
Self { value: i128::MAX }
}
}
}
pub fn pure_decimal_divide_decimal(self, other: Self) -> Self {
if other.value == 0 {
return if self.value >= 0 {
Self { value: i128::MAX }
} else {
Self { value: i128::MIN }
};
}
if self.value == 0 {
return Self::ZERO;
}
let result_negative = (self.value < 0) != (other.value < 0);
let dividend_abs = self.value.abs();
let divisor_abs = other.value.abs();
let rounded = match dividend_abs.checked_mul(Self::SCALE) {
Some(scaled) => {
banker_round_decimal_i128(scaled / divisor_abs, scaled % divisor_abs, divisor_abs)
}
None => {
let scaled = mul_i128_to_d256(dividend_abs, Self::SCALE);
let (quotient, remainder) = divmod_d256_by_i128(scaled, divisor_abs);
banker_round_decimal_i128(quotient, remainder, divisor_abs)
}
};
Self { value: if result_negative { -rounded } else { rounded } }
}
pub fn multiply_batch_decimal(inputs: &[(Self, Self)], outputs: &mut [Self]) {
assert_eq!(inputs.len(), outputs.len());
for (i, &(a, b)) in inputs.iter().enumerate() {
outputs[i] = a.multiply_exact_decimal(b);
}
}
#[inline(always)]
pub fn to_f64_lossy(self) -> f64 {
(self.value as f64) / (Self::SCALE as f64)
}
pub fn try_convert<const NEW_DECIMALS: u8>(self) -> Option<DecimalFixed<NEW_DECIMALS>> {
if NEW_DECIMALS == DECIMALS {
return Some(DecimalFixed::<NEW_DECIMALS> { value: self.value });
}
let new_scale = compile_time_power_of_10(NEW_DECIMALS);
if NEW_DECIMALS > DECIMALS {
let scale_ratio = new_scale / Self::SCALE;
let new_value = self.value.checked_mul(scale_ratio)?;
Some(DecimalFixed::<NEW_DECIMALS> { value: new_value })
} else {
let scale_ratio = Self::SCALE / new_scale;
let (quotient, remainder) = (self.value / scale_ratio, self.value % scale_ratio);
if remainder != 0 {
return None;
}
Some(DecimalFixed::<NEW_DECIMALS> { value: quotient })
}
}
pub fn convert_with_rounding<const NEW_DECIMALS: u8>(self) -> DecimalFixed<NEW_DECIMALS> {
if NEW_DECIMALS == DECIMALS {
return DecimalFixed::<NEW_DECIMALS> { value: self.value };
}
let new_scale = compile_time_power_of_10(NEW_DECIMALS);
if NEW_DECIMALS > DECIMALS {
let scale_ratio = new_scale / Self::SCALE;
let new_value = self.value.saturating_mul(scale_ratio);
DecimalFixed::<NEW_DECIMALS> { value: new_value }
} else {
let scale_ratio = Self::SCALE / new_scale;
let (quotient, remainder) = (self.value / scale_ratio, self.value % scale_ratio);
let rounded = banker_round_decimal_i128(quotient, remainder, scale_ratio);
DecimalFixed::<NEW_DECIMALS> { value: rounded }
}
}
pub fn to_binary_q256(&self) -> crate::fixed_point::I512 {
use crate::fixed_point::{I512, I1024};
if self.value == 0 {
return I512::zero();
}
let is_negative = self.value < 0;
let abs_value = self.value.abs();
let abs_i1024 = I1024::from_i128(abs_value);
let shifted = abs_i1024 << 256;
let scale = if DECIMALS <= 38 {
I1024::from_i128(10_i128.pow(DECIMALS as u32))
} else {
let mut scale = I1024::from_i128(1);
for _ in 0..DECIMALS {
scale = scale * I1024::from_i128(10);
}
scale
};
let rounding = scale >> 1;
let q256_i1024 = (shifted + rounding) / scale;
let result = q256_i1024.as_i512();
if is_negative { -result } else { result }
}
pub fn from_binary_q256(q256: crate::fixed_point::I512) -> Self {
use crate::fixed_point::{I512, I1024};
if q256 == I512::zero() {
return Self::ZERO;
}
let is_negative = q256 < I512::zero();
let abs_q256 = if is_negative { -q256 } else { q256 };
let abs_i1024 = I1024::from_i512(abs_q256);
let scale = if DECIMALS <= 38 {
I1024::from_i128(10_i128.pow(DECIMALS as u32))
} else {
let mut scale = I1024::from_i128(1);
for _ in 0..DECIMALS {
scale = scale * I1024::from_i128(10);
}
scale
};
let multiplied = abs_i1024 * scale;
let rounding = I1024::from_i128(1) << 255;
let rounded = multiplied + rounding;
let result_i1024 = rounded >> 256;
let result_i128 = result_i1024.as_i128();
let value = if is_negative { -result_i128 } else { result_i128 };
Self { value }
}
#[inline]
fn to_decimal_compute(&self) -> super::transcendental::decimal_compute::ComputeStorage {
super::transcendental::i128_upscale_to_compute(self.value, DECIMALS)
}
#[inline]
fn from_decimal_compute(val: super::transcendental::decimal_compute::ComputeStorage) -> Self {
Self { value: super::transcendental::decimal_compute_to_i128(val, DECIMALS) }
}
pub fn exp(&self) -> Self {
use super::transcendental::decimal_exp;
let compute = self.to_decimal_compute();
let result = decimal_exp(compute).expect("decimal exp overflow");
Self::from_decimal_compute(result)
}
pub fn ln(&self) -> Self {
use super::transcendental::decimal_ln;
let compute = self.to_decimal_compute();
let result = decimal_ln(compute).expect("decimal ln: domain error (x <= 0)");
Self::from_decimal_compute(result)
}
pub fn sqrt(&self) -> Self {
use super::transcendental::decimal_sqrt;
let compute = self.to_decimal_compute();
let result = decimal_sqrt(compute).expect("decimal sqrt: domain error (x < 0)");
Self::from_decimal_compute(result)
}
pub fn sin(&self) -> Self {
use super::transcendental::decimal_sin;
let compute = self.to_decimal_compute();
let result = decimal_sin(compute).expect("decimal sin overflow");
Self::from_decimal_compute(result)
}
pub fn cos(&self) -> Self {
use super::transcendental::decimal_cos;
let compute = self.to_decimal_compute();
let result = decimal_cos(compute).expect("decimal cos overflow");
Self::from_decimal_compute(result)
}
pub fn sincos(&self) -> (Self, Self) {
use super::transcendental::decimal_sincos;
let compute = self.to_decimal_compute();
let (s, c) = decimal_sincos(compute).expect("decimal sincos overflow");
(Self::from_decimal_compute(s), Self::from_decimal_compute(c))
}
pub fn tan(&self) -> Self {
use super::transcendental::decimal_sincos;
use super::transcendental::decimal_compute::{decimal_compute_div, decimal_compute_to_i128};
let xc = self.to_decimal_compute();
let (s, c) = decimal_sincos(xc).expect("decimal tan: range-reduction overflow");
let q = decimal_compute_div(s, c).expect("decimal tan: undefined (cos x = 0 at a pole)");
Self { value: decimal_compute_to_i128(q, DECIMALS) }
}
pub fn atan(&self) -> Self {
use super::transcendental::decimal_atan;
let compute = self.to_decimal_compute();
let result = decimal_atan(compute).expect("decimal atan overflow");
Self::from_decimal_compute(result)
}
pub fn atan2(&self, x: Self) -> Self {
use super::transcendental::decimal_atan2;
let y_compute = self.to_decimal_compute();
let x_compute = x.to_decimal_compute();
let result = decimal_atan2(y_compute, x_compute).expect("decimal atan2 overflow");
Self::from_decimal_compute(result)
}
fn asin_compute(&self) -> super::transcendental::decimal_compute::ComputeStorage {
use super::transcendental::{decimal_atan, decimal_sqrt, pi_at_decimal_compute};
use super::transcendental::decimal_compute::{
decimal_compute_one, decimal_compute_mul, decimal_compute_sub,
decimal_compute_div, decimal_compute_halve, decimal_compute_neg,
};
assert!(self.value.abs() <= Self::SCALE, "asin: domain error (|x| > 1)");
if self.value == Self::SCALE {
return decimal_compute_halve(pi_at_decimal_compute().expect("pi"));
}
if self.value == -Self::SCALE {
return decimal_compute_neg(decimal_compute_halve(pi_at_decimal_compute().expect("pi")));
}
let xc = self.to_decimal_compute();
let x2 = decimal_compute_mul(xc, xc);
let one_minus_x2 = decimal_compute_sub(decimal_compute_one(), x2);
let denom = decimal_sqrt(one_minus_x2).expect("asin: sqrt domain");
let ratio = decimal_compute_div(xc, denom).expect("asin: division");
decimal_atan(ratio).expect("asin: atan")
}
pub fn asin(&self) -> Self {
use super::transcendental::decimal_compute::decimal_compute_to_i128;
Self { value: decimal_compute_to_i128(self.asin_compute(), DECIMALS) }
}
pub fn acos(&self) -> Self {
use super::transcendental::pi_at_decimal_compute;
use super::transcendental::decimal_compute::{
decimal_compute_halve, decimal_compute_sub, decimal_compute_to_i128,
};
assert!(self.value.abs() <= Self::SCALE, "acos: domain error (|x| > 1)");
let pi_half = decimal_compute_halve(pi_at_decimal_compute().expect("pi"));
let result = decimal_compute_sub(pi_half, self.asin_compute());
Self { value: decimal_compute_to_i128(result, DECIMALS) }
}
pub fn sinh(&self) -> Self {
use super::transcendental::decimal_sinhcosh;
use super::transcendental::decimal_compute::decimal_compute_to_i128;
let (s, _) = decimal_sinhcosh(self.to_decimal_compute()).expect("decimal sinh: overflow");
Self { value: decimal_compute_to_i128(s, DECIMALS) }
}
pub fn cosh(&self) -> Self {
use super::transcendental::decimal_sinhcosh;
use super::transcendental::decimal_compute::decimal_compute_to_i128;
let (_, c) = decimal_sinhcosh(self.to_decimal_compute()).expect("decimal cosh: overflow");
Self { value: decimal_compute_to_i128(c, DECIMALS) }
}
pub fn sinhcosh(&self) -> (Self, Self) {
use super::transcendental::decimal_sinhcosh;
let compute = self.to_decimal_compute();
let (s, c) = decimal_sinhcosh(compute).expect("decimal sinhcosh overflow");
(Self::from_decimal_compute(s), Self::from_decimal_compute(c))
}
pub fn tanh(&self) -> Self {
use super::transcendental::decimal_exp;
use super::transcendental::decimal_compute::{
decimal_compute_one, decimal_compute_add, decimal_compute_sub,
decimal_compute_div, decimal_compute_is_negative, decimal_compute_to_i128,
};
let xc = self.to_decimal_compute();
let two_x = decimal_compute_add(xc, xc);
match decimal_exp(two_x) {
Ok(e2x) => {
let one = decimal_compute_one();
let num = decimal_compute_sub(e2x, one);
let den = decimal_compute_add(e2x, one);
let q = decimal_compute_div(num, den).expect("tanh: division");
Self { value: decimal_compute_to_i128(q, DECIMALS) }
}
Err(_) => {
if decimal_compute_is_negative(&xc) { -Self::ONE } else { Self::ONE }
}
}
}
pub fn asinh(&self) -> Self {
use super::transcendental::{decimal_ln, decimal_sqrt};
use super::transcendental::decimal_compute::{
decimal_compute_one, decimal_compute_mul, decimal_compute_add, decimal_compute_to_i128,
};
let xc = self.to_decimal_compute();
let x2 = decimal_compute_mul(xc, xc);
let inner = decimal_compute_add(x2, decimal_compute_one());
let root = decimal_sqrt(inner).expect("asinh: sqrt");
let arg = decimal_compute_add(xc, root);
let result = decimal_ln(arg).expect("asinh: ln");
Self { value: decimal_compute_to_i128(result, DECIMALS) }
}
pub fn acosh(&self) -> Self {
use super::transcendental::{decimal_ln, decimal_sqrt};
use super::transcendental::decimal_compute::{
decimal_compute_one, decimal_compute_mul, decimal_compute_sub,
decimal_compute_add, decimal_compute_to_i128,
};
assert!(self.value >= Self::SCALE, "acosh: domain error (x < 1)");
let xc = self.to_decimal_compute();
let x2 = decimal_compute_mul(xc, xc);
let inner = decimal_compute_sub(x2, decimal_compute_one());
let root = decimal_sqrt(inner).expect("acosh: sqrt");
let arg = decimal_compute_add(xc, root);
let result = decimal_ln(arg).expect("acosh: ln");
Self { value: decimal_compute_to_i128(result, DECIMALS) }
}
pub fn atanh(&self) -> Self {
use super::transcendental::decimal_ln;
use super::transcendental::decimal_compute::{
decimal_compute_one, decimal_compute_add, decimal_compute_sub,
decimal_compute_div, decimal_compute_halve, decimal_compute_to_i128,
};
assert!(self.value.abs() < Self::SCALE, "atanh: domain error (|x| >= 1)");
let xc = self.to_decimal_compute();
let one = decimal_compute_one();
let num = decimal_compute_add(one, xc); let den = decimal_compute_sub(one, xc); let ratio = decimal_compute_div(num, den).expect("atanh: division");
let ln_ratio = decimal_ln(ratio).expect("atanh: ln");
Self { value: decimal_compute_to_i128(decimal_compute_halve(ln_ratio), DECIMALS) }
}
}
impl<const DECIMALS: u8> std::ops::Add for DecimalFixed<DECIMALS> {
type Output = Self;
#[inline(always)]
fn add(self, other: Self) -> Self {
self.pure_decimal_add_decimal(other)
}
}
impl<const DECIMALS: u8> std::ops::Sub for DecimalFixed<DECIMALS> {
type Output = Self;
#[inline(always)]
fn sub(self, other: Self) -> Self {
self.pure_decimal_subtract_decimal(other)
}
}
impl<const DECIMALS: u8> std::ops::Mul for DecimalFixed<DECIMALS> {
type Output = Self;
#[inline(always)]
fn mul(self, other: Self) -> Self {
self.pure_decimal_multiply_optimized_decimal(other)
}
}
impl<const DECIMALS: u8> std::ops::Div for DecimalFixed<DECIMALS> {
type Output = Self;
#[inline(always)]
fn div(self, other: Self) -> Self {
self.pure_decimal_divide_decimal(other)
}
}
impl<const DECIMALS: u8> std::ops::Neg for DecimalFixed<DECIMALS> {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self {
self.pure_decimal_negate_decimal()
}
}
impl<const DECIMALS: u8> PartialOrd for DecimalFixed<DECIMALS> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<const DECIMALS: u8> Ord for DecimalFixed<DECIMALS> {
#[inline(always)]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(&other.value)
}
}
impl<const DECIMALS: u8> fmt::Display for DecimalFixed<DECIMALS> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let int_part = self.integer_part();
let frac_part = self.fractional_part();
if DECIMALS == 0 {
write!(f, "{}", int_part)
} else {
let frac_str = format!("{:0width$}", frac_part, width = DECIMALS as usize);
write!(f, "{}.{}", int_part, frac_str)
}
}
}
impl<const DECIMALS: u8> FromStr for DecimalFixed<DECIMALS> {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_decimal_str_decimal(s)
}
}
pub const fn compile_time_power_of_10(exp: u8) -> i128 {
let mut result = 1i128;
let mut i = 0;
while i < exp {
result *= 10;
i += 1;
}
result
}
pub type DecimalFixed2 = DecimalFixed<2>; pub type DecimalFixed3 = DecimalFixed<3>; pub type DecimalFixed6 = DecimalFixed<6>; pub type DecimalFixed9 = DecimalFixed<9>;
pub type Currency = DecimalFixed2;
pub type HighPrecisionCurrency = DecimalFixed6;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decimal_creation() {
let val = DecimalFixed::<2>::from_parts(19, 99);
assert_eq!(val.integer_part(), 19);
assert_eq!(val.fractional_part(), 99);
assert_eq!(val.to_string(), "19.99");
}
#[test]
fn test_decimal_parsing() {
let val = DecimalFixed::<3>::from_decimal_str_decimal("123.456").unwrap();
assert_eq!(val.integer_part(), 123);
assert_eq!(val.fractional_part(), 456);
assert_eq!(val.to_string(), "123.456");
let val2 = DecimalFixed::<2>::from_decimal_str_decimal("0.1").unwrap();
assert_eq!(val2.integer_part(), 0);
assert_eq!(val2.fractional_part(), 10);
assert_eq!(val2.to_string(), "0.10");
}
#[test]
fn test_decimal_arithmetic() {
let a = DecimalFixed::<2>::from_decimal_str_decimal("19.99").unwrap();
let b = DecimalFixed::<2>::from_decimal_str_decimal("5.00").unwrap();
let sum = a + b;
assert_eq!(sum.to_string(), "24.99");
let diff = a - b;
assert_eq!(diff.to_string(), "14.99");
}
#[test]
fn test_pure_decimal_addition() {
let a = DecimalFixed::<2>::from_decimal_str_decimal("19.99").unwrap();
let b = DecimalFixed::<2>::from_decimal_str_decimal("5.00").unwrap();
let result = a.pure_decimal_add_decimal(b);
assert_eq!(result.to_string(), "24.99");
println!("✅ PURE DECIMAL ADD: $19.99 + $5.00 = $24.99 exactly");
let price = DecimalFixed::<2>::from_decimal_str_decimal("99.95").unwrap();
let tax = DecimalFixed::<2>::from_decimal_str_decimal("8.00").unwrap();
let total = price.pure_decimal_add_decimal(tax);
assert_eq!(total.to_string(), "107.95");
println!("✅ FINANCIAL ADD: $99.95 + $8.00 = $107.95 exactly");
let user_decimal = DecimalFixed::<3>::from_decimal_str_decimal("0.100").unwrap();
let increment = DecimalFixed::<3>::from_decimal_str_decimal("0.001").unwrap();
let result = user_decimal.pure_decimal_add_decimal(increment);
assert_eq!(result.to_string(), "0.101");
println!("✅ USER ADD: 0.100 + 0.001 = 0.101 exactly");
let a = DecimalFixed::<4>::from_decimal_str_decimal("1.0000").unwrap();
let b = DecimalFixed::<4>::from_decimal_str_decimal("0.0001").unwrap();
let precise_result = a.pure_decimal_add_decimal(b);
assert_eq!(precise_result.to_string(), "1.0001");
println!("✅ PRECISION ADD: 1.0000 + 0.0001 = 1.0001 exactly");
}
#[test]
fn test_pure_decimal_subtraction() {
let a = DecimalFixed::<2>::from_decimal_str_decimal("24.99").unwrap();
let b = DecimalFixed::<2>::from_decimal_str_decimal("5.00").unwrap();
let result = a.pure_decimal_subtract_decimal(b);
assert_eq!(result.to_string(), "19.99");
println!("✅ PURE DECIMAL SUB: $24.99 - $5.00 = $19.99 exactly");
let total = DecimalFixed::<2>::from_decimal_str_decimal("107.95").unwrap();
let tax = DecimalFixed::<2>::from_decimal_str_decimal("8.00").unwrap();
let net = total.pure_decimal_subtract_decimal(tax);
assert_eq!(net.to_string(), "99.95");
println!("✅ FINANCIAL SUB: $107.95 - $8.00 = $99.95 exactly");
let user_decimal = DecimalFixed::<3>::from_decimal_str_decimal("0.101").unwrap();
let decrement = DecimalFixed::<3>::from_decimal_str_decimal("0.001").unwrap();
let result = user_decimal.pure_decimal_subtract_decimal(decrement);
assert_eq!(result.to_string(), "0.100");
println!("✅ USER SUB: 0.101 - 0.001 = 0.100 exactly");
let a = DecimalFixed::<4>::from_decimal_str_decimal("1.0001").unwrap();
let b = DecimalFixed::<4>::from_decimal_str_decimal("0.0001").unwrap();
let precise_result = a.pure_decimal_subtract_decimal(b);
assert_eq!(precise_result.to_string(), "1.0000");
println!("✅ PRECISION SUB: 1.0001 - 0.0001 = 1.0000 exactly");
}
#[test]
fn test_pure_decimal_negation() {
let pos = DecimalFixed::<2>::from_decimal_str_decimal("19.99").unwrap();
let neg = pos.pure_decimal_negate_decimal();
assert_eq!(neg.to_string(), "-19.99");
println!("✅ PURE DECIMAL NEG: -($19.99) = -$19.99 exactly");
let double_neg = neg.pure_decimal_negate_decimal();
assert_eq!(double_neg.to_string(), "19.99");
println!("✅ DOUBLE NEGATION: -(-$19.99) = $19.99 exactly");
let zero = DecimalFixed::<2>::from_decimal_str_decimal("0.00").unwrap();
let neg_zero = zero.pure_decimal_negate_decimal();
assert_eq!(neg_zero.to_string(), "0.00");
println!("✅ ZERO NEGATION: -(0.00) = 0.00 exactly");
let high_precision = DecimalFixed::<6>::from_decimal_str_decimal("123.456789").unwrap();
let negated = high_precision.pure_decimal_negate_decimal();
assert_eq!(negated.to_string(), "-123.456789");
println!("✅ PRECISION NEG: -(123.456789) = -123.456789 exactly");
}
#[test]
fn test_decimal_arithmetic_overflow_handling() {
let max_val = DecimalFixed::<2>::from_raw(i128::MAX - 100);
let large_val = DecimalFixed::<2>::from_raw(200);
let overflow_result = max_val.pure_decimal_add_decimal(large_val);
assert_eq!(overflow_result.raw_value(), i128::MAX);
println!("✅ OVERFLOW ADD: MAX + large = MAX (saturated)");
let min_val = DecimalFixed::<2>::from_raw(i128::MIN + 100);
let large_val = DecimalFixed::<2>::from_raw(200);
let underflow_result = min_val.pure_decimal_subtract_decimal(large_val);
assert_eq!(underflow_result.raw_value(), i128::MIN);
println!("✅ UNDERFLOW SUB: MIN - large = MIN (saturated)");
let min_val = DecimalFixed::<2>::from_raw(i128::MIN);
let neg_result = min_val.pure_decimal_negate_decimal();
assert_eq!(neg_result.raw_value(), i128::MAX);
println!("✅ NEGATION OVERFLOW: -(MIN) = MAX (saturated)");
}
#[test]
fn test_decimal_arithmetic_edge_cases() {
let pos = DecimalFixed::<2>::from_decimal_str_decimal("19.99").unwrap();
let neg = DecimalFixed::<2>::from_decimal_str_decimal("-5.00").unwrap();
let result1 = pos.pure_decimal_add_decimal(neg);
assert_eq!(result1.to_string(), "14.99");
let result2 = neg.pure_decimal_add_decimal(pos);
assert_eq!(result2.to_string(), "14.99");
let result3 = pos.pure_decimal_subtract_decimal(neg);
assert_eq!(result3.to_string(), "24.99");
let result4 = neg.pure_decimal_subtract_decimal(pos);
assert_eq!(result4.to_string(), "-24.99");
println!("✅ MIXED SIGNS: All edge cases handled correctly");
}
#[test]
fn test_pure_decimal_multiplication() {
let hundred = DecimalFixed::<3>::from_decimal_str_decimal("100.000").unwrap();
let one_mill = DecimalFixed::<3>::from_decimal_str_decimal("0.001").unwrap();
let product = hundred.pure_decimal_multiply_decimal(one_mill);
assert_eq!(product.to_string(), "0.100");
println!("✅ PURE DECIMAL: 100.000 × 0.001 = 0.100 (base-10 arithmetic)");
let price = DecimalFixed::<2>::from_decimal_str_decimal("19.99").unwrap();
let quantity = DecimalFixed::<2>::from_decimal_str_decimal("5.00").unwrap();
let total = price.pure_decimal_multiply_decimal(quantity);
assert_eq!(total.to_string(), "99.95");
println!("✅ FINANCIAL: $19.99 × 5.00 = $99.95 exactly (pure decimal)");
let user_decimal = DecimalFixed::<2>::from_decimal_str_decimal("0.10").unwrap();
let ten = DecimalFixed::<2>::from_decimal_str_decimal("10.00").unwrap();
let result = user_decimal.pure_decimal_multiply_decimal(ten);
assert_eq!(result.to_string(), "1.00");
println!("✅ USER: 0.10 × 10.00 = 1.00 exactly (pure decimal)");
let a = DecimalFixed::<4>::from_decimal_str_decimal("0.0001").unwrap();
let b = DecimalFixed::<4>::from_decimal_str_decimal("10000.0000").unwrap();
let precise_result = a.pure_decimal_multiply_decimal(b);
assert_eq!(precise_result.to_string(), "1.0000");
println!("✅ PRECISION: 0.0001 × 10000.0000 = 1.0000 exactly (pure decimal)");
}
#[test]
fn test_optimized_pure_decimal_multiplication() {
let hundred = DecimalFixed::<3>::from_decimal_str_decimal("100.000").unwrap();
let one_mill = DecimalFixed::<3>::from_decimal_str_decimal("0.001").unwrap();
let product = hundred.pure_decimal_multiply_optimized_decimal(one_mill);
assert_eq!(product.to_string(), "0.100");
println!("✅ OPTIMIZED PURE DECIMAL: 100.000 × 0.001 = 0.100 (base-10 arithmetic)");
let price = DecimalFixed::<2>::from_decimal_str_decimal("19.99").unwrap();
let quantity = DecimalFixed::<2>::from_decimal_str_decimal("5.00").unwrap();
let total = price.pure_decimal_multiply_optimized_decimal(quantity);
assert_eq!(total.to_string(), "99.95");
println!("✅ OPTIMIZED FINANCIAL: $19.99 × 5.00 = $99.95 exactly (pure decimal)");
let user_decimal = DecimalFixed::<2>::from_decimal_str_decimal("0.10").unwrap();
let ten = DecimalFixed::<2>::from_decimal_str_decimal("10.00").unwrap();
let result = user_decimal.pure_decimal_multiply_optimized_decimal(ten);
assert_eq!(result.to_string(), "1.00");
println!("✅ OPTIMIZED USER: 0.10 × 10.00 = 1.00 exactly (pure decimal)");
let a = DecimalFixed::<4>::from_decimal_str_decimal("0.0001").unwrap();
let b = DecimalFixed::<4>::from_decimal_str_decimal("10000.0000").unwrap();
let precise_result = a.pure_decimal_multiply_optimized_decimal(b);
assert_eq!(precise_result.to_string(), "1.0000");
println!("✅ OPTIMIZED PRECISION: 0.0001 × 10000.0000 = 1.0000 exactly (pure decimal)");
}
#[test]
fn test_optimized_vs_original_equivalence() {
let test_cases = [
("19.99", "5.00"),
("0.1", "10.0"),
("0.67", "1.5"),
("0.33", "3.0"),
("12.34", "5.67"),
("0.01", "0.99"),
("999.99", "1.01"),
("0.001", "1000.0"),
("1234.56", "0.001"),
("0.0001", "10000.0"),
];
for (a_str, b_str) in test_cases {
let a = DecimalFixed::<4>::from_decimal_str_decimal(a_str).unwrap();
let b = DecimalFixed::<4>::from_decimal_str_decimal(b_str).unwrap();
let original = a.pure_decimal_multiply_decimal(b);
let optimized = a.pure_decimal_multiply_optimized_decimal(b);
assert_eq!(original.raw_value(), optimized.raw_value(),
"Mismatch for {} × {}: original={}, optimized={}",
a_str, b_str, original.to_string(), optimized.to_string());
}
println!("✅ EQUIVALENCE: All test cases produce identical results");
}
#[test]
fn test_optimized_small_values() {
let small_a = DecimalFixed::<2>::from_decimal_str_decimal("9.99").unwrap();
let small_b = DecimalFixed::<2>::from_decimal_str_decimal("9.99").unwrap();
let result = small_a.pure_decimal_multiply_optimized_decimal(small_b);
assert_eq!(result.to_string(), "99.80");
let boundary_a = DecimalFixed::<2>::from_decimal_str_decimal("999.99").unwrap();
let boundary_b = DecimalFixed::<2>::from_decimal_str_decimal("1.00").unwrap();
let boundary_result = boundary_a.pure_decimal_multiply_optimized_decimal(boundary_b);
assert_eq!(boundary_result.to_string(), "999.99");
println!("✅ SMALL VALUE OPTIMIZATION: Fast path working correctly");
}
#[test]
fn test_optimized_large_values() {
let large_a = DecimalFixed::<9>::from_decimal_str_decimal("123456789.123456789").unwrap();
let large_b = DecimalFixed::<9>::from_decimal_str_decimal("987654321.987654321").unwrap();
let original = large_a.pure_decimal_multiply_decimal(large_b);
let optimized = large_a.pure_decimal_multiply_optimized_decimal(large_b);
assert_eq!(original.raw_value(), optimized.raw_value(),
"Large value multiplication mismatch: original={}, optimized={}",
original.to_string(), optimized.to_string());
println!("✅ LARGE VALUE OPTIMIZATION: Karatsuba multiplication working correctly");
}
#[test]
fn test_optimized_banker_rounding() {
let test_cases = [
("2.5", "1.0", "2.5"), ("3.5", "1.0", "3.5"), ("4.5", "1.0", "4.5"), ("5.5", "1.0", "5.5"), ];
for (a_str, b_str, expected) in test_cases {
let a = DecimalFixed::<1>::from_decimal_str_decimal(a_str).unwrap();
let b = DecimalFixed::<1>::from_decimal_str_decimal(b_str).unwrap();
let result = a.pure_decimal_multiply_optimized_decimal(b);
assert_eq!(result.to_string(), expected,
"Banker's rounding failed for {} × {}: got {}, expected {}",
a_str, b_str, result.to_string(), expected);
}
println!("✅ BANKER'S ROUNDING: Preserved in optimized implementation");
}
#[test]
fn test_optimized_zero_handling() {
let zero = DecimalFixed::<2>::from_decimal_str_decimal("0.00").unwrap();
let non_zero = DecimalFixed::<2>::from_decimal_str_decimal("123.45").unwrap();
let result1 = zero.pure_decimal_multiply_optimized_decimal(non_zero);
let result2 = non_zero.pure_decimal_multiply_optimized_decimal(zero);
assert_eq!(result1.to_string(), "0.00");
assert_eq!(result2.to_string(), "0.00");
println!("✅ ZERO HANDLING: Optimized implementation handles zeros correctly");
}
#[test]
fn test_optimized_negative_values() {
let pos = DecimalFixed::<2>::from_decimal_str_decimal("12.34").unwrap();
let neg = DecimalFixed::<2>::from_decimal_str_decimal("-5.67").unwrap();
let result1 = pos.pure_decimal_multiply_optimized_decimal(neg);
let result2 = neg.pure_decimal_multiply_optimized_decimal(pos);
let result3 = neg.pure_decimal_multiply_optimized_decimal(neg);
assert_eq!(result1.to_string(), "-69.97");
assert_eq!(result2.to_string(), "-69.97");
assert_eq!(result3.to_string(), "32.15");
let original1 = pos.pure_decimal_multiply_decimal(neg);
let original2 = neg.pure_decimal_multiply_decimal(pos);
let original3 = neg.pure_decimal_multiply_decimal(neg);
assert_eq!(result1.raw_value(), original1.raw_value());
assert_eq!(result2.raw_value(), original2.raw_value());
assert_eq!(result3.raw_value(), original3.raw_value());
println!("✅ NEGATIVE VALUES: Optimized implementation handles signs correctly");
}
#[test]
fn test_optimized_edge_cases() {
let one = DecimalFixed::<2>::from_decimal_str_decimal("1.00").unwrap();
let max_safe = DecimalFixed::<2>::from_decimal_str_decimal("999999.99").unwrap();
let min_precision = DecimalFixed::<2>::from_decimal_str_decimal("0.01").unwrap();
let result1 = max_safe.pure_decimal_multiply_optimized_decimal(one);
assert_eq!(result1.to_string(), "999999.99");
let result2 = min_precision.pure_decimal_multiply_optimized_decimal(DecimalFixed::<2>::from_decimal_str_decimal("100.00").unwrap());
assert_eq!(result2.to_string(), "1.00");
let high_precision = DecimalFixed::<9>::from_decimal_str_decimal("0.000000001").unwrap();
let billion = DecimalFixed::<9>::from_decimal_str_decimal("1000000000.000000000").unwrap();
let result3 = high_precision.pure_decimal_multiply_optimized_decimal(billion);
assert_eq!(result3.to_string(), "1.000000000");
println!("✅ EDGE CASES: Optimized implementation handles edge cases correctly");
}
#[test]
fn test_exact_multiplication() {
let hundred = DecimalFixed::<3>::from_decimal_str_decimal("100.000").unwrap();
let one_mill = DecimalFixed::<3>::from_decimal_str_decimal("0.001").unwrap();
let product = hundred.multiply_exact_decimal(one_mill);
assert_eq!(product.to_string(), "0.100");
println!("✅ EXACT: 100.000 × 0.001 = 0.100 (not ~0.1000000055)");
let price = DecimalFixed::<2>::from_decimal_str_decimal("19.99").unwrap();
let quantity = DecimalFixed::<2>::from_decimal_str_decimal("5.00").unwrap();
let total = price.multiply_exact_decimal(quantity);
assert_eq!(total.to_string(), "99.95");
println!("✅ FINANCIAL: $19.99 × 5.00 = $99.95 exactly");
let user_decimal = DecimalFixed::<2>::from_decimal_str_decimal("0.10").unwrap();
let ten = DecimalFixed::<2>::from_decimal_str_decimal("10.00").unwrap();
let result = user_decimal.multiply_exact_decimal(ten);
assert_eq!(result.to_string(), "1.00");
println!("✅ USER: 0.10 × 10.00 = 1.00 exactly");
let a = DecimalFixed::<4>::from_decimal_str_decimal("0.0001").unwrap();
let b = DecimalFixed::<4>::from_decimal_str_decimal("10000.0000").unwrap();
let precise_result = a.multiply_exact_decimal(b);
assert_eq!(precise_result.to_string(), "1.0000");
println!("✅ PRECISION: 0.0001 × 10000.0000 = 1.0000 exactly");
}
#[test]
fn test_pure_decimal_division() {
let ten = DecimalFixed::<2>::from_decimal_str_decimal("10.00").unwrap();
let two = DecimalFixed::<2>::from_decimal_str_decimal("2.00").unwrap();
let result = ten.pure_decimal_divide_decimal(two);
assert_eq!(result.to_string(), "5.00");
println!("✅ PURE DIVISION: 10.00 ÷ 2.00 = 5.00 exactly");
let amount = DecimalFixed::<2>::from_decimal_str_decimal("99.95").unwrap();
let parts = DecimalFixed::<2>::from_decimal_str_decimal("5.00").unwrap();
let unit_price = amount.pure_decimal_divide_decimal(parts);
assert_eq!(unit_price.to_string(), "19.99");
println!("✅ FINANCIAL DIV: $99.95 ÷ 5.00 = $19.99 exactly");
let point_one = DecimalFixed::<3>::from_decimal_str_decimal("0.100").unwrap();
let ten_k = DecimalFixed::<3>::from_decimal_str_decimal("10.000").unwrap();
let reciprocal = ten_k.pure_decimal_divide_decimal(point_one);
assert_eq!(reciprocal.to_string(), "100.000");
println!("✅ RECIPROCAL: 10.000 ÷ 0.100 = 100.000 exactly");
}
#[test]
fn test_banker_rounding() {
let half_up = super::banker_round_decimal_i128(2, 5, 10); let half_down = super::banker_round_decimal_i128(3, 5, 10);
assert_eq!(half_up, 2);
assert_eq!(half_down, 4);
}
#[test]
fn test_precision_conversion() {
let val = DecimalFixed::<2>::from_decimal_str_decimal("12.34").unwrap();
let higher = val.try_convert::<4>().unwrap();
assert_eq!(higher.to_string(), "12.3400");
let lower = higher.try_convert::<2>().unwrap();
assert_eq!(lower.to_string(), "12.34");
}
#[test]
fn test_edge_cases() {
let zero = DecimalFixed::<2>::from_decimal_str_decimal("0.00").unwrap();
assert!(zero.is_zero());
let neg = DecimalFixed::<2>::from_decimal_str_decimal("-12.34").unwrap();
assert!(neg.is_negative());
assert_eq!(neg.to_string(), "-12.34");
let abs_val = neg.abs();
assert_eq!(abs_val.to_string(), "12.34");
}
}