use mpdec::{Decimal, error::MPDecimalError};
use std::str::FromStr;
#[cfg(test)]
mod creation_tests {
use super::*;
#[test]
fn test_decimal_zero() {
let zero = Decimal::zero().unwrap();
assert!(zero.is_zero());
assert!(zero.is_positive());
assert!(!zero.is_negative());
assert!(zero.is_finite());
assert_eq!(zero.to_string(), "0");
}
#[test]
fn test_decimal_one() {
let one = Decimal::one().unwrap();
assert!(!one.is_zero());
assert!(one.is_positive());
assert!(!one.is_negative());
assert!(one.is_finite());
assert_eq!(one.to_string(), "1");
}
#[test]
fn test_decimal_new() {
let decimal = Decimal::checked_new().unwrap();
assert!(!decimal.is_zero());
assert!(decimal.is_finite());
}
#[test]
fn test_from_primitives() {
let u32_val = Decimal::from_u32(42).unwrap();
assert_eq!(u32_val.to_string(), "42");
assert!(u32_val.is_positive());
assert!(u32_val.is_integer());
let i32_val = Decimal::from_i32(-42).unwrap();
assert_eq!(i32_val.to_string(), "-42");
assert!(i32_val.is_negative());
assert!(i32_val.is_integer());
let u64_val = Decimal::from_u64(123456789).unwrap();
assert_eq!(u64_val.to_string(), "123456789");
assert!(u64_val.is_positive());
assert!(u64_val.is_integer());
let i64_val = Decimal::from_i64(-987654321).unwrap();
assert_eq!(i64_val.to_string(), "-987654321");
assert!(i64_val.is_negative());
assert!(i64_val.is_integer());
}
#[test]
fn test_from_string_valid() {
let test_cases = vec![
"0",
"1",
"-1",
"42",
"-42",
"3.14159",
"-2.71828",
"0.5",
"-0.25",
"123.456789",
"1000000",
"0.000001",
];
for case in test_cases {
let decimal = Decimal::from_str(case).unwrap();
assert!(decimal.is_finite());
println!("Input: {}, Output: {}", case, decimal);
}
}
#[test]
fn test_from_string_scientific() {
let test_cases = vec![
"1e0", "1E0", "1e1", "1e-1", "1.5e2", "-2.5e-3", "1e10", "1e-10",
];
for case in test_cases {
let decimal = Decimal::from_str(case).unwrap();
assert!(decimal.is_finite());
println!("Scientific: {}, Result: {}", case, decimal);
}
}
#[test]
fn test_from_string_invalid() {
let invalid_cases = vec!["", "abc", "1.2.3", "1e", "e1", "1.2e", "++1", "--1", "1..2"];
for case in invalid_cases {
let result = Decimal::from_str(case);
assert!(result.is_err(), "Expected error for: '{}'", case);
}
}
#[test]
fn test_from_float_basic() {
let f32_val = Decimal::from_f32(3.14f32).unwrap();
assert!(f32_val.is_positive());
assert!(!f32_val.is_integer());
let f64_val = Decimal::from_f64(2.718).unwrap();
assert!(f64_val.is_positive());
assert!(!f64_val.is_integer());
}
}
#[cfg(test)]
mod classification_tests {
use super::*;
#[test]
fn test_sign_classification() {
let positive = Decimal::from_str("42.5").unwrap();
assert!(positive.is_positive());
assert!(!positive.is_negative());
assert!(!positive.is_zero());
let negative = Decimal::from_str("-42.5").unwrap();
assert!(!negative.is_positive());
assert!(negative.is_negative());
assert!(!negative.is_zero());
let zero = Decimal::zero().unwrap();
assert!(!zero.is_negative());
assert!(zero.is_zero());
assert!(zero.is_positive());
}
#[test]
fn test_finite_classification() {
let normal_values = vec!["0", "1", "-1", "123.456", "1e10", "1e-10"];
for value_str in normal_values {
let decimal = Decimal::from_str(value_str).unwrap();
assert!(decimal.is_finite(), "Expected {} to be finite", value_str);
assert!(
!decimal.is_infinite(),
"Expected {} to not be infinite",
value_str
);
assert!(!decimal.is_nan(), "Expected {} to not be NaN", value_str);
assert!(
!decimal.is_special(),
"Expected {} to not be special",
value_str
);
}
}
#[test]
fn test_integer_classification() {
let integer_cases = vec![
("0", true),
("1", true),
("-1", true),
("42", true),
("-42", true),
("1000", true),
];
let non_integer_cases = vec![
("1.5", false),
("3.14159", false),
("-2.71828", false),
("0.1", false),
];
for (input, expected) in integer_cases {
let decimal = Decimal::from_str(input).unwrap();
assert_eq!(
decimal.is_integer(),
expected,
"Testing is_integer for {}",
input
);
}
for (input, expected) in non_integer_cases {
let decimal = Decimal::from_str(input).unwrap();
assert_eq!(
decimal.is_integer(),
expected,
"Testing is_integer for {}",
input
);
}
}
#[test]
fn test_even_odd_classification() {
let even_cases = vec!["0", "2", "4", "10", "-2", "-4"];
let odd_cases = vec!["1", "3", "5", "11", "-1", "-3"];
for case in even_cases {
let decimal = Decimal::from_str(case).unwrap();
if decimal.is_integer() {
assert!(decimal.is_even(), "Expected {} to be even", case);
assert!(!decimal.is_odd(), "Expected {} to not be odd", case);
}
}
for case in odd_cases {
let decimal = Decimal::from_str(case).unwrap();
if decimal.is_integer() {
assert!(decimal.is_odd(), "Expected {} to be odd", case);
assert!(!decimal.is_even(), "Expected {} to not be even", case);
}
}
}
}
#[cfg(test)]
mod arithmetic_tests {
use super::*;
#[test]
fn test_basic_addition() {
let a = Decimal::from_str("1.5").unwrap();
let b = Decimal::from_str("2.3").unwrap();
let result = a.checked_add(&b).unwrap();
assert_eq!(result.to_string(), "3.8");
let a = Decimal::from_str("1.5").unwrap();
let b = Decimal::from_str("2.3").unwrap();
let result = a + b;
assert_eq!(result.to_string(), "3.8");
}
#[test]
fn test_basic_subtraction() {
let a = Decimal::from_str("5.7").unwrap();
let b = Decimal::from_str("2.3").unwrap();
let result = a.checked_sub(&b).unwrap();
assert_eq!(result.to_string(), "3.4");
let a = Decimal::from_str("5.7").unwrap();
let b = Decimal::from_str("2.3").unwrap();
let result = a - b;
assert_eq!(result.to_string(), "3.4");
}
#[test]
fn test_basic_multiplication() {
let a = Decimal::from_str("2.5").unwrap();
let b = Decimal::from_str("4.0").unwrap();
let result = a.checked_mul(&b).unwrap();
assert!(result.to_string() == "10.0" || result.to_string() == "10.00");
let a = Decimal::from_str("2.5").unwrap();
let b = Decimal::from_str("4.0").unwrap();
let result = a * b;
assert!(result.to_string() == "10.0" || result.to_string() == "10.00");
}
#[test]
fn test_basic_division() {
let a = Decimal::from_str("10.0").unwrap();
let b = Decimal::from_str("2.5").unwrap();
let result = a.checked_div(&b).unwrap();
assert!(result.to_string() == "4" || result.to_string() == "4.0");
let a = Decimal::from_str("10.0").unwrap();
let b = Decimal::from_str("2.5").unwrap();
let result = a / b;
assert!(result.to_string() == "4" || result.to_string() == "4.0");
}
#[test]
fn test_division_by_zero() {
let dividend = Decimal::from_str("10.0").unwrap();
let zero = Decimal::zero().unwrap();
let result = dividend.checked_div(&zero);
assert!(result.is_err());
match result.unwrap_err() {
MPDecimalError::DivisionByZero => (),
other => panic!("Expected DivisionByZero, got: {:?}", other),
}
}
#[test]
fn test_arithmetic_with_integers() {
let decimal = Decimal::from_str("10.5").unwrap();
let result = decimal.clone() + 5i32;
assert_eq!(result.to_string(), "15.5");
let result = decimal.clone() - 3i32;
assert_eq!(result.to_string(), "7.5");
let result = decimal.clone() * 2i32;
assert_eq!(result.to_string(), "21.0");
let result = decimal.clone() / 2i32;
assert_eq!(result.to_string(), "5.25");
let result = decimal.clone() + 5u32;
assert_eq!(result.to_string(), "15.5");
let result = decimal.clone() + 5i64;
assert_eq!(result.to_string(), "15.5");
let result = decimal.clone() + 5u64;
assert_eq!(result.to_string(), "15.5");
}
#[test]
fn test_precision_arithmetic() {
let a = Decimal::from_str("0.1").unwrap();
let b = Decimal::from_str("0.2").unwrap();
let result = a + b;
assert_eq!(result.to_string(), "0.3");
let a = Decimal::from_str("0.1").unwrap();
let b = Decimal::from_str("0.1").unwrap();
let result = a * b;
assert_eq!(result.to_string(), "0.01");
}
#[test]
fn test_reminder() {
let a = Decimal::from_str("5").unwrap();
let b = Decimal::from_str("2").unwrap();
let result = a % b;
println!("{result}");
assert_eq!(result.to_string(), "1");
}
}
#[cfg(test)]
mod comparison_tests {
use super::*;
#[test]
fn test_equality() {
let a = Decimal::from_str("42.5").unwrap();
let b = Decimal::from_str("42.5").unwrap();
let c = Decimal::from_str("42.6").unwrap();
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn test_ordering() {
let values = vec!["-10", "-1", "0", "0.5", "1", "1.5", "10"];
let decimals: Vec<Decimal> = values
.iter()
.map(|s| Decimal::from_str(s).unwrap())
.collect();
for i in 0..decimals.len() {
for j in (i + 1)..decimals.len() {
assert!(
decimals[i] < decimals[j],
"{} should be less than {}",
values[i],
values[j]
);
assert!(
decimals[j] > decimals[i],
"{} should be greater than {}",
values[j],
values[i]
);
}
}
}
#[test]
fn test_comparison_methods() {
let a = Decimal::from_str("1.5").unwrap();
let b = Decimal::from_str("2.5").unwrap();
let c = Decimal::from_str("1.5").unwrap();
assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Less));
assert_eq!(b.partial_cmp(&a), Some(std::cmp::Ordering::Greater));
assert_eq!(a.partial_cmp(&c), Some(std::cmp::Ordering::Equal));
assert_eq!(a.cmp(&b), std::cmp::Ordering::Less);
assert_eq!(b.cmp(&a), std::cmp::Ordering::Greater);
assert_eq!(a.cmp(&c), std::cmp::Ordering::Equal);
}
}
#[cfg(test)]
mod math_operations_tests {
use super::*;
#[test]
fn test_sqrt_basic() {
let test_cases = vec![
("4", "2"),
("9", "3"),
("16", "4"),
("25", "5"),
("1", "1"),
("0", "0"),
];
for (input, expected) in test_cases {
let decimal = Decimal::from_str(input).unwrap();
let result = decimal.checked_sqrt().unwrap();
let expected_decimal = Decimal::from_str(expected).unwrap();
assert_eq!(
result, expected_decimal,
"sqrt({}) should equal {}",
input, expected
);
}
}
#[test]
fn test_sqrt_negative() {
let negative = Decimal::from_str("-4").unwrap();
let result = negative.checked_sqrt();
assert!(
result.is_err(),
"Square root of negative number should error"
);
}
#[test]
fn test_pow_basic() {
let test_cases = vec![
("2", "3", "8"),
("3", "2", "9"),
("5", "0", "1"),
("10", "2", "100"),
];
for (base_str, exp_str, expected_str) in test_cases {
let base = Decimal::from_str(base_str).unwrap();
let exponent = Decimal::from_str(exp_str).unwrap();
let result = base.checked_pow(&exponent).unwrap();
let expected = Decimal::from_str(expected_str).unwrap();
assert_eq!(
result, expected,
"{}^{} should equal {}",
base_str, exp_str, expected_str
);
}
}
#[test]
fn test_exp_ln_basic() {
let zero = Decimal::zero().unwrap();
let exp_zero = zero.checked_exp().unwrap();
let one = Decimal::one().unwrap();
assert_eq!(exp_zero, one, "exp(0) should equal 1");
let ln_one = one.checked_ln().unwrap();
assert_eq!(ln_one, zero, "ln(1) should equal 0");
}
#[test]
fn test_log10_basic() {
let test_cases = vec![("1", "0"), ("10", "1"), ("100", "2"), ("1000", "3")];
for (input_str, expected_str) in test_cases {
let input = Decimal::from_str(input_str).unwrap();
let result = input.checked_log10().unwrap();
let expected = Decimal::from_str(expected_str).unwrap();
assert_eq!(
result, expected,
"log10({}) should equal {}",
input_str, expected_str
);
}
}
#[test]
fn test_logarithm_domain_errors() {
let zero = Decimal::zero().unwrap();
let negative = Decimal::from_str("-1").unwrap();
let ln_zero_result = zero.checked_ln();
let ln_neg_result = negative.checked_ln();
println!("ln(0) result: {:?}", ln_zero_result);
println!("ln(-1) result: {:?}", ln_neg_result);
let log10_zero_result = zero.checked_log10();
let log10_neg_result = negative.checked_log10();
println!("log10(0) result: {:?}", log10_zero_result);
println!("log10(-1) result: {:?}", log10_neg_result);
}
}
#[cfg(test)]
mod sign_operations_tests {
use super::*;
#[test]
fn test_unary_operations() {
let positive = Decimal::from_str("42.5").unwrap();
let negative = Decimal::from_str("-42.5").unwrap();
let pos_result = positive.clone().checked_unary_plus().unwrap();
assert_eq!(pos_result, positive);
let neg_result = positive.clone().checked_unary_minus().unwrap();
let expected_neg = Decimal::from_str("-42.5").unwrap();
assert_eq!(neg_result, expected_neg);
let neg_op_result = -positive.clone();
assert_eq!(neg_op_result, expected_neg);
}
#[test]
fn test_abs() {
let test_cases = vec![
("0", "0"),
("1", "1"),
("-1", "1"),
("42.5", "42.5"),
("-42.5", "42.5"),
];
for (input_str, expected_str) in test_cases {
let input = Decimal::from_str(input_str).unwrap();
let result = input.checked_abs().unwrap();
let expected = Decimal::from_str(expected_str).unwrap();
assert_eq!(
result, expected,
"abs({}) should be {}",
input_str, expected_str
);
}
}
}
#[cfg(test)]
mod integral_operations_tests {
use super::*;
#[test]
fn test_floor() {
let test_cases = vec![
("1.1", "1"),
("1.9", "1"),
("-1.1", "-2"),
("-1.9", "-2"),
("42.7", "42"),
("-42.7", "-43"),
];
for (input_str, expected_str) in test_cases {
let input = Decimal::from_str(input_str).unwrap();
let result = input.checked_floor().unwrap();
let expected = Decimal::from_str(expected_str).unwrap();
assert_eq!(
result, expected,
"floor({}) should be {}",
input_str, expected_str
);
}
}
#[test]
fn test_ceil() {
let test_cases = vec![
("1.1", "2"),
("1.9", "2"),
("-1.1", "-1"),
("-1.9", "-1"),
("42.7", "43"),
("-42.7", "-42"),
];
for (input_str, expected_str) in test_cases {
let input = Decimal::from_str(input_str).unwrap();
let result = input.checked_ceil().unwrap();
let expected = Decimal::from_str(expected_str).unwrap();
assert_eq!(
result, expected,
"ceil({}) should be {}",
input_str, expected_str
);
}
}
#[test]
fn test_trunc() {
let test_cases = vec![
("1.1", "1"),
("1.9", "1"),
("-1.1", "-1"),
("-1.9", "-1"),
("42.7", "42"),
("-42.7", "-42"),
];
for (input_str, expected_str) in test_cases {
let input = Decimal::from_str(input_str).unwrap();
let result = input.checked_trunc().unwrap();
let expected = Decimal::from_str(expected_str).unwrap();
assert_eq!(
result, expected,
"trunc({}) should be {}",
input_str, expected_str
);
}
}
}
#[cfg(test)]
mod error_handling_tests {
use super::*;
#[test]
fn test_error_types() {
let zero = Decimal::zero().unwrap();
let dividend = Decimal::from_str("10").unwrap();
let div_zero_result = dividend.checked_div(&zero);
assert!(div_zero_result.is_err());
let invalid_str_result = Decimal::from_str("not_a_number");
assert!(invalid_str_result.is_err());
}
#[test]
fn test_error_display() {
let errors = vec![
MPDecimalError::ConversionSyntax,
MPDecimalError::DivisionByZero,
MPDecimalError::InvalidOperation,
MPDecimalError::Overflow,
MPDecimalError::Underflow,
];
for error in errors {
let display_str = format!("{}", error);
assert!(!display_str.is_empty());
println!("Error: {}", display_str);
}
}
}
#[cfg(test)]
mod comprehensive_integration_tests {
use super::*;
#[test]
fn test_arithmetic_properties() {
let a = Decimal::from_str("2").unwrap();
let b = Decimal::from_str("3").unwrap();
let c = Decimal::from_str("4").unwrap();
let zero = Decimal::zero().unwrap();
let one = Decimal::one().unwrap();
assert_eq!(a.clone() + zero.clone(), a); assert_eq!(a.clone() + b.clone(), b.clone() + a.clone());
assert_eq!(a.clone() * one.clone(), a); assert_eq!(a.clone() * b.clone(), b.clone() * a.clone());
let add_left = (a.clone() + b.clone()) + c.clone();
let add_right = a.clone() + (b.clone() + c.clone());
assert_eq!(add_left, add_right);
let mul_left = (a.clone() * b.clone()) * c.clone();
let mul_right = a.clone() * (b.clone() * c.clone());
assert_eq!(mul_left, mul_right);
}
#[test]
fn test_complex_calculation() {
let principal = Decimal::from_str("1000").unwrap();
let rate = Decimal::from_str("0.05").unwrap();
let time = Decimal::from_str("2").unwrap();
let one = Decimal::one().unwrap();
let one_plus_rate = one + rate;
let factor = one_plus_rate.checked_pow(&time).unwrap();
let amount = principal * factor;
let expected = Decimal::from_str("1102.5").unwrap();
assert_eq!(amount, expected);
}
#[test]
fn test_precision_preservation() {
let mut result = Decimal::from_str("1").unwrap();
let factor = Decimal::from_str("1.1").unwrap();
for _ in 0..10 {
result = result * factor.clone();
}
for _ in 0..10 {
result = result / factor.clone();
}
let one = Decimal::one().unwrap();
let diff = if result > one.clone() {
result - one.clone()
} else {
one.clone() - result
};
let tolerance = Decimal::from_str("0.000001").unwrap();
assert!(
diff < tolerance,
"Precision should be maintained through operations"
);
}
#[test]
fn test_large_number_operations() {
let large1 = Decimal::from_str("123456789012345678901234567890").unwrap();
let large2 = Decimal::from_str("987654321098765432109876543210").unwrap();
let sum = large1.clone() + large2.clone();
assert!(sum > large1);
assert!(sum > large2);
let product = large1.clone() * Decimal::from_str("2").unwrap();
assert!(product > large1);
}
#[test]
fn test_clone_and_equality() {
let original = Decimal::from_str("123.456").unwrap();
let cloned = original.clone();
assert_eq!(original, cloned);
assert_eq!(original.to_string(), cloned.to_string());
}
#[test]
fn test_display_formatting() {
let decimals = vec![
Decimal::zero().unwrap(),
Decimal::one().unwrap(),
Decimal::from_str("123.456").unwrap(),
Decimal::from_str("-789.123").unwrap(),
];
for decimal in decimals {
let display_str = format!("{}", decimal);
let to_string_str = decimal.to_string();
assert_eq!(display_str, to_string_str);
assert!(!display_str.is_empty());
println!("Decimal: {}", display_str);
}
}
}