use symplex::prelude::*;
fn check(expr: &Ex, expected: &str) {
let s = format!("{expr}");
assert_eq!(s, expected, "expected '{expected}', got '{s}'");
}
#[test]
fn diff_known_apply_constant_fibonacci() {
let ctx = Context::new();
let x = ctx.symbol("x");
let n = ctx.int(10);
let fib = n.fibonacci();
let result = fib.diff(&x);
check(&result, "0");
}
#[test]
fn diff_known_apply_constant_lucas() {
let ctx = Context::new();
let x = ctx.symbol("x");
let n = ctx.int(5);
let luc = n.lucas();
let result = luc.diff(&x);
check(&result, "0");
}
#[test]
fn diff_known_apply_variable_fibonacci() {
let ctx = Context::new();
let x = ctx.symbol("x");
let fib = x.fibonacci();
let result = fib.diff(&x);
let s = format!("{result}");
assert!(
s.contains("Derivative") && s.contains("fibonacci"),
"d/dx(fibonacci(x)) should be a formal Derivative, got '{s}'"
);
}
#[test]
fn diff_known_apply_variable_lucas() {
let ctx = Context::new();
let x = ctx.symbol("x");
let luc = x.lucas();
let result = luc.diff(&x);
let s = format!("{result}");
assert!(
s.contains("Derivative") && s.contains("lucas"),
"d/dx(lucas(x)) should be a formal Derivative, got '{s}'"
);
}
#[test]
fn diff_known_apply_other_symbol() {
let ctx = Context::new();
let x = ctx.symbol("x");
let y = ctx.symbol("y");
let fib = y.fibonacci();
let result = fib.diff(&x);
check(&result, "0");
}
#[test]
fn diff_known_apply_chain_rule_x_squared() {
let ctx = Context::new();
let x = ctx.symbol("x");
let x_sq = x.powi(2);
let fib = x_sq.fibonacci();
let result = fib.diff(&x);
let s = format!("{result}");
assert!(
s.contains("2") && s.contains("x") && s.contains("Derivative"),
"d/dx(fibonacci(x²)) should contain 2, x, and Derivative, got '{s}'"
);
}
#[test]
fn diff_known_apply_chain_sin() {
let ctx = Context::new();
let x = ctx.symbol("x");
let sin_x = x.sin();
let fib = sin_x.fibonacci();
let result = fib.diff(&x);
let s = format!("{result}");
assert!(
s.contains("cos"),
"d/dx(fibonacci(sin(x))) should contain cos(x) factor, got '{s}'"
);
}
#[test]
fn diff_known_apply_chain_exp() {
let ctx = Context::new();
let x = ctx.symbol("x");
let exp_x = x.exp();
let fib = exp_x.fibonacci();
let result = fib.diff(&x);
let s = format!("{result}");
assert!(
s.contains("exp"),
"d/dx(fibonacci(exp(x))) should contain exp(x) factor, got '{s}'"
);
assert!(
s.contains("Derivative"),
"d/dx(fibonacci(exp(x))) should contain formal Derivative, got '{s}'"
);
}
#[test]
fn diff_known_apply_chain_linear() {
let ctx = Context::new();
let x = ctx.symbol("x");
let three = ctx.int(3);
let one = ctx.int(1);
let arg = &three * &x + &one;
let fib = arg.fibonacci();
let result = fib.diff(&x);
let s = format!("{result}");
assert!(
s.contains("3") && s.contains("Derivative"),
"d/dx(fibonacci(3x+1)) should contain 3 and Derivative, got '{s}'"
);
}
#[test]
fn regression_power_rule() {
let ctx = Context::new();
let x = ctx.symbol("x");
check(&x.powi(3).diff(&x), "3*x^2");
}
#[test]
fn regression_second_derivative() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = x.powi(4);
let first = expr.diff(&x);
check(&first, "4*x^3");
let second = first.diff(&x);
check(&second, "12*x^2");
}
#[test]
fn regression_elementary_chain_rule() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = x.powi(2).sin();
let result = expr.diff(&x);
let s = format!("{result}");
assert!(
s.contains("cos") && s.contains("2"),
"d/dx(sin(x²)) should contain cos and 2, got '{s}'"
);
}
#[test]
fn regression_exp_diff() {
let ctx = Context::new();
let x = ctx.symbol("x");
check(&x.exp().diff(&x), "exp(x)");
}
#[test]
fn regression_ln_diff() {
let ctx = Context::new();
let x = ctx.symbol("x");
check(&x.ln().diff(&x), "1/x");
}
#[test]
fn regression_sin_diff() {
let ctx = Context::new();
let x = ctx.symbol("x");
check(&x.sin().diff(&x), "cos(x)");
}
#[test]
fn regression_cos_diff() {
let ctx = Context::new();
let x = ctx.symbol("x");
check(&x.cos().diff(&x), "-sin(x)");
}
#[test]
fn regression_product_rule() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = &x * &x.sin();
let result = expr.diff(&x);
let s = format!("{result}");
assert!(
s.contains("sin") && s.contains("cos"),
"d/dx(x*sin(x)) should contain sin and cos, got '{s}'"
);
}
#[test]
fn regression_constant_diff() {
let ctx = Context::new();
let x = ctx.symbol("x");
let five = ctx.int(5);
check(&five.diff(&x), "0");
}