mathhook_core/calculus/integrals/
basic.rs1use 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;
10pub struct BasicIntegrals;
12impl BasicIntegrals {
13 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 pub fn handle_constant(expr: &Expression, variable: Symbol) -> Expression {
58 Expression::mul(vec![expr.clone(), Expression::symbol(variable)])
59 }
60 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 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 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 pub fn handle_power(base: &Expression, exp: &Expression, variable: Symbol) -> Expression {
177 if let (Expression::Symbol(sym), Expression::Number(Number::Integer(n))) = (base, exp) {
178 if *sym == variable {
179 if *n == -1 {
180 Expression::function(
181 "ln",
182 vec![Expression::function(
183 "abs",
184 vec![Expression::symbol(variable)],
185 )],
186 )
187 } else {
188 let new_exp = Expression::integer(n + 1);
189 let coefficient = Expression::mul(vec![
190 Expression::integer(1),
191 Expression::pow(Expression::integer(n + 1), Expression::integer(-1)),
192 ]);
193 Expression::mul(vec![
194 coefficient,
195 Expression::pow(Expression::symbol(variable), new_exp),
196 ])
197 }
198 } else {
199 Expression::mul(vec![
200 Expression::pow(base.clone(), exp.clone()),
201 Expression::symbol(variable),
202 ])
203 }
204 } else if let (Expression::Symbol(sym), Expression::Number(Number::Rational(r))) =
205 (base, exp)
206 {
207 if *sym == variable {
208 let p = r.numer();
209 let q = r.denom();
210 let p_plus_q = p + q;
211 if p_plus_q.is_zero() {
212 Expression::function(
213 "ln",
214 vec![Expression::function(
215 "abs",
216 vec![Expression::symbol(variable)],
217 )],
218 )
219 } else {
220 let new_exp_rational = BigRational::new(p_plus_q.clone(), q.clone());
221 let new_exp = Expression::Number(Number::rational(new_exp_rational));
222 let coefficient_rational = BigRational::new(q.clone(), p_plus_q);
223 let coefficient = Expression::Number(Number::rational(coefficient_rational));
224 Expression::mul(vec![
225 coefficient,
226 Expression::pow(Expression::symbol(variable), new_exp),
227 ])
228 }
229 } else {
230 Expression::mul(vec![
231 Expression::pow(base.clone(), exp.clone()),
232 Expression::symbol(variable),
233 ])
234 }
235 } else {
236 Expression::integral(Expression::pow(base.clone(), exp.clone()), variable)
237 }
238 }
239 pub fn is_constant_wrt(expr: &Expression, variable: &Symbol) -> bool {
252 match expr {
253 Expression::Number(_) | Expression::Constant(_) => true,
254 Expression::Symbol(sym) => sym != variable,
255 Expression::Add(terms) => terms.iter().all(|t| Self::is_constant_wrt(t, variable)),
256 Expression::Mul(factors) => factors.iter().all(|f| Self::is_constant_wrt(f, variable)),
257 Expression::Pow(base, exp) => {
258 Self::is_constant_wrt(base, variable) && Self::is_constant_wrt(exp, variable)
259 }
260 Expression::Function { args, .. } => {
261 args.iter().all(|a| Self::is_constant_wrt(a, variable))
262 }
263 _ => false,
264 }
265 }
266}
267#[cfg(test)]
268mod tests {
269 use super::*;
270 use crate::symbol;
271 use num_bigint::BigInt;
272 #[test]
273 fn test_constant_integration() {
274 let x = symbol!(x);
275 let expr = Expression::integer(5);
276 let result = BasicIntegrals::handle_constant(&expr, x.clone());
277 assert_eq!(
278 result,
279 Expression::mul(vec![Expression::integer(5), Expression::symbol(x)])
280 );
281 }
282 #[test]
283 fn test_symbol_integration() {
284 let x = symbol!(x);
285 let result = BasicIntegrals::handle_symbol(&x, &x);
286 let expected = Expression::mul(vec![
287 Expression::rational(1, 2),
288 Expression::pow(Expression::symbol(x), Expression::integer(2)),
289 ]);
290 assert_eq!(result, expected);
291 }
292 #[test]
293 fn test_sum_integration() {
294 let x = symbol!(x);
295 let terms = vec![Expression::symbol(x.clone()), Expression::integer(3)];
296 let result = BasicIntegrals::handle_sum(&terms, &x, 0);
297 assert!(!result.is_zero());
298 }
299 #[test]
300 fn test_noncommutative_matrix_integration_order() {
301 let x = symbol!(x);
302 let a = crate::core::Symbol::matrix("A");
303 let result = BasicIntegrals::handle_symbol(&a, &x);
304 if let Expression::Mul(factors) = &result {
305 assert_eq!(
306 factors.len(),
307 2,
308 "Result should be a product of two factors"
309 );
310 let has_a = factors.iter().any(|f| {
311 if let Expression::Symbol(s) = f {
312 s.name() == "A"
313 } else {
314 false
315 }
316 });
317 let has_x = factors.iter().any(|f| {
318 if let Expression::Symbol(s) = f {
319 s.name() == "x"
320 } else {
321 false
322 }
323 });
324 assert!(has_a, "Result should contain matrix A");
325 assert!(has_x, "Result should contain variable x");
326 } else {
327 panic!("Expected Mul expression for integration result");
328 }
329 }
330 #[test]
331 fn test_noncommutative_operator_integration() {
332 let t = symbol!(t);
333 let p = crate::core::Symbol::operator("P");
334 let result = BasicIntegrals::handle_symbol(&p, &t);
335 if let Expression::Mul(factors) = &result {
336 assert_eq!(
337 factors.len(),
338 2,
339 "Result should be a product of two factors"
340 );
341 let has_p = factors.iter().any(|f| {
342 if let Expression::Symbol(s) = f {
343 s.name() == "P"
344 } else {
345 false
346 }
347 });
348 let has_t = factors.iter().any(|f| {
349 if let Expression::Symbol(s) = f {
350 s.name() == "t"
351 } else {
352 false
353 }
354 });
355 assert!(has_p, "Result should contain operator P");
356 assert!(has_t, "Result should contain variable t");
357 } else {
358 panic!("Expected Mul expression for integration result");
359 }
360 }
361 #[test]
362 fn test_noncommutative_product_integration() {
363 let x = symbol!(x);
364 let a = crate::core::Symbol::matrix("A");
365 let b = crate::core::Symbol::matrix("B");
366 let result = BasicIntegrals::handle_product(
367 &[Expression::symbol(a.clone()), Expression::symbol(b.clone())],
368 x.clone(),
369 0,
370 );
371 assert!(!result.is_zero());
372 }
373 #[test]
374 fn test_commutative_integration_unchanged() {
375 let x = symbol!(x);
376 let y = symbol!(y);
377 let result = BasicIntegrals::handle_symbol(&y, &x);
378 if let Expression::Mul(factors) = &result {
379 assert_eq!(factors.len(), 2);
380 }
381 }
382 #[test]
383 fn test_is_constant_wrt_scalar() {
384 let x = symbol!(x);
385 let y = symbol!(y);
386 assert!(BasicIntegrals::is_constant_wrt(&Expression::integer(5), &x));
387 assert!(BasicIntegrals::is_constant_wrt(
388 &Expression::symbol(y.clone()),
389 &x
390 ));
391 assert!(!BasicIntegrals::is_constant_wrt(
392 &Expression::symbol(x.clone()),
393 &x
394 ));
395 }
396 #[test]
397 fn test_is_constant_wrt_noncommutative() {
398 let x = symbol!(x);
399 let a = crate::core::Symbol::matrix("A");
400 assert!(BasicIntegrals::is_constant_wrt(&Expression::symbol(a), &x));
401 }
402 #[test]
403 fn test_rational_exponent_one_half() {
404 let x = symbol!(x);
405 let base = Expression::symbol(x.clone());
406 let exp = Expression::Number(Number::rational(BigRational::new(
407 BigInt::from(1),
408 BigInt::from(2),
409 )));
410 let result = BasicIntegrals::handle_power(&base, &exp, x.clone());
411 let expected_coeff = Expression::Number(Number::rational(BigRational::new(
412 BigInt::from(2),
413 BigInt::from(3),
414 )));
415 let expected_exp = Expression::Number(Number::rational(BigRational::new(
416 BigInt::from(3),
417 BigInt::from(2),
418 )));
419 let expected = Expression::mul(vec![
420 expected_coeff,
421 Expression::pow(Expression::symbol(x), expected_exp),
422 ]);
423 assert_eq!(result, expected);
424 }
425 #[test]
426 fn test_rational_exponent_two_thirds() {
427 let x = symbol!(x);
428 let base = Expression::symbol(x.clone());
429 let exp = Expression::Number(Number::rational(BigRational::new(
430 BigInt::from(2),
431 BigInt::from(3),
432 )));
433 let result = BasicIntegrals::handle_power(&base, &exp, x.clone());
434 let expected_coeff = Expression::Number(Number::rational(BigRational::new(
435 BigInt::from(3),
436 BigInt::from(5),
437 )));
438 let expected_exp = Expression::Number(Number::rational(BigRational::new(
439 BigInt::from(5),
440 BigInt::from(3),
441 )));
442 let expected = Expression::mul(vec![
443 expected_coeff,
444 Expression::pow(Expression::symbol(x), expected_exp),
445 ]);
446 assert_eq!(result, expected);
447 }
448 #[test]
449 fn test_rational_exponent_minus_one() {
450 let x = symbol!(x);
451 let base = Expression::symbol(x.clone());
452 let exp = Expression::Number(Number::rational(BigRational::new(
453 BigInt::from(-1),
454 BigInt::from(1),
455 )));
456 let result = BasicIntegrals::handle_power(&base, &exp, x.clone());
457 let expected = Expression::function(
458 "ln",
459 vec![Expression::function("abs", vec![Expression::symbol(x)])],
460 );
461 assert_eq!(result, expected);
462 }
463}