use mathhook_core::calculus::derivatives::Derivative;
use mathhook_core::calculus::integrals::risch::try_risch_integration;
use mathhook_core::calculus::integrals::Integration;
use mathhook_core::core::expression::CalculusData;
use mathhook_core::simplify::Simplify;
use mathhook_core::{symbol, Expression};
fn verify_risch_ftc(expr: &Expression, var: mathhook_core::Symbol) {
let integral_result = try_risch_integration(expr, &var);
if let Some(integral) = integral_result {
let derivative = integral.derivative(var.clone()).simplify();
let original = expr.simplify();
assert_eq!(
derivative, original,
"Fundamental Theorem violated: d/dx(∫f dx) ≠ f for Risch integration"
);
}
}
#[test]
fn test_risch_exp_x_mathematical_correctness() {
let x = symbol!(x);
let integrand = Expression::function("exp", vec![Expression::symbol(x.clone())]);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫e^x dx should succeed via Risch");
verify_risch_ftc(&integrand, x);
}
#[test]
fn test_risch_exp_2x_mathematical_correctness() {
let x = symbol!(x);
let integrand = Expression::function(
"exp",
vec![Expression::mul(vec![
Expression::integer(2),
Expression::symbol(x.clone()),
])],
);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫e^(2x) dx should succeed via Risch");
verify_risch_ftc(&integrand, x);
}
#[test]
fn test_risch_exp_3x_mathematical_correctness() {
let x = symbol!(x);
let integrand = Expression::function(
"exp",
vec![Expression::mul(vec![
Expression::integer(3),
Expression::symbol(x.clone()),
])],
);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫e^(3x) dx should succeed via Risch");
verify_risch_ftc(&integrand, x);
}
#[test]
fn test_risch_exp_negative_x_mathematical_correctness() {
let x = symbol!(x);
let integrand = Expression::function(
"exp",
vec![Expression::mul(vec![
Expression::integer(-1),
Expression::symbol(x.clone()),
])],
);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫e^(-x) dx should succeed via Risch");
verify_risch_ftc(&integrand, x);
}
#[test]
fn test_risch_exp_ax_general_pattern() {
let x = symbol!(x);
for a in [1, 2, 3, 5, -1, -2] {
let integrand = Expression::function(
"exp",
vec![Expression::mul(vec![
Expression::integer(a),
Expression::symbol(x.clone()),
])],
);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫e^({}x) dx should succeed via Risch", a);
verify_risch_ftc(&integrand, x.clone());
}
}
#[test]
fn test_risch_one_over_x_mathematical_correctness() {
let x = symbol!(x);
let integrand = Expression::mul(vec![
Expression::integer(1),
Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1)),
]);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫1/x dx should succeed via Risch");
verify_risch_ftc(&integrand, x);
}
#[test]
fn test_risch_one_over_ax_general_pattern() {
let x = symbol!(x);
for a in [2, 3, 5] {
let integrand = Expression::mul(vec![
Expression::integer(1),
Expression::pow(
Expression::mul(vec![Expression::integer(a), Expression::symbol(x.clone())]),
Expression::integer(-1),
),
]);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫1/({}x) dx should succeed via Risch", a);
verify_risch_ftc(&integrand, x.clone());
}
}
#[test]
fn test_risch_one_over_x_plus_constant() {
let x = symbol!(x);
for c in [1, 5, -2] {
let integrand = Expression::mul(vec![
Expression::integer(1),
Expression::pow(
Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(c)]),
Expression::integer(-1),
),
]);
let result = try_risch_integration(&integrand, &x);
assert!(result.is_some(), "∫1/(x+{}) dx should succeed via Risch", c);
verify_risch_ftc(&integrand, x.clone());
}
}
#[test]
fn test_risch_one_over_linear_general() {
let x = symbol!(x);
let test_cases = vec![
(2, 3), (3, 1), (1, 5), ];
for (a, b) in test_cases {
let integrand = Expression::mul(vec![
Expression::integer(1),
Expression::pow(
Expression::add(vec![
Expression::mul(vec![Expression::integer(a), Expression::symbol(x.clone())]),
Expression::integer(b),
]),
Expression::integer(-1),
),
]);
let result = try_risch_integration(&integrand, &x);
assert!(
result.is_some(),
"∫1/({}x+{}) dx should succeed via Risch",
a,
b
);
verify_risch_ftc(&integrand, x.clone());
}
}
#[test]
fn test_risch_detects_exp_x_squared_non_elementary() {
let x = symbol!(x);
let x_squared = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
let integrand = Expression::function("exp", vec![x_squared]);
let result = try_risch_integration(&integrand, &x);
assert!(
result.is_none(),
"∫e^(x²) dx should be detected as non-elementary by Risch"
);
}
#[test]
fn test_risch_detects_exp_negative_x_squared_non_elementary() {
let x = symbol!(x);
let neg_x_squared = Expression::mul(vec![
Expression::integer(-1),
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
]);
let integrand = Expression::function("exp", vec![neg_x_squared]);
let result = try_risch_integration(&integrand, &x);
assert!(
result.is_none(),
"∫e^(-x²) dx should be detected as non-elementary (Gaussian)"
);
}
#[test]
fn test_risch_detects_exp_over_x_non_elementary() {
let x = symbol!(x);
let exp_x = Expression::function("exp", vec![Expression::symbol(x.clone())]);
let integrand = Expression::mul(vec![
exp_x,
Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1)),
]);
let result = try_risch_integration(&integrand, &x);
assert!(
result.is_none(),
"∫e^x/x dx should be detected as non-elementary (exponential integral Ei)"
);
}
#[test]
fn test_integration_strategy_handles_non_elementary() {
let x = symbol!(x);
let x_squared = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
let integrand = Expression::function("exp", vec![x_squared]);
let result = integrand.integrate(x, 0);
let is_symbolic = if let Expression::Calculus(data) = &result {
matches!(&**data, CalculusData::Integral { .. })
} else {
false
};
assert!(
is_symbolic,
"Integration of e^(x²) should return symbolic integral (non-elementary)"
);
}
#[test]
fn test_integration_strategy_exp_x_via_risch() {
let x = symbol!(x);
let integrand = Expression::function("exp", vec![Expression::symbol(x.clone())]);
let result = integrand.integrate(x.clone(), 0);
let is_symbolic = if let Expression::Calculus(data) = &result {
matches!(&**data, CalculusData::Integral { .. })
} else {
false
};
assert!(
!is_symbolic,
"Integration of e^x should be solved (not symbolic)"
);
let derivative = result.derivative(x.clone()).simplify();
let original = integrand.simplify();
assert_eq!(derivative, original, "d/dx(∫e^x dx) must equal e^x");
}
#[test]
fn test_integration_strategy_one_over_x_via_risch() {
let x = symbol!(x);
let integrand = Expression::mul(vec![
Expression::integer(1),
Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1)),
]);
let result = integrand.integrate(x.clone(), 0);
let is_symbolic = if let Expression::Calculus(data) = &result {
matches!(&**data, CalculusData::Integral { .. })
} else {
false
};
assert!(
!is_symbolic,
"Integration of 1/x should be solved (not symbolic)"
);
}
#[test]
fn test_integration_strategy_polynomial_not_risch() {
let x = symbol!(x);
let polynomials = vec![
Expression::integer(5), Expression::symbol(x.clone()), Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)), Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(3)),
Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
Expression::integer(1),
]), ];
for poly in polynomials {
let result = poly.integrate(x.clone(), 0);
let is_symbolic = if let Expression::Calculus(data) = &result {
matches!(&**data, CalculusData::Integral { .. })
} else {
false
};
assert!(
!is_symbolic,
"Polynomial integration should not return symbolic integral"
);
let derivative = result.derivative(x.clone()).simplify();
let original = poly.simplify();
assert_eq!(
derivative, original,
"d/dx(∫polynomial dx) must equal polynomial"
);
}
}
#[test]
fn test_risch_layer_cooperates_with_other_layers() {
let x = symbol!(x);
let test_cases = vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::function("sin", vec![Expression::symbol(x.clone())]),
Expression::function("exp", vec![Expression::symbol(x.clone())]),
];
for case in test_cases {
let result = case.integrate(x.clone(), 0);
let derivative = result.derivative(x.clone()).simplify();
let original = case.simplify();
if !matches!(result, Expression::Calculus(_)) {
assert_eq!(
derivative, original,
"FTC must hold for elementary integrals"
);
}
}
}
#[test]
fn test_ftc_verification_all_risch_cases() {
let x = symbol!(x);
let risch_solvable = vec![
Expression::function("exp", vec![Expression::symbol(x.clone())]),
Expression::function(
"exp",
vec![Expression::mul(vec![
Expression::integer(2),
Expression::symbol(x.clone()),
])],
),
Expression::function(
"exp",
vec![Expression::mul(vec![
Expression::integer(-1),
Expression::symbol(x.clone()),
])],
),
Expression::mul(vec![
Expression::integer(1),
Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1)),
]),
];
for expr in risch_solvable {
verify_risch_ftc(&expr, x.clone());
}
}
#[test]
fn test_non_elementary_cases_return_symbolic() {
let x = symbol!(x);
let non_elementary = vec![
Expression::function(
"exp",
vec![Expression::pow(
Expression::symbol(x.clone()),
Expression::integer(2),
)],
),
Expression::function(
"exp",
vec![Expression::mul(vec![
Expression::integer(-1),
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
])],
),
];
for expr in non_elementary {
let result = expr.integrate(x.clone(), 0);
let is_symbolic = if let Expression::Calculus(data) = &result {
matches!(&**data, CalculusData::Integral { .. })
} else {
false
};
assert!(
is_symbolic,
"Non-elementary integrals must return symbolic form"
);
}
}