mathhook_core/calculus/integrals/risch/
helpers.rs

1//! Helper functions for Risch algorithm
2//!
3//! Common utilities used across Risch implementation.
4
5use crate::core::{Expression, Number, Symbol};
6
7/// Check if expression is just the variable
8///
9/// Returns true if the expression is exactly the given symbol variable.
10///
11/// # Arguments
12///
13/// * `expr` - The expression to check
14/// * `var` - The variable symbol to compare against
15///
16/// # Examples
17///
18/// ```rust
19/// use mathhook_core::calculus::integrals::risch::helpers::is_just_variable;
20/// use mathhook_core::{Expression, symbol};
21///
22/// let x = symbol!(x);
23/// let expr = Expression::symbol(x.clone());
24/// assert!(is_just_variable(&expr, &x));
25/// ```
26pub fn is_just_variable(expr: &Expression, var: &Symbol) -> bool {
27    matches!(expr, Expression::Symbol(s) if *s == *var)
28}
29
30/// Check if expression is the constant 1
31///
32/// # Arguments
33///
34/// * `expr` - The expression to check
35///
36/// # Examples
37///
38/// ```rust
39/// use mathhook_core::calculus::integrals::risch::helpers::is_one;
40/// use mathhook_core::Expression;
41///
42/// let one = Expression::integer(1);
43/// assert!(is_one(&one));
44///
45/// let two = Expression::integer(2);
46/// assert!(!is_one(&two));
47/// ```
48pub fn is_one(expr: &Expression) -> bool {
49    matches!(expr, Expression::Number(n) if n.is_one())
50}
51
52/// Extract division pattern from expression
53///
54/// Division can be represented as:
55/// 1. Mul([numerator, Pow(denominator, -1)]) for general divisions like 3/x
56/// 2. Pow(denominator, -1) for 1/x (the 1 is simplified away)
57///
58/// Returns Some((numerator, denominator)) if pattern matches.
59pub fn extract_division(expr: &Expression) -> Option<(Expression, Expression)> {
60    match expr {
61        // Case 1: General division num/den represented as Mul([num, den^(-1)])
62        Expression::Mul(factors) if factors.len() == 2 => {
63            // Check if second factor is denominator^(-1)
64            if let Expression::Pow(base, exp) = &factors[1] {
65                if let Expression::Number(Number::Integer(-1)) = &**exp {
66                    return Some((factors[0].clone(), (**base).clone()));
67                }
68            }
69            // Check if first factor is denominator^(-1)
70            if let Expression::Pow(base, exp) = &factors[0] {
71                if let Expression::Number(Number::Integer(-1)) = &**exp {
72                    return Some((factors[1].clone(), (**base).clone()));
73                }
74            }
75            None
76        }
77        // Case 2: 1/den represented as den^(-1) (the 1 is simplified away)
78        Expression::Pow(base, exp) => {
79            if let Expression::Number(Number::Integer(-1)) = &**exp {
80                Some((Expression::integer(1), (**base).clone()))
81            } else {
82                None
83            }
84        }
85        _ => None,
86    }
87}