mathhook_core/calculus/integrals/
basic.rs

1//! Basic integration rules for constants, symbols, sums, and simple powers
2//!
3//! Preserves order for noncommutative expressions (matrices, operators, quaternions).
4//! Integration maintains factor order to ensure correctness with noncommutative algebra.
5use crate::calculus::integrals::strategy::integrate_with_strategy;
6use crate::core::{Expression, Number, Symbol};
7use crate::simplify::Simplify;
8use num_rational::BigRational;
9use num_traits::Zero;
10/// Basic integration operations
11pub struct BasicIntegrals;
12impl BasicIntegrals {
13    /// Handle integration of calculus expressions
14    ///
15    /// # Examples
16    ///
17    /// ```rust
18    /// use mathhook_core::calculus::integrals::{BasicIntegrals, Integration};
19    /// use mathhook_core::{Expression, symbol};
20    ///
21    /// let x = symbol!(x);
22    /// let expr = Expression::integral(Expression::symbol(x.clone()), x.clone());
23    /// let result = expr.integrate(x, 0);
24    /// ```
25    pub fn handle_calculus(
26        expr: &Expression,
27        data: &crate::core::expression::CalculusData,
28        variable: Symbol,
29    ) -> Expression {
30        match data {
31            crate::core::expression::CalculusData::Integral {
32                variable: var,
33                bounds,
34                ..
35            } => {
36                if *var == variable && bounds.is_none() {
37                    expr.clone()
38                } else {
39                    Expression::integral(expr.clone(), variable)
40                }
41            }
42            _ => Expression::integral(expr.clone(), variable),
43        }
44    }
45    /// Handle integration of constant expressions
46    ///
47    /// # Examples
48    ///
49    /// ```rust
50    /// use mathhook_core::{Expression, BasicIntegrals};
51    /// use mathhook_core::symbol;
52    ///
53    /// let x = symbol!(x);
54    /// let expr = Expression::integer(5);
55    /// let result = BasicIntegrals::handle_constant(&expr, x);
56    /// ```
57    pub fn handle_constant(expr: &Expression, variable: Symbol) -> Expression {
58        Expression::mul(vec![expr.clone(), Expression::symbol(variable)])
59    }
60    /// Handle integration of symbol expressions
61    ///
62    /// # Examples
63    ///
64    /// ```rust
65    /// use mathhook_core::{Expression, BasicIntegrals};
66    /// use mathhook_core::symbol;
67    ///
68    /// let x = symbol!(x);
69    /// let y = symbol!(y);
70    /// let dx = BasicIntegrals::handle_symbol(&x, &x);
71    /// let dy = BasicIntegrals::handle_symbol(&x, &y);
72    /// ```
73    pub fn handle_symbol(sym: &Symbol, variable: &Symbol) -> Expression {
74        if sym == variable {
75            Expression::mul(vec![
76                Expression::mul(vec![
77                    Expression::integer(1),
78                    Expression::pow(Expression::integer(2), Expression::integer(-1)),
79                ]),
80                Expression::pow(Expression::symbol(variable.clone()), Expression::integer(2)),
81            ])
82        } else {
83            Expression::mul(vec![
84                Expression::symbol(sym.clone()),
85                Expression::symbol(variable.clone()),
86            ])
87        }
88    }
89    /// Handle integration of sum expressions using linearity
90    ///
91    /// # Arguments
92    ///
93    /// * `terms` - Terms in the sum
94    /// * `variable` - Variable to integrate with respect to
95    /// * `depth` - Current recursion depth
96    ///
97    /// # Examples
98    ///
99    /// ```rust
100    /// use mathhook_core::{Expression, BasicIntegrals};
101    /// use mathhook_core::symbol;
102    /// use mathhook_core::calculus::integrals::Integration;
103    ///
104    /// let x = symbol!(x);
105    /// let terms = vec![Expression::symbol(x.clone()), Expression::integer(5)];
106    /// let result = BasicIntegrals::handle_sum(&terms, &x, 0);
107    /// ```
108    pub fn handle_sum(terms: &[Expression], variable: &Symbol, depth: usize) -> Expression {
109        let integrals: Vec<Expression> = terms
110            .iter()
111            .map(|term| integrate_with_strategy(term, variable.clone(), depth + 1))
112            .collect();
113        Expression::add(integrals).simplify()
114    }
115    /// Handle integration of product expressions
116    ///
117    /// Preserves factor order for noncommutative expressions (matrices, operators, quaternions).
118    ///
119    /// # Arguments
120    ///
121    /// * `factors` - Factors in the product
122    /// * `variable` - Variable to integrate with respect to
123    /// * `depth` - Current recursion depth
124    ///
125    /// # Examples
126    ///
127    /// ```rust
128    /// use mathhook_core::{Expression, BasicIntegrals};
129    /// use mathhook_core::symbol;
130    ///
131    /// let x = symbol!(x);
132    /// let factors = vec![Expression::integer(3), Expression::symbol(x.clone())];
133    /// let result = BasicIntegrals::handle_product(&factors, x, 0);
134    /// ```
135    pub fn handle_product(factors: &[Expression], variable: Symbol, depth: usize) -> Expression {
136        let (constants, variables): (Vec<_>, Vec<_>) = factors
137            .iter()
138            .partition(|f| Self::is_constant_wrt(f, &variable));
139        if variables.is_empty() {
140            return Expression::mul(vec![
141                Expression::mul(factors.to_vec()),
142                Expression::symbol(variable),
143            ]);
144        }
145        if variables.len() == 1 {
146            let constant_part = if constants.is_empty() {
147                Expression::integer(1)
148            } else {
149                Expression::mul(constants.into_iter().cloned().collect())
150            };
151            let integrated_variable =
152                integrate_with_strategy(variables[0], variable.clone(), depth + 1);
153            let result = Expression::mul(vec![constant_part, integrated_variable]);
154            let simplified = result.simplify();
155            return simplified;
156        }
157        Expression::integral(Expression::mul(factors.to_vec()), variable)
158    }
159    /// Handle integration of power expressions using power rule
160    ///
161    /// Power rule for integer exponents: ∫x^n dx = x^(n+1)/(n+1) + C (n ≠ -1)
162    /// Power rule for rational exponents: ∫x^(p/q) dx = (q/(p+q))·x^((p+q)/q) + C (p+q ≠ 0)
163    /// Special case: ∫x^(-1) dx = ln|x| + C
164    ///
165    /// For expressions like x^2, uses the standard power rule.
166    /// For more complex expressions, defers to by-parts or other methods.
167    ///
168    /// # Arguments
169    ///
170    /// * `base` - Base of the power expression
171    /// * `exponent` - Exponent of the power expression
172    /// * `variable` - Variable to integrate with respect to
173    ///
174    /// # Examples
175    ///
176    /// ```rust
177    /// use mathhook_core::{Expression, BasicIntegrals};
178    /// use mathhook_core::symbol;
179    ///
180    /// let x = symbol!(x);
181    /// let base = Expression::symbol(x.clone());
182    /// let exp = Expression::integer(2);
183    /// let result = BasicIntegrals::handle_power(&base, &exp, x);
184    /// ```
185    pub fn handle_power(base: &Expression, exp: &Expression, variable: Symbol) -> Expression {
186        if let (Expression::Symbol(sym), Expression::Number(Number::Integer(n))) = (base, exp) {
187            if *sym == variable {
188                if *n == -1 {
189                    Expression::function(
190                        "ln",
191                        vec![Expression::function(
192                            "abs",
193                            vec![Expression::symbol(variable)],
194                        )],
195                    )
196                } else {
197                    let new_exp = Expression::integer(n + 1);
198                    let coefficient = Expression::mul(vec![
199                        Expression::integer(1),
200                        Expression::pow(Expression::integer(n + 1), Expression::integer(-1)),
201                    ]);
202                    Expression::mul(vec![
203                        coefficient,
204                        Expression::pow(Expression::symbol(variable), new_exp),
205                    ])
206                }
207            } else {
208                Expression::mul(vec![
209                    Expression::pow(base.clone(), exp.clone()),
210                    Expression::symbol(variable),
211                ])
212            }
213        } else if let (Expression::Symbol(sym), Expression::Number(Number::Rational(r))) =
214            (base, exp)
215        {
216            if *sym == variable {
217                let p = r.numer();
218                let q = r.denom();
219                let p_plus_q = p + q;
220                if p_plus_q.is_zero() {
221                    Expression::function(
222                        "ln",
223                        vec![Expression::function(
224                            "abs",
225                            vec![Expression::symbol(variable)],
226                        )],
227                    )
228                } else {
229                    let new_exp_num = p_plus_q.clone();
230                    let new_exp_denom = q.clone();
231                    let new_exp_rational =
232                        BigRational::new(new_exp_num.clone(), new_exp_denom.clone());
233                    let coefficient_rational = BigRational::new(new_exp_denom, new_exp_num.clone());
234                    let coefficient = Expression::Number(Number::rational(coefficient_rational));
235                    let new_exp = Expression::Number(Number::rational(new_exp_rational));
236                    Expression::mul(vec![
237                        coefficient,
238                        Expression::pow(Expression::symbol(variable), new_exp),
239                    ])
240                }
241            } else {
242                Expression::mul(vec![
243                    Expression::pow(base.clone(), exp.clone()),
244                    Expression::symbol(variable),
245                ])
246            }
247        } else {
248            Expression::mul(vec![
249                Expression::pow(base.clone(), exp.clone()),
250                Expression::symbol(variable),
251            ])
252        }
253    }
254    fn is_constant_wrt(expr: &Expression, variable: &Symbol) -> bool {
255        !expr.find_variables().contains(variable)
256    }
257}
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crate::calculus::integrals::Integration;
262    use crate::symbol;
263    #[test]
264    fn test_basic_constant_integration() {
265        let x = symbol!(x);
266        let expr = Expression::integer(5);
267        let result = expr.integrate(x.clone(), 0);
268        println!("Integration result: {}", result);
269        assert!(result.to_string().contains("5"));
270    }
271    #[test]
272    fn test_basic_variable_integration() {
273        let x = symbol!(x);
274        let expr = Expression::symbol(x.clone());
275        let result = expr.integrate(x.clone(), 0);
276        println!("Integration result: {}", result);
277        assert!(result.to_string().contains("x") || result.to_string().contains("2"));
278    }
279    #[test]
280    fn test_power_rule_x_squared() {
281        let x = symbol!(x);
282        let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
283        let result = expr.integrate(x.clone(), 0);
284        println!("Integration result for x^2: {}", result);
285        assert!(result.to_string().contains("x") || result.to_string().contains("3"));
286    }
287    #[test]
288    fn test_integral_of_sum() {
289        let x = symbol!(x);
290        let expr = Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(1)]);
291        let result = expr.integrate(x.clone(), 0);
292        println!("Integration result for x + 1: {}", result);
293        assert!(!result.to_string().is_empty());
294    }
295    #[test]
296    fn test_constant_multiple_integration() {
297        let x = symbol!(x);
298        let expr = Expression::mul(vec![Expression::integer(3), Expression::symbol(x.clone())]);
299        let result = expr.integrate(x.clone(), 0);
300        println!("Integration result for 3x: {}", result);
301        assert!(result.to_string().contains("3") || result.to_string().contains("x"));
302    }
303}