use super::{D256, divmod_d256_by_i128, banker_round_decimal_i128};
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 {
if self.value == 0 || other.value == 0 {
return Self::ZERO;
}
let result_negative = (self.value < 0) != (other.value < 0);
let a_abs = self.value.abs();
let b_abs = other.value.abs();
let a_digits = extract_decimal_digits(a_abs);
let b_digits = extract_decimal_digits(b_abs);
let product_digits = decimal_long_multiply(&a_digits, &b_digits);
let unscaled_result = assemble_decimal_digits(&product_digits);
let (quotient, remainder) = (unscaled_result / Self::SCALE, unscaled_result % Self::SCALE);
let rounded = pure_decimal_banker_round(quotient, remainder, Self::SCALE);
let final_result = if result_negative { -rounded } else { rounded };
Self { value: final_result }
}
pub fn pure_decimal_multiply_optimized_decimal(self, other: Self) -> Self {
if self.value == 0 || other.value == 0 {
return Self::ZERO;
}
let result_negative = (self.value < 0) != (other.value < 0);
let a_abs = self.value.abs();
let b_abs = other.value.abs();
if a_abs < SMALL_VALUE_THRESHOLD && b_abs < SMALL_VALUE_THRESHOLD {
return self.pure_decimal_multiply_small_optimized_decimal(other);
}
let mut a_digits = [0u8; MAX_DECIMAL_DIGITS];
let mut b_digits = [0u8; MAX_DECIMAL_DIGITS];
let a_len = extract_decimal_digits_stack_optimized(a_abs, &mut a_digits);
let b_len = extract_decimal_digits_stack_optimized(b_abs, &mut b_digits);
let mut product_digits = [0u8; MAX_DECIMAL_DIGITS * 2];
let product_len = decimal_long_multiply_stack_optimized(&a_digits[..a_len], &b_digits[..b_len], &mut product_digits);
let unscaled_result = assemble_decimal_digits_stack_optimized(&product_digits[..product_len]);
let (quotient, remainder) = (unscaled_result / Self::SCALE, unscaled_result % Self::SCALE);
let rounded = pure_decimal_banker_round(quotient, remainder, Self::SCALE);
let final_result = if result_negative { -rounded } else { rounded };
Self { value: final_result }
}
fn pure_decimal_multiply_small_optimized_decimal(self, other: Self) -> Self {
let product = self.value * other.value;
let (quotient, remainder) = (product / Self::SCALE, product % Self::SCALE);
let rounded = pure_decimal_banker_round(quotient, remainder, Self::SCALE);
Self { value: rounded }
}
pub fn multiply_0ulp_decimal(self, other: Self) -> Self {
let a_extended = D256::from_i128(self.value);
let b_extended = D256::from_i128(other.value);
let product_256 = a_extended * b_extended;
let (quotient, remainder) = divmod_d256_by_i128(product_256, Self::SCALE);
let rounded = banker_round_decimal_i128(quotient, remainder, Self::SCALE);
Self { value: rounded }
}
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 scaled_dividend = dividend_abs * Self::SCALE;
let dividend_digits = extract_decimal_digits(scaled_dividend);
let divisor_digits = extract_decimal_digits(divisor_abs);
let (quotient_digits, remainder_digits) = decimal_long_divide(÷nd_digits, &divisor_digits);
let quotient = assemble_decimal_digits("ient_digits);
let remainder = assemble_decimal_digits(&remainder_digits);
let rounded = pure_decimal_banker_round(quotient, remainder, divisor_abs);
let final_result = if result_negative { -rounded } else { rounded };
Self { value: final_result }
}
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_0ulp_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 {
let (s, c) = self.sincos();
Self { value: (s.value * Self::SCALE) / c.value }
}
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)
}
pub fn asin(&self) -> Self {
let x2 = *self * *self;
let one_minus_x2 = Self::ONE - x2;
let denom = one_minus_x2.sqrt();
let ratio = Self { value: (self.value * Self::SCALE) / denom.value };
ratio.atan()
}
pub fn acos(&self) -> Self {
use super::transcendental::pi_at_decimal_compute;
use super::transcendental::decimal_compute::{decimal_compute_halve, decimal_compute_to_i128};
let pi = pi_at_decimal_compute().expect("pi computation");
let pi_half = decimal_compute_halve(pi);
let pi_half_val = Self { value: decimal_compute_to_i128(pi_half, DECIMALS) };
pi_half_val - self.asin()
}
pub fn sinh(&self) -> Self {
let e_pos = self.exp();
let e_neg = (-*self).exp();
Self { value: (e_pos.value - e_neg.value) / 2 }
}
pub fn cosh(&self) -> Self {
let e_pos = self.exp();
let e_neg = (-*self).exp();
Self { value: (e_pos.value + e_neg.value) / 2 }
}
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 {
let two_x = Self { value: self.value * 2 };
let e2x = two_x.exp();
let num = e2x.value - Self::SCALE;
let den = e2x.value + Self::SCALE;
Self { value: (num * Self::SCALE) / den }
}
pub fn asinh(&self) -> Self {
let x2 = *self * *self;
let inner = Self { value: x2.value + Self::SCALE }; let root = inner.sqrt();
let arg = Self { value: self.value + root.value };
arg.ln()
}
pub fn acosh(&self) -> Self {
let x2 = *self * *self;
let inner = Self { value: x2.value - Self::SCALE }; let root = inner.sqrt();
let arg = Self { value: self.value + root.value };
arg.ln()
}
pub fn atanh(&self) -> Self {
let one_plus = Self { value: Self::SCALE + self.value };
let one_minus = Self { value: Self::SCALE - self.value };
let ratio = one_plus / one_minus;
let ln_ratio = ratio.ln();
Self { value: ln_ratio.value / 2 }
}
}
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
}
const MAX_DECIMAL_DIGITS: usize = 39;
const SMALL_VALUE_THRESHOLD: i128 = 1_000_000;
fn extract_decimal_digits_stack_optimized(value: i128, digits: &mut [u8; MAX_DECIMAL_DIGITS]) -> usize {
if value == 0 {
digits[0] = 0;
return 1;
}
let mut remaining = value;
let mut pos = 0;
while remaining >= 10000 && pos + 4 <= MAX_DECIMAL_DIGITS {
let chunk = remaining % 10000;
remaining /= 10000;
digits[pos] = (chunk % 10) as u8;
digits[pos + 1] = ((chunk / 10) % 10) as u8;
digits[pos + 2] = ((chunk / 100) % 10) as u8;
digits[pos + 3] = ((chunk / 1000) % 10) as u8;
pos += 4;
}
while remaining > 0 && pos < MAX_DECIMAL_DIGITS {
digits[pos] = (remaining % 10) as u8;
remaining /= 10;
pos += 1;
}
pos
}
fn assemble_decimal_digits_stack_optimized(digits: &[u8]) -> i128 {
let mut result = 0i128;
let mut power = 1i128;
let mut i = 0;
while i + 4 <= digits.len() {
let chunk = digits[i] as i128 * power +
digits[i + 1] as i128 * (power * 10) +
digits[i + 2] as i128 * (power * 100) +
digits[i + 3] as i128 * (power * 1000);
result = result.saturating_add(chunk);
power = power.saturating_mul(10000);
i += 4;
}
while i < digits.len() {
result = result.saturating_add(power.saturating_mul(digits[i] as i128));
power = power.saturating_mul(10);
i += 1;
}
result
}
fn decimal_long_multiply_stack_optimized(a_digits: &[u8], b_digits: &[u8], result: &mut [u8]) -> usize {
let result_len = a_digits.len() + b_digits.len();
for i in 0..result_len {
result[i] = 0;
}
for (i, &a_digit) in a_digits.iter().enumerate() {
if a_digit == 0 {
continue; }
let mut carry = 0u16;
let a_digit_u16 = a_digit as u16;
let mut j = 0;
while j + 3 < b_digits.len() {
for k in 0..4 {
let product = a_digit_u16 * (b_digits[j + k] as u16) + result[i + j + k] as u16 + carry;
result[i + j + k] = (product % 10) as u8;
carry = product / 10;
}
j += 4;
}
while j < b_digits.len() {
let product = a_digit_u16 * (b_digits[j] as u16) + result[i + j] as u16 + carry;
result[i + j] = (product % 10) as u8;
carry = product / 10;
j += 1;
}
if carry > 0 && i + b_digits.len() < result_len {
let carry_pos = i + b_digits.len();
let mut propagate_carry = carry as u8;
let mut pos = carry_pos;
while propagate_carry > 0 && pos < result_len {
let sum = result[pos] + propagate_carry;
result[pos] = sum % 10;
propagate_carry = sum / 10;
pos += 1;
}
}
}
let mut actual_len = result_len;
while actual_len > 1 && result[actual_len - 1] == 0 {
actual_len -= 1;
}
actual_len
}
fn extract_decimal_digits(value: i128) -> Vec<u8> {
if value == 0 {
return vec![0];
}
let mut digits = Vec::new();
let mut remaining = value;
while remaining > 0 {
digits.push((remaining % 10) as u8);
remaining /= 10;
}
digits
}
fn assemble_decimal_digits(digits: &[u8]) -> i128 {
let mut result = 0i128;
let mut power = 1i128;
for &digit in digits {
result = result.saturating_add(power.saturating_mul(digit as i128));
power = power.saturating_mul(10);
}
result
}
fn decimal_long_multiply(a_digits: &[u8], b_digits: &[u8]) -> Vec<u8> {
let mut result = vec![0u8; a_digits.len() + b_digits.len()];
for (i, &a_digit) in a_digits.iter().enumerate() {
let mut carry = 0u16;
for (j, &b_digit) in b_digits.iter().enumerate() {
let product = (a_digit as u16) * (b_digit as u16) + result[i + j] as u16 + carry;
result[i + j] = (product % 10) as u8;
carry = product / 10;
}
if carry > 0 {
result[i + b_digits.len()] = carry as u8;
}
}
while result.len() > 1 && result[result.len() - 1] == 0 {
result.pop();
}
result
}
fn decimal_long_divide(dividend_digits: &[u8], divisor_digits: &[u8]) -> (Vec<u8>, Vec<u8>) {
if divisor_digits.len() == 1 && divisor_digits[0] == 0 {
return (vec![0], vec![0]);
}
if dividend_digits.len() == 1 && dividend_digits[0] == 0 {
return (vec![0], vec![0]);
}
let dividend: Vec<u8> = dividend_digits.iter().rev().cloned().collect();
let divisor: Vec<u8> = divisor_digits.iter().rev().cloned().collect();
if is_less_than(÷nd, &divisor) {
let remainder: Vec<u8> = dividend.iter().rev().cloned().collect();
return (vec![0], remainder);
}
let mut quotient = Vec::new();
let mut current_dividend = Vec::new();
for &digit in ÷nd {
current_dividend.push(digit);
while current_dividend.len() > 1 && current_dividend[0] == 0 {
current_dividend.remove(0);
}
let mut count = 0u8;
while !is_less_than(¤t_dividend, &divisor) {
current_dividend = subtract_decimal_digits(¤t_dividend, &divisor);
count += 1;
}
quotient.push(count);
}
quotient.reverse();
current_dividend.reverse();
while quotient.len() > 1 && quotient[quotient.len() - 1] == 0 {
quotient.pop();
}
(quotient, current_dividend)
}
fn is_less_than(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return a.len() < b.len();
}
for (a_digit, b_digit) in a.iter().zip(b.iter()) {
if a_digit < b_digit {
return true;
} else if a_digit > b_digit {
return false;
}
}
false }
fn subtract_decimal_digits(a: &[u8], b: &[u8]) -> Vec<u8> {
let mut result = a.to_vec();
let mut borrow = 0u8;
let offset = a.len() - b.len();
for i in (0..a.len()).rev() {
let b_digit = if i >= offset { b[i - offset] } else { 0 };
if result[i] >= b_digit + borrow {
result[i] -= b_digit + borrow;
borrow = 0;
} else {
result[i] = result[i] + 10 - b_digit - borrow;
borrow = 1;
}
}
while result.len() > 1 && result[0] == 0 {
result.remove(0);
}
result
}
fn pure_decimal_banker_round(quotient: i128, remainder: i128, divisor: i128) -> i128 {
let half_divisor = divisor / 2;
let abs_remainder = remainder.abs();
if abs_remainder < half_divisor {
quotient } else if abs_remainder > half_divisor {
if remainder >= 0 {
quotient + 1
} else {
quotient - 1
}
} else {
if quotient % 2 == 0 {
quotient } else {
if remainder >= 0 {
quotient + 1 } else {
quotient - 1 }
}
}
}
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_0ulp_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_0ulp_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_0ulp_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_0ulp_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_0ulp_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");
}
}