use super::differential_extension::DifferentialExtension;
use crate::core::{Expression, Number};
pub fn hermite_reduction(
expr: &Expression,
_extensions: &[DifferentialExtension],
) -> Option<(Expression, Expression)> {
use super::helpers::extract_division;
if let Some((num, _den)) = extract_division(expr) {
if num == Expression::integer(1) {
return Some((Expression::integer(0), expr.clone()));
}
}
if is_rational_function(expr) {
Some((expr.clone(), Expression::integer(0)))
} else {
Some((Expression::integer(0), expr.clone()))
}
}
pub fn is_rational_function(expr: &Expression) -> bool {
use super::helpers::extract_division;
match expr {
Expression::Number(_) | Expression::Constant(_) | Expression::Symbol(_) => true,
Expression::Add(terms) => terms.iter().all(is_rational_function),
Expression::Mul(factors) => {
if let Some((num, den)) = extract_division(expr) {
is_rational_function(&num) && is_rational_function(&den)
} else {
factors.iter().all(is_rational_function)
}
}
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(n)) = &**exp {
if *n < 0 {
return is_rational_function(base);
}
}
is_rational_function(base) && is_nonnegative_integer(exp)
}
Expression::Function { name, .. } => {
!is_transcendental_function(name)
}
_ => false,
}
}
fn is_nonnegative_integer(expr: &Expression) -> bool {
match expr {
Expression::Number(Number::Integer(n)) => *n >= 0,
_ => false,
}
}
fn is_transcendental_function(name: &str) -> bool {
matches!(
name,
"exp"
| "ln"
| "log"
| "sin"
| "cos"
| "tan"
| "cot"
| "sec"
| "csc"
| "arcsin"
| "arccos"
| "arctan"
| "sinh"
| "cosh"
| "tanh"
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol;
#[test]
fn test_is_rational_polynomial() {
let x = symbol!(x);
let expr = Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::symbol(x.clone()),
Expression::integer(1),
]);
assert!(is_rational_function(&expr));
}
#[test]
fn test_is_rational_fraction() {
let x = symbol!(x);
let expr = Expression::div(
Expression::integer(1),
Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(1)]),
);
assert!(is_rational_function(&expr));
}
#[test]
fn test_is_not_rational_exponential() {
let x = symbol!(x);
let expr = Expression::function("exp", vec![Expression::symbol(x.clone())]);
assert!(!is_rational_function(&expr));
}
#[test]
fn test_is_not_rational_logarithm() {
let x = symbol!(x);
let expr = Expression::function("ln", vec![Expression::symbol(x.clone())]);
assert!(!is_rational_function(&expr));
}
#[test]
fn test_hermite_reduction_logarithmic_derivative() {
let x = symbol!(x);
let expr = Expression::div(Expression::integer(1), Expression::symbol(x.clone()));
let extensions = vec![DifferentialExtension::Rational];
let result = hermite_reduction(&expr, &extensions);
assert!(result.is_some());
let (rational, transcendental) = result.unwrap();
assert_eq!(rational, Expression::integer(0));
assert_ne!(transcendental, Expression::integer(0));
}
#[test]
fn test_hermite_reduction_polynomial() {
let x = symbol!(x);
let expr = Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::integer(1),
]);
let extensions = vec![DifferentialExtension::Rational];
let result = hermite_reduction(&expr, &extensions);
assert!(result.is_some());
let (rational, transcendental) = result.unwrap();
assert_ne!(rational, Expression::integer(0));
assert_eq!(transcendental, Expression::integer(0));
}
#[test]
fn test_hermite_reduction_transcendental() {
let x = symbol!(x);
let expr = Expression::function("exp", vec![Expression::symbol(x)]);
let extensions = vec![DifferentialExtension::Rational];
let result = hermite_reduction(&expr, &extensions);
assert!(result.is_some());
let (rational, transcendental) = result.unwrap();
assert_eq!(rational, Expression::integer(0));
assert_ne!(transcendental, Expression::integer(0));
}
}