use mathhook_core::calculus::integrals::educational::IntegrationExplanation;
use mathhook_core::calculus::integrals::strategy::integrate_with_strategy;
use mathhook_core::core::{Expression, Symbol};
use mathhook_core::formatter::latex::LaTeXFormatter as _;
use std::sync::Arc;
fn symbol(name: &str) -> Symbol {
Symbol::scalar(name)
}
fn x() -> Symbol {
symbol("x")
}
fn integer(n: i64) -> Expression {
Expression::integer(n)
}
fn add(terms: Vec<Expression>) -> Expression {
Expression::Add(Arc::new(terms))
}
fn mul(factors: Vec<Expression>) -> Expression {
Expression::Mul(Arc::new(factors))
}
fn pow(base: Expression, exp: Expression) -> Expression {
Expression::Pow(Arc::new(base), Arc::new(exp))
}
fn sin(arg: Expression) -> Expression {
Expression::function("sin", vec![arg])
}
fn cos(arg: Expression) -> Expression {
Expression::function("cos", vec![arg])
}
fn exp(arg: Expression) -> Expression {
Expression::function("exp", vec![arg])
}
fn ln(arg: Expression) -> Expression {
Expression::function("ln", vec![arg])
}
#[test]
fn test_explanation_for_power_rule() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(3));
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
assert!(
explanation.strategy_used().contains("power")
|| explanation.strategy_used().contains("basic")
|| explanation.strategy_used().contains("table")
);
}
#[test]
fn test_explanation_for_trig_integral() {
let var = x();
let expr = sin(Expression::Symbol(var.clone()));
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
assert!(
explanation.strategy_used().contains("trig")
|| explanation.strategy_used().contains("table")
);
}
#[test]
fn test_explanation_for_rational_function() {
let var = x();
let expr = mul(vec![
integer(1),
pow(
add(vec![Expression::Symbol(var.clone()), integer(1)]),
integer(-1),
),
]);
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_explanation_for_substitution() {
let var = x();
let expr = mul(vec![
Expression::Symbol(var.clone()),
sin(pow(Expression::Symbol(var.clone()), integer(2))),
]);
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_explanation_for_by_parts() {
let var = x();
let expr = mul(vec![
Expression::Symbol(var.clone()),
ln(Expression::Symbol(var.clone())),
]);
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_explanation_includes_formula() {
let var = x();
let expr = exp(Expression::Symbol(var.clone()));
let explanation = IntegrationExplanation::generate(&expr, &var);
let steps_text = explanation.steps().join(" ");
assert!(steps_text.contains("e") || steps_text.contains("exp"));
}
#[test]
fn test_explanation_multiple_steps() {
let var = x();
let expr = pow(sin(Expression::Symbol(var.clone())), integer(2));
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(explanation.steps().len() > 1);
}
#[test]
fn test_explanation_for_sum_of_integrals() {
let var = x();
let expr = add(vec![
Expression::Symbol(var.clone()),
sin(Expression::Symbol(var.clone())),
]);
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_explanation_for_constant_multiple() {
let var = x();
let expr = mul(vec![integer(5), Expression::Symbol(var.clone())]);
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_latex_format_simple_polynomial() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(2));
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("x"));
assert!(latex.contains("2") || latex.contains("^"));
}
#[test]
fn test_latex_format_fraction() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(-1));
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("x"));
}
#[test]
fn test_latex_format_trig_function() {
let var = x();
let expr = sin(Expression::Symbol(var.clone()));
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("sin") || latex.contains("\\sin"));
}
#[test]
fn test_latex_format_integral_result() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(2));
let result = integrate_with_strategy(&expr, var.clone(), 0);
let latex = result
.to_latex(None)
.unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("x"));
assert!(latex.contains("3"));
}
#[test]
fn test_latex_format_complex_expression() {
let var = x();
let numerator = add(vec![
pow(Expression::Symbol(var.clone()), integer(2)),
integer(1),
]);
let denominator = add(vec![Expression::Symbol(var.clone()), integer(1)]);
let expr = mul(vec![numerator, pow(denominator, integer(-1))]);
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("x"));
}
#[test]
fn test_latex_format_exponential() {
let var = x();
let expr = exp(Expression::Symbol(var.clone()));
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("e") || latex.contains("exp"));
}
#[test]
fn test_latex_format_logarithm() {
let var = x();
let expr = ln(Expression::Symbol(var.clone()));
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("ln") || latex.contains("log"));
}
#[test]
fn test_latex_format_product() {
let var = x();
let expr = mul(vec![
Expression::Symbol(var.clone()),
sin(Expression::Symbol(var.clone())),
]);
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("x"));
assert!(latex.contains("sin"));
}
#[test]
fn test_latex_format_sum() {
let var = x();
let expr = add(vec![
Expression::Symbol(var.clone()),
sin(Expression::Symbol(var.clone())),
]);
let latex = expr.to_latex(None).unwrap_or_else(|_| "error".to_string());
assert!(latex.contains("x"));
assert!(latex.contains("sin"));
}
#[test]
fn test_step_by_step_includes_original() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(2));
let explanation = IntegrationExplanation::generate(&expr, &var);
let first_step = &explanation.steps()[0];
assert!(first_step.contains("x") || first_step.contains("∫"));
}
#[test]
fn test_step_by_step_includes_result() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(2));
let explanation = IntegrationExplanation::generate(&expr, &var);
let steps = explanation.steps();
let last_step = steps.last().unwrap();
assert!(!last_step.is_empty());
}
#[test]
fn test_step_by_step_logical_progression() {
let var = x();
let expr = pow(sin(Expression::Symbol(var.clone())), integer(2));
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_educational_message_for_table_lookup() {
let var = x();
let expr = sin(Expression::Symbol(var.clone()));
let explanation = IntegrationExplanation::generate(&expr, &var);
let strategy = explanation.strategy_used();
assert!(!strategy.is_empty());
}
#[test]
fn test_educational_message_for_substitution_pattern() {
let var = x();
let expr = mul(vec![
integer(2),
Expression::Symbol(var.clone()),
exp(pow(Expression::Symbol(var.clone()), integer(2))),
]);
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_educational_message_for_partial_fractions() {
let var = x();
let expr = mul(vec![
integer(1),
pow(
add(vec![
pow(Expression::Symbol(var.clone()), integer(2)),
integer(-1),
]),
integer(-1),
),
]);
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_educational_message_includes_why() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(3));
let explanation = IntegrationExplanation::generate(&expr, &var);
let all_steps = explanation.steps().join(" ");
assert!(all_steps.len() > 10); }
#[test]
fn test_latex_in_educational_steps() {
let var = x();
let expr = sin(Expression::Symbol(var.clone()));
let explanation = IntegrationExplanation::generate(&expr, &var);
for step in explanation.steps() {
assert!(!step.is_empty());
}
}
#[test]
fn test_explanation_for_non_elementary() {
let var = x();
let expr = exp(pow(Expression::Symbol(var.clone()), integer(2)));
let explanation = IntegrationExplanation::generate(&expr, &var);
assert!(!explanation.steps().is_empty());
}
#[test]
fn test_strategy_attribution_table() {
let var = x();
let expr = cos(Expression::Symbol(var.clone()));
let explanation = IntegrationExplanation::generate(&expr, &var);
let strategy = explanation.strategy_used();
assert!(strategy.contains("table") || strategy.contains("trig"));
}
#[test]
fn test_strategy_attribution_rational() {
let var = x();
let expr = pow(Expression::Symbol(var.clone()), integer(-1));
let explanation = IntegrationExplanation::generate(&expr, &var);
let strategy = explanation.strategy_used();
assert!(!strategy.is_empty());
}
#[test]
fn test_strategy_attribution_substitution() {
let var = x();
let expr = mul(vec![
Expression::Symbol(var.clone()),
exp(pow(Expression::Symbol(var.clone()), integer(2))),
]);
let explanation = IntegrationExplanation::generate(&expr, &var);
let strategy = explanation.strategy_used();
assert!(!strategy.is_empty());
}