use symplex::prelude::*;
#[test]
fn regression_mul_neg_one_times_add() {
let ctx = Context::new();
let a = ctx.symbol("a");
let neg1 = ctx.int(-1);
let sum = &a + &neg1; let expr = &neg1 * ∑
eprintln!("expr = {expr}");
let result = &expr - &expr;
eprintln!("result = {result}");
assert!(
result.is_zero_structural(),
"expr - expr should be structural zero, got: {result}"
);
}
#[test]
fn regression_neg_of_product() {
let ctx = Context::new();
let a = ctx.symbol("a");
let neg1 = ctx.int(-1);
let sum = &a + &neg1;
let expr = &neg1 * ∑
eprintln!("expr = {expr}");
let neg_expr = -&expr;
eprintln!("neg(expr) = {neg_expr}");
let result = &expr + &neg_expr;
eprintln!("expr + neg(expr) = {result}");
assert!(
result.is_zero_structural(),
"expr + neg(expr) should be structural zero, got: {result}"
);
}
#[test]
fn regression_simple_add_self_sub() {
let ctx = Context::new();
let a = ctx.symbol("a");
let expr = &a + &ctx.int(-1);
eprintln!("expr = {expr}");
let result = &expr - &expr;
eprintln!("result = {result}");
assert!(
result.is_zero_structural(),
"(-1 + a) - (-1 + a) should be zero, got: {result}"
);
}
#[test]
fn regression_neg_distributes_over_add() {
let ctx = Context::new();
let a = ctx.symbol("a");
let expr = &a + &ctx.int(-1); let neg_expr = -&expr;
eprintln!("expr = {expr}");
eprintln!("neg(expr) = {neg_expr}");
let s = format!("{neg_expr}");
assert!(
!s.contains("(-"),
"neg should distribute over Add, not wrap it: got {s}"
);
}
#[test]
fn regression_as_coeff_term_roundtrip() {
let ctx = Context::new();
let a = ctx.symbol("a");
let expr = &a * 3; let _expr_display = format!("{expr}");
let three = ctx.int(3);
let expr2 = &three * &a;
assert_eq!(
format!("{expr}"),
format!("{expr2}"),
"3*a built two ways should match"
);
assert_eq!(expr, expr2, "3*a built two ways should be same ExprId");
let b = ctx.symbol("b");
let expr3 = &a * &b * 2; let expr3_display = format!("{expr3}");
eprintln!("2*a*b = {expr3_display}");
let zero = &expr3 - &expr3;
assert!(
zero.is_zero_structural(),
"2*a*b - 2*a*b should be zero, got: {zero}"
);
}
#[test]
fn regression_smart_simplify_gcd_dropped() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = &(&x * 6) + 12;
let result = expr.simplify();
let point = ctx.rational(7, 10);
let val_orig = expr.subs(&x, &point).eval_f64().unwrap();
let val_result = result.subs(&x, &point).eval_f64().unwrap();
assert!(
(val_orig - val_result).abs() < 1e-10,
"smart_simplify(6x + 12) changed the value: {val_orig} vs {val_result}"
);
}
#[test]
fn regression_smart_simplify_gcd_with_pythagorean() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = &(&x.sin().powi(2) * 2) + &(&x.cos().powi(2) * 2);
let result = expr.simplify();
let result_str = format!("{result}");
assert_eq!(
result_str, "2",
"2sin²+2cos² should smart_simplify to 2, got: {result_str}"
);
}
#[test]
fn regression_by_parts_x_ln_x_no_crash() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = &x * &x.ln();
let result = expr.integrate(&x);
let result_str = format!("{result}");
assert!(
!result_str.contains("Integral"),
"∫ x·ln(x) dx should be integrable, got: {result_str}"
);
let deriv = result.diff(&x);
let point = ctx.int(2);
let val_orig = expr.subs(&x, &point).eval_f64().unwrap();
let val_deriv = deriv.subs(&x, &point).eval_f64().unwrap();
assert!(
(val_orig - val_deriv).abs() < 1e-8,
"d/dx(∫ x·ln(x) dx) should equal x·ln(x) at x=2: {val_orig} vs {val_deriv}"
);
}
#[test]
fn regression_pow_pow_negative_base() {
let ctx = Context::new();
let x = ctx.symbol("x");
let half = ctx.rational(1, 2);
let expr = x.powi(2).pow(&half);
let result = expr.simplify();
let result_str = format!("{result}");
assert_eq!(
result_str, "abs(x)",
"(x^2)^(1/2) should simplify to abs(x), got: {result_str}"
);
}
#[test]
fn regression_asin_sin_symbolic_not_simplified() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = x.sin().asin();
let result = expr.simplify();
let result_str = format!("{result}");
assert_eq!(
result_str, "asin(sin(x))",
"asin(sin(x)) should stay for symbolic x, got: {result_str}"
);
}
#[test]
fn regression_acosh_cosh_gives_abs() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = x.cosh().acosh();
let result = expr.simplify();
let result_str = format!("{result}");
assert_eq!(
result_str, "abs(x)",
"acosh(cosh(x)) should give |x|, got: {result_str}"
);
}