use mathhook_core::calculus::Limits;
use mathhook_core::{expr, symbol, Expression, MathConstant, Simplify};
#[test]
fn test_limit_polynomial_at_point() {
let x = symbol!(x);
let f = expr!(x ^ 2);
let limit = f.limit(&x, &expr!(2));
assert_eq!(limit.simplify(), expr!(4));
}
#[test]
fn test_limit_polynomial_substitution() {
let x = symbol!(x);
let f = expr!((x ^ 2) + (2 * x) + 1);
let limit = f.limit(&x, &expr!(3));
assert_eq!(limit.simplify(), expr!(16));
}
#[test]
fn test_limit_rational_cancellation() {
let x = symbol!(x);
let numerator = expr!((x ^ 2) - 4);
let denominator = expr!(x - 2);
let f = Expression::div(numerator, denominator);
let limit = f.limit(&x, &expr!(2));
assert_eq!(limit.simplify(), expr!(4));
}
#[test]
#[ignore = "BUG: limit at pole returns unevaluated limit function"]
fn test_limit_rational_at_pole() {
let x = symbol!(x);
let f = Expression::div(expr!(1), Expression::symbol(x.clone()));
let limit = f.limit(&x, &expr!(0));
let result = limit.simplify();
match result {
Expression::Constant(c) => {
assert!(matches!(
c,
MathConstant::Infinity | MathConstant::NegativeInfinity | MathConstant::Undefined
));
}
_ => panic!(
"Expected constant (Infinity or Undefined) for 1/x at 0, got {}",
result
),
}
}
#[test]
fn test_limit_polynomial_at_infinity() {
let x = symbol!(x);
let f = expr!(x ^ 2);
let limit = f.limit_at_infinity(&x);
let result = limit.simplify();
assert_eq!(result, Expression::infinity());
}
#[test]
fn test_limit_rational_same_degree_at_infinity() {
let x = symbol!(x);
let numerator = expr!((3 * (x ^ 2)) + (2 * x));
let denominator = expr!((x ^ 2) + 1);
let f = Expression::div(numerator, denominator);
let limit = f.limit_at_infinity(&x);
assert_eq!(limit.simplify(), expr!(3));
}
#[test]
fn test_limit_rational_higher_degree_numerator() {
let x = symbol!(x);
let f = Expression::div(expr!(x ^ 3), expr!(x ^ 2));
let limit = f.limit_at_infinity(&x);
assert_eq!(limit.simplify(), Expression::infinity());
}
#[test]
fn test_limit_rational_higher_degree_denominator() {
let x = symbol!(x);
let f = Expression::div(expr!(x ^ 2), expr!(x ^ 3));
let limit = f.limit_at_infinity(&x);
assert_eq!(limit.simplify(), expr!(0));
}
#[test]
fn test_limit_zero_over_zero_sin_x_over_x() {
let x = symbol!(x);
let f = Expression::div(
Expression::function("sin", vec![Expression::symbol(x.clone())]),
Expression::symbol(x.clone()),
);
let limit = f.limit(&x, &expr!(0));
assert_eq!(limit.simplify(), expr!(1));
}
#[test]
fn test_limit_infinity_over_infinity() {
let x = symbol!(x);
let f = Expression::div(
Expression::function("ln", vec![Expression::symbol(x.clone())]),
Expression::symbol(x.clone()),
);
let limit = f.limit_at_infinity(&x);
assert_eq!(limit.simplify(), expr!(0));
}
#[test]
fn test_limit_one_to_infinity_e_definition() {
let x = symbol!(x);
let base = expr!(1 + (1 / x));
let f = Expression::pow(base, Expression::symbol(x.clone()));
let limit = f.limit_at_infinity(&x);
assert_eq!(limit.simplify(), Expression::e());
}
#[test]
fn test_limit_sin_x_over_x() {
let x = symbol!(x);
let f = Expression::div(
Expression::function("sin", vec![Expression::symbol(x.clone())]),
Expression::symbol(x.clone()),
);
let limit = f.limit(&x, &expr!(0));
assert_eq!(limit.simplify(), expr!(1));
}
#[test]
fn test_limit_one_minus_cos_over_x_squared() {
let x = symbol!(x);
let numerator = expr!(1 - cos(x));
let denominator = expr!(x ^ 2);
let f = Expression::div(numerator, denominator);
let limit = f.limit(&x, &expr!(0));
assert_eq!(limit.simplify(), Expression::rational(1, 2));
}
#[test]
#[ignore = "BUG: tan(x)/x limit returns sec^2(0) instead of simplifying to 1"]
fn test_limit_tan_x_over_x() {
let x = symbol!(x);
let f = Expression::div(
Expression::function("tan", vec![Expression::symbol(x.clone())]),
Expression::symbol(x.clone()),
);
let limit = f.limit(&x, &expr!(0));
assert_eq!(limit.simplify(), expr!(1));
}
#[test]
fn test_limit_exp_minus_one_over_x() {
let x = symbol!(x);
let numerator = expr!(exp(x) - 1);
let f = Expression::div(numerator, Expression::symbol(x.clone()));
let limit = f.limit(&x, &expr!(0));
assert_eq!(limit.simplify(), expr!(1));
}
#[test]
fn test_limit_ln_one_plus_x_over_x() {
let x = symbol!(x);
let numerator = Expression::function("ln", vec![expr!(1 + x)]);
let f = Expression::div(numerator, Expression::symbol(x.clone()));
let limit = f.limit(&x, &expr!(0));
assert_eq!(limit.simplify(), expr!(1));
}
#[test]
fn test_limit_x_sin_one_over_x() {
let x = symbol!(x);
let inner = Expression::div(expr!(1), Expression::symbol(x.clone()));
let f = Expression::mul(vec![
Expression::symbol(x.clone()),
Expression::function("sin", vec![inner]),
]);
let limit = f.limit(&x, &expr!(0));
assert_eq!(limit.simplify(), expr!(0));
}
#[test]
fn test_limit_constructor_creates_calculus_expression() {
let x = symbol!(x);
let f = expr!(x ^ 2);
let limit = Expression::limit(f, x.clone(), expr!(2));
match limit {
Expression::Calculus(_) => {}
_ => panic!("Expected Calculus variant for limit expression"),
}
}