use mathhook_core::algebra::equation_analyzer::{
EquationAnalyzer, EquationType, SmartEquationSolver,
};
use mathhook_core::core::Expression;
use mathhook_core::symbol;
#[test]
fn test_ode_detection_simple_derivative() {
let y = symbol!(y);
let y_prime = symbol!(y_prime);
let lhs = Expression::add(vec![
Expression::symbol(y_prime),
Expression::mul(vec![Expression::integer(2), Expression::symbol(y.clone())]),
]);
let equation_type = EquationAnalyzer::analyze(&lhs, &y);
assert_eq!(
equation_type,
EquationType::ODE,
"Should detect equation with y_prime as ODE"
);
}
#[test]
fn test_ode_detection_function_derivative() {
let y = symbol!(y);
let x = symbol!(x);
let derivative_expr = Expression::function(
"derivative",
vec![Expression::symbol(y.clone()), Expression::symbol(x.clone())],
);
let equation_type = EquationAnalyzer::analyze(&derivative_expr, &y);
assert_eq!(
equation_type,
EquationType::ODE,
"Should detect derivative() function as ODE"
);
}
#[test]
fn test_pde_detection() {
let u = symbol!(u);
let partial_u = symbol!(partial_u);
let expr = Expression::symbol(partial_u);
let equation_type = EquationAnalyzer::analyze(&expr, &u);
assert_eq!(
equation_type,
EquationType::PDE,
"Should detect partial derivative symbol as PDE"
);
}
#[test]
fn test_smart_solver_ode_routing() {
let y = symbol!(y);
let y_prime = symbol!(y_prime);
let equation = Expression::add(vec![
Expression::symbol(y_prime),
Expression::mul(vec![Expression::integer(2), Expression::symbol(y.clone())]),
]);
let solver = SmartEquationSolver::new();
let (_result, explanation) = solver.solve_with_equation(&equation, &y);
let steps_str = format!("{:?}", explanation);
assert!(
steps_str.contains("ODE") || steps_str.contains("differential"),
"SmartEquationSolver should route to ODE solver and mention it in explanation"
);
assert!(
!steps_str.contains("linear equation solver (isolation method)"),
"Should NOT route to linear solver for ODE"
);
}
#[test]
fn test_non_ode_still_works() {
let x = symbol!(x);
let quadratic = Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::mul(vec![Expression::integer(3), Expression::symbol(x.clone())]),
Expression::integer(2),
]);
let equation_type = EquationAnalyzer::analyze(&quadratic, &x);
assert_eq!(
equation_type,
EquationType::Quadratic,
"Non-ODE equations should still be detected correctly (no regression)"
);
}
#[test]
fn test_architectural_pattern_no_hardcoded_ode_matching() {
let y = symbol!(y);
let y_prime = symbol!(y_prime);
let z_prime = symbol!(z_prime);
for notation in [y_prime, z_prime] {
let expr = Expression::symbol(notation);
let eq_type = EquationAnalyzer::analyze(&expr, &y);
assert_eq!(
eq_type,
EquationType::ODE,
"All derivative notations with _prime suffix should be detected consistently via helper methods"
);
}
}
#[test]
fn test_no_stub_implementations_in_routing() {
let solver = SmartEquationSolver::new();
let y = symbol!(y);
let y_prime = symbol!(y_prime);
let equation = Expression::symbol(y_prime);
let (_result, explanation) = solver.solve_with_equation(&equation, &y);
assert!(
!explanation.steps.is_empty(),
"ODE routing should produce explanation steps, not be a stub"
);
let has_ode_classification = explanation
.steps
.iter()
.any(|step| step.title.contains("ODE") || step.description.contains("differential"));
assert!(
has_ode_classification,
"Should have ODE-specific classification step"
);
}