use mathhook_core::prelude::*;
use num_bigint::BigInt;
use num_rational::BigRational;
mod boundary_values {
use super::*;
#[test]
fn test_zero_boundary_conditions() {
let zero = expr!(0);
let var = expr!(x);
assert_eq!(expr!(x + 0).simplify(), var);
assert_eq!(expr!(0 + x), var);
assert_eq!(expr!(x * 0).simplify(), zero);
assert_eq!(expr!(0 * x).simplify(), zero);
assert_eq!(expr!(x ^ 0).simplify(), expr!(1));
assert_eq!(expr!(0 ^ 1).simplify(), zero);
assert_eq!(expr!(0 + 0).simplify(), zero);
assert_eq!(expr!(0 * 0).simplify(), zero);
}
#[test]
fn test_unity_boundary_conditions() {
let one = expr!(1);
let var = expr!(x);
assert_eq!(expr!(x * 1).simplify(), var);
assert_eq!(expr!(1 * x).simplify(), var);
assert_eq!(expr!(x ^ 1).simplify(), var);
assert_eq!(expr!(1 ^ x).simplify(), one);
assert_eq!(expr!(1 * 1).simplify(), one);
assert_eq!(expr!(1 ^ 1).simplify().simplify(), one);
}
#[test]
fn test_negative_number_handling() {
let pos = expr!(5);
let neg = expr!(-5);
let zero = expr!(0);
assert_eq!(
Expression::add(vec![pos.clone(), neg.clone()]).simplify(),
zero
);
assert_eq!(
Expression::add(vec![neg.clone(), pos.clone()]).simplify(),
zero
);
assert_eq!(
Expression::mul(vec![pos, neg.clone()]).simplify(),
expr!(-25)
);
assert_eq!(
Expression::mul(vec![neg.clone(), neg.clone()]).simplify(),
expr!(25)
);
assert_eq!(Expression::pow(neg.clone(), expr!(2)).simplify(), expr!(25));
assert_eq!(Expression::pow(neg, expr!(3)).simplify(), expr!(-125));
}
#[test]
fn test_extreme_integer_values() {
let max_int = Expression::integer(i64::MAX);
let min_int = Expression::integer(i64::MIN);
let one = expr!(1);
let near_max = Expression::add(vec![max_int, one]);
let near_min = Expression::add(vec![min_int, Expression::integer(-1)]);
let max_result = near_max.simplify();
let min_result = near_min.simplify();
assert!(
matches!(max_result, Expression::Number(_)),
"Near-overflow result should be a number"
);
assert!(
matches!(min_result, Expression::Number(_)),
"Near-underflow result should be a number"
);
}
}
mod large_numbers {
use super::*;
#[test]
fn test_large_integer_arithmetic() {
let large_a = Expression::big_integer(
BigInt::parse_bytes(b"12345678901234567890123456789012345", 10).unwrap(),
);
let large_b = Expression::big_integer(
BigInt::parse_bytes(b"98765432109876543210987654321098765", 10).unwrap(),
);
let sum = Expression::add(vec![large_a.clone(), large_b.clone()]).simplify();
let product = Expression::mul(vec![large_a, large_b]).simplify();
assert!(
matches!(
sum,
Expression::Number(Number::BigInteger(_) | Number::Integer(_))
),
"Large number sum should be a number, got: {sum}"
);
assert!(
matches!(
product,
Expression::Number(Number::BigInteger(_) | Number::Integer(_))
),
"Large number product should be a number, got: {product}"
);
}
#[test]
fn test_large_number_gcd() {
let large_a = Expression::big_integer(BigInt::parse_bytes(b"123456789012345", 10).unwrap());
let large_b = Expression::big_integer(BigInt::parse_bytes(b"987654321098765", 10).unwrap());
let gcd_result = large_a.gcd(&large_b);
assert!(
matches!(gcd_result, Expression::Number(_)),
"GCD of large numbers should return a number"
);
assert_eq!(large_a.gcd(&large_b), large_b.gcd(&large_a));
}
#[test]
fn test_factorial_like_growth() {
let mut factorial = Expression::integer(1);
for i in 2..=20 {
factorial = Expression::mul(vec![factorial, Expression::integer(i)]).simplify();
}
let expected_factorial_20 =
Expression::big_integer(BigInt::parse_bytes(b"2432902008176640000", 10).unwrap());
assert_eq!(
factorial, expected_factorial_20,
"20! should be computed correctly"
);
}
}
mod rational_edge_cases {
use super::*;
#[test]
fn test_rational_with_large_denominators() {
let large_denom = BigInt::parse_bytes(b"123456789012345", 10).unwrap();
let rational = Expression::number(Number::rational(BigRational::new(
BigInt::from(1),
large_denom,
)));
let doubled = Expression::mul(vec![Expression::integer(2), rational]).simplify();
assert!(
matches!(doubled, Expression::Number(Number::Rational(_))),
"Rational arithmetic should preserve rational type when possible"
);
}
#[test]
fn test_rational_reduction_edge_cases() {
let test_cases = vec![
(0, 1, 0, 1), (1, 1, 1, 1), (-1, 1, -1, 1), (2, 4, 1, 2), (-6, 9, -2, 3), (100, 25, 4, 1), (17, 1, 17, 1), ];
for (num, den, exp_num, exp_den) in test_cases {
let rational = Expression::number(Number::rational(BigRational::new(
BigInt::from(num),
BigInt::from(den),
)));
let expected = Expression::number(Number::rational(BigRational::new(
BigInt::from(exp_num),
BigInt::from(exp_den),
)));
assert_eq!(
rational.simplify(),
expected,
"{num}/{den} should reduce to {exp_num}/{exp_den}"
);
}
}
#[test]
fn test_rational_arithmetic_precision() {
let test_cases = vec![
(1, 3, 1, 6, 1, 2), (2, 5, 3, 10, 7, 10), (1, 4, 1, 4, 1, 2), ];
for (a_num, a_den, b_num, b_den, exp_num, exp_den) in test_cases {
let a = Expression::number(Number::rational(BigRational::new(
BigInt::from(a_num),
BigInt::from(a_den),
)));
let b = Expression::number(Number::rational(BigRational::new(
BigInt::from(b_num),
BigInt::from(b_den),
)));
let expected = Expression::number(Number::rational(BigRational::new(
BigInt::from(exp_num),
BigInt::from(exp_den),
)));
let result = Expression::add(vec![a, b]).simplify();
assert_eq!(
result, expected,
"{a_num}/{a_den} + {b_num}/{b_den} should equal {exp_num}/{exp_den}"
);
}
}
#[test]
fn test_mixed_number_types() {
let integer = Expression::integer(3);
let rational = Expression::number(Number::rational(BigRational::new(
BigInt::from(1),
BigInt::from(2),
)));
let float_val = Expression::number(Number::float(2.5));
let mixed_expr = Expression::add(vec![integer, rational, float_val]);
let result = mixed_expr.simplify();
assert!(
matches!(result, Expression::Number(_)),
"Mixed number arithmetic should produce a number"
);
}
}
mod expression_structure {
use super::*;
#[test]
fn test_deeply_nested_expressions() {
let mut expr = Expression::integer(1);
expr = (0..49).fold(expr, |acc, _| {
Expression::add(vec![Expression::integer(1), acc])
});
let result = expr.simplify();
assert_eq!(result, Expression::integer(50));
}
#[test]
fn test_wide_expressions() {
let many_terms: Vec<Expression> = (1..=1000).map(Expression::integer).collect();
let wide_expr = Expression::add(many_terms);
let result = wide_expr.simplify();
assert_eq!(result, Expression::integer(500500)); }
#[test]
fn test_empty_expression_lists() {
let empty_add = Expression::add(vec![]);
let empty_mul = Expression::mul(vec![]);
let _add_result = empty_add.simplify();
let _mul_result = empty_mul.simplify();
}
#[test]
fn test_single_element_expressions() {
let x = symbol!(x);
let single_add = Expression::add(vec![Expression::symbol(x.clone())]);
let single_mul = Expression::mul(vec![Expression::symbol(x.clone())]);
assert_eq!(single_add.simplify(), Expression::symbol(x.clone()));
assert_eq!(single_mul.simplify(), Expression::symbol(x));
}
#[test]
fn test_alternating_operations() {
let alternating_sum = Expression::add(vec![
Expression::integer(10),
Expression::integer(-5),
Expression::integer(8),
Expression::integer(-3),
Expression::integer(7),
Expression::integer(-2),
]);
let result = alternating_sum.simplify();
assert_eq!(
result,
Expression::integer(15),
"Alternating sum should be computed correctly"
);
}
}
mod symbol_edge_cases {
use super::*;
#[test]
fn test_symbol_name_edge_cases() {
let edge_case_names = vec![
"x", "var123", "x_prime", "X", "very_long_variable_name_that_exceeds_typical_lengths", ];
for name in edge_case_names {
let symbol = Symbol::new(name);
let expr = Expression::symbol(symbol.clone());
assert_eq!(expr.simplify(), Expression::symbol(symbol));
let arithmetic = Expression::add(vec![expr.clone(), Expression::integer(1)]).simplify();
assert!(
matches!(
arithmetic,
Expression::Add(_) | Expression::Symbol(_) | Expression::Number(_)
),
"Arithmetic with symbols should produce valid expression"
);
}
}
#[test]
fn test_identical_symbols() {
let x1 = symbol!(x);
let x2 = symbol!(x);
let expr1 = Expression::symbol(x1);
let expr2 = Expression::symbol(x2);
assert_eq!(expr1, expr2);
let sum = Expression::add(vec![expr1.clone(), expr2.clone()]);
let product = Expression::mul(vec![expr1, expr2]);
let sum_result = sum.simplify();
let product_result = product.simplify();
assert!(
matches!(
sum_result,
Expression::Add(_)
| Expression::Mul(_)
| Expression::Symbol(_)
| Expression::Number(_)
),
"Sum of identical symbols should produce valid expression"
);
assert!(
matches!(
product_result,
Expression::Add(_)
| Expression::Mul(_)
| Expression::Pow(_, _)
| Expression::Symbol(_)
| Expression::Number(_)
),
"Product of identical symbols should produce valid expression"
);
}
#[test]
fn test_symbols_in_complex_expressions() {
let x = symbol!(x);
let y = symbol!(y);
let complex_expr = Expression::add(vec![
Expression::mul(vec![
Expression::integer(2),
Expression::pow(Expression::symbol(x), Expression::integer(2)),
]),
Expression::mul(vec![Expression::integer(3), Expression::symbol(y)]),
Expression::integer(5),
]);
let result = complex_expr.simplify();
assert!(
matches!(
result,
Expression::Add(_)
| Expression::Mul(_)
| Expression::Pow(_, _)
| Expression::Symbol(_)
| Expression::Number(_)
),
"Complex symbolic expression should produce valid result"
);
}
}
mod performance_edge_cases {
use super::*;
use std::time::{Duration, Instant};
#[test]
fn test_memory_efficiency_large_expressions() {
let start = Instant::now();
for _ in 0..100 {
let terms: Vec<Expression> = (1..=100).map(Expression::integer).collect();
let expr = Expression::add(terms);
let _ = expr.simplify();
}
let duration = start.elapsed();
assert!(
duration < Duration::from_secs(5),
"Large expression handling too slow: {duration:?}"
);
}
#[test]
fn test_recursive_expression_limits() {
let mut expr = Expression::integer(0);
for i in 1..=20 {
expr = Expression::add(vec![Expression::integer(i), expr]);
}
let result = expr.simplify();
assert_eq!(result, Expression::integer(210)); }
}
mod float_edge_cases {
use super::*;
#[test]
fn test_float_arithmetic_precision() {
let float_a = Expression::number(Number::float(0.1));
let float_b = Expression::number(Number::float(0.2));
let sum = Expression::add(vec![float_a, float_b]).simplify();
assert!(
matches!(sum, Expression::Number(Number::Float(_))),
"Float arithmetic should produce float result"
);
}
#[test]
fn test_float_special_values() {
let infinity = Expression::number(Number::float(f64::INFINITY));
let neg_infinity = Expression::number(Number::float(f64::NEG_INFINITY));
let nan = Expression::number(Number::float(f64::NAN));
let _ = infinity.simplify();
let _ = neg_infinity.simplify();
let _ = nan.simplify();
let expr_with_inf = Expression::add(vec![Expression::integer(5), infinity]);
let _ = expr_with_inf.simplify();
}
#[test]
fn test_float_integer_mixed_arithmetic() {
let float_val = Expression::number(Number::float(2.5));
let integer_val = Expression::integer(3);
let mixed_sum = Expression::add(vec![float_val.clone(), integer_val.clone()]).simplify();
let mixed_product = Expression::mul(vec![float_val, integer_val]).simplify();
assert!(
matches!(mixed_sum, Expression::Number(_)),
"Mixed float-integer arithmetic should produce number"
);
assert!(
matches!(mixed_product, Expression::Number(_)),
"Mixed float-integer arithmetic should produce number"
);
}
}