1use crate::algebra::gcd::PolynomialGcd;
30use crate::algebra::polynomial_division::polynomial_div;
31use crate::calculus::derivatives::Derivative;
32use crate::core::{Expression, Symbol};
33use crate::simplify::Simplify;
34
35#[derive(Debug, Clone, PartialEq)]
37pub struct RationalIntegral {
38 pub polynomial_part: Expression,
40
41 pub logarithmic_terms: Vec<(Expression, Expression)>,
43
44 pub remaining: Option<Expression>,
46}
47
48pub fn integrate_rational(
88 numerator: &Expression,
89 denominator: &Expression,
90 var: &Symbol,
91) -> RationalIntegral {
92 if denominator.is_zero() {
93 return RationalIntegral {
94 polynomial_part: Expression::undefined(),
95 logarithmic_terms: vec![],
96 remaining: None,
97 };
98 }
99
100 if numerator.is_zero() {
101 return RationalIntegral {
102 polynomial_part: Expression::integer(0),
103 logarithmic_terms: vec![],
104 remaining: None,
105 };
106 }
107
108 let (quotient, remainder) = polynomial_div(numerator, denominator, var);
109
110 let polynomial_part = integrate_polynomial("ient, var);
111
112 if remainder.is_zero() {
113 return RationalIntegral {
114 polynomial_part,
115 logarithmic_terms: vec![],
116 remaining: None,
117 };
118 }
119
120 let (log_terms, remaining_rational) = hermite_reduce(&remainder, denominator, var);
121
122 RationalIntegral {
123 polynomial_part,
124 logarithmic_terms: log_terms,
125 remaining: remaining_rational,
126 }
127}
128
129fn integrate_polynomial(poly: &Expression, var: &Symbol) -> Expression {
133 match poly {
134 Expression::Number(n) => Expression::mul(vec![
135 Expression::Number(n.clone()),
136 Expression::symbol(var.clone()),
137 ]),
138 Expression::Symbol(s) if s == var => Expression::mul(vec![
139 Expression::rational(1, 2),
140 Expression::pow(Expression::symbol(var.clone()), Expression::integer(2)),
141 ]),
142 Expression::Pow(base, exp) => {
143 if let (Expression::Symbol(s), Expression::Number(n)) = (base.as_ref(), exp.as_ref()) {
144 if s == var {
145 let new_exp = Expression::add(vec![
146 Expression::Number(n.clone()),
147 Expression::integer(1),
148 ]);
149 let denom = Expression::add(vec![
150 Expression::Number(n.clone()),
151 Expression::integer(1),
152 ]);
153 return Expression::mul(vec![
154 Expression::pow(base.as_ref().clone(), new_exp),
155 Expression::pow(denom, Expression::integer(-1)),
156 ])
157 .simplify();
158 }
159 }
160 Expression::function(
161 "integrate",
162 vec![poly.clone(), Expression::symbol(var.clone())],
163 )
164 }
165 Expression::Mul(factors) => {
166 let mut coeff = Expression::integer(1);
167 let mut var_part = Expression::integer(1);
168
169 for factor in factors.iter() {
170 if contains_var(factor, var) {
171 var_part = Expression::mul(vec![var_part, factor.clone()]);
172 } else {
173 coeff = Expression::mul(vec![coeff, factor.clone()]);
174 }
175 }
176
177 let integrated_var_part = integrate_polynomial(&var_part, var);
178 Expression::mul(vec![coeff, integrated_var_part]).simplify()
179 }
180 Expression::Add(terms) => {
181 let integrated_terms: Vec<Expression> = terms
182 .iter()
183 .map(|term| integrate_polynomial(term, var))
184 .collect();
185 Expression::add(integrated_terms).simplify()
186 }
187 _ => Expression::function(
188 "integrate",
189 vec![poly.clone(), Expression::symbol(var.clone())],
190 ),
191 }
192}
193
194fn hermite_reduce(
218 numerator: &Expression,
219 denominator: &Expression,
220 var: &Symbol,
221) -> (Vec<(Expression, Expression)>, Option<Expression>) {
222 let denom_deriv = denominator.derivative(var.clone());
223
224 let d = denominator.gcd(&denom_deriv);
225
226 if d == Expression::integer(1) {
227 return handle_square_free_denominator(numerator, denominator, var);
228 }
229
230 extract_logarithmic_terms(numerator, denominator, &d, var)
231}
232
233fn handle_square_free_denominator(
240 numerator: &Expression,
241 denominator: &Expression,
242 var: &Symbol,
243) -> (Vec<(Expression, Expression)>, Option<Expression>) {
244 let denom_deriv = denominator.derivative(var.clone());
245
246 if let Some(coeff) = extract_derivative_coefficient(numerator, &denom_deriv) {
247 return (vec![(coeff, denominator.clone())], None);
248 }
249
250 (
251 vec![],
252 Some(Expression::div(numerator.clone(), denominator.clone())),
253 )
254}
255
256fn extract_logarithmic_terms(
260 numerator: &Expression,
261 denominator: &Expression,
262 d: &Expression,
263 var: &Symbol,
264) -> (Vec<(Expression, Expression)>, Option<Expression>) {
265 let s = denominator.quo_polynomial(d, var);
266
267 let (_gcd, _s_coeff, t_coeff) = s.cofactors(d);
268
269 let s_deriv = s.derivative(var.clone());
270 let intermediate = Expression::add(vec![
271 d.clone(),
272 Expression::mul(vec![
273 Expression::integer(-1),
274 Expression::mul(vec![t_coeff, s_deriv]),
275 ]),
276 ])
277 .simplify();
278
279 let v = d.gcd(&intermediate);
280
281 if v != Expression::integer(1) && !v.is_zero() {
282 let log_coeff = numerator.quo_polynomial(&v, var);
283
284 let remaining_num = numerator.rem_polynomial(&v, var);
285 let remaining = if !remaining_num.is_zero() {
286 Some(Expression::div(remaining_num, denominator.clone()))
287 } else {
288 None
289 };
290
291 return (vec![(log_coeff, v)], remaining);
292 }
293
294 (
295 vec![],
296 Some(Expression::div(numerator.clone(), denominator.clone())),
297 )
298}
299
300fn extract_derivative_coefficient(
304 numerator: &Expression,
305 derivative: &Expression,
306) -> Option<Expression> {
307 if numerator == derivative {
308 return Some(Expression::integer(1));
309 }
310
311 if let Expression::Mul(factors) = numerator {
312 let mut constant_part = Expression::integer(1);
313 let mut remaining_part = Expression::integer(1);
314
315 for factor in factors.iter() {
316 if !contains_any_symbol(factor) {
317 constant_part = Expression::mul(vec![constant_part, factor.clone()]);
318 } else {
319 remaining_part = Expression::mul(vec![remaining_part, factor.clone()]);
320 }
321 }
322
323 if remaining_part.simplify() == derivative.simplify() {
324 return Some(constant_part.simplify());
325 }
326 }
327
328 None
329}
330
331fn contains_var(expr: &Expression, var: &Symbol) -> bool {
333 match expr {
334 Expression::Symbol(s) => s == var,
335 Expression::Number(_) => false,
336 Expression::Add(terms) | Expression::Mul(terms) => {
337 terms.iter().any(|t| contains_var(t, var))
338 }
339 Expression::Pow(base, exp) => contains_var(base, var) || contains_var(exp, var),
340 Expression::Function { args, .. } => args.iter().any(|a| contains_var(a, var)),
341 _ => false,
342 }
343}
344
345fn contains_any_symbol(expr: &Expression) -> bool {
347 match expr {
348 Expression::Symbol(_) => true,
349 Expression::Number(_) => false,
350 Expression::Add(terms) | Expression::Mul(terms) => terms.iter().any(contains_any_symbol),
351 Expression::Pow(base, exp) => contains_any_symbol(base) || contains_any_symbol(exp),
352 Expression::Function { args, .. } => args.iter().any(contains_any_symbol),
353 _ => false,
354 }
355}
356
357pub fn assemble_integral(result: &RationalIntegral) -> Expression {
361 let mut terms = vec![];
362
363 if result.polynomial_part != Expression::integer(0) {
364 terms.push(result.polynomial_part.clone());
365 }
366
367 for (coeff, arg) in &result.logarithmic_terms {
368 let log_term = Expression::mul(vec![
369 coeff.clone(),
370 Expression::function("ln", vec![Expression::function("abs", vec![arg.clone()])]),
371 ]);
372 terms.push(log_term);
373 }
374
375 if let Some(remaining) = &result.remaining {
376 let symbolic_integral = Expression::function("integrate", vec![remaining.clone()]);
377 terms.push(symbolic_integral);
378 }
379
380 if terms.is_empty() {
381 Expression::integer(0)
382 } else if terms.len() == 1 {
383 terms[0].clone()
384 } else {
385 Expression::add(terms).simplify()
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392 use crate::{expr, symbol};
393
394 #[test]
395 fn test_integrate_polynomial() {
396 let x = symbol!(x);
397
398 let result = integrate_polynomial(&expr!(x), &x);
399 println!("∫ x dx = {}", result);
400 assert!(!result.is_zero());
401
402 let result = integrate_polynomial(&expr!(x ^ 2), &x);
403 println!("∫ x² dx = {}", result);
404 assert!(!result.is_zero());
405
406 let result = integrate_polynomial(&expr!(5), &x);
407 println!("∫ 5 dx = {}", result);
408 assert!(!result.is_zero());
409 }
410
411 #[test]
412 fn test_integrate_rational_exact_division() {
413 let x = symbol!(x);
414
415 let num = expr!((x ^ 2) - 1);
416 let den = expr!(x - 1);
417 let result = integrate_rational(&num, &den, &x);
418
419 println!("∫ (x² - 1)/(x - 1) dx:");
420 println!(" Polynomial part: {}", result.polynomial_part);
421 println!(" Log terms: {:?}", result.logarithmic_terms);
422 println!(" Remaining: {:?}", result.remaining);
423
424 assert!(!result.polynomial_part.is_zero());
425 assert!(result.logarithmic_terms.is_empty());
426 assert!(result.remaining.is_none());
427 }
428
429 #[test]
430 fn test_integrate_rational_with_remainder() {
431 let x = symbol!(x);
432
433 let num = expr!((x ^ 2) + 1);
434 let den = expr!(x - 1);
435 let result = integrate_rational(&num, &den, &x);
436
437 println!("∫ (x² + 1)/(x - 1) dx:");
438 println!(" Polynomial part: {}", result.polynomial_part);
439 println!(" Log terms: {:?}", result.logarithmic_terms);
440 println!(" Remaining: {:?}", result.remaining);
441
442 assert!(!result.polynomial_part.is_zero());
443 }
444
445 #[test]
446 fn test_integrate_rational_logarithmic() {
447 let x = symbol!(x);
448
449 let num = expr!(1);
450 let den = expr!(x - 1);
451 let result = integrate_rational(&num, &den, &x);
452
453 println!("∫ 1/(x - 1) dx:");
454 println!(" Polynomial part: {}", result.polynomial_part);
455 println!(" Log terms: {:?}", result.logarithmic_terms);
456 println!(" Remaining: {:?}", result.remaining);
457
458 assert_eq!(result.polynomial_part, Expression::integer(0));
459 }
460
461 #[test]
462 fn test_integrate_rational_derivative_pattern() {
463 let x = symbol!(x);
464
465 let num = expr!(2 * x);
466 let den = expr!((x ^ 2) + 1);
467 let result = integrate_rational(&num, &den, &x);
468
469 println!("∫ 2x/(x² + 1) dx:");
470 println!(" Polynomial part: {}", result.polynomial_part);
471 println!(" Log terms: {:?}", result.logarithmic_terms);
472 println!(" Remaining: {:?}", result.remaining);
473
474 let assembled = assemble_integral(&result);
475 println!(" Assembled: {}", assembled);
476 }
477
478 #[test]
479 fn test_integrate_rational_partial_fractions() {
480 let x = symbol!(x);
481
482 let num = expr!(1);
483 let den = expr!((x ^ 2) - 1);
484 let result = integrate_rational(&num, &den, &x);
485
486 println!("∫ 1/(x² - 1) dx:");
487 println!(" Polynomial part: {}", result.polynomial_part);
488 println!(" Log terms: {:?}", result.logarithmic_terms);
489 println!(" Remaining: {:?}", result.remaining);
490
491 let assembled = assemble_integral(&result);
492 println!(" Assembled: {}", assembled);
493 }
494
495 #[test]
496 fn test_integrate_rational_complex_numerator() {
497 let x = symbol!(x);
498
499 let num = expr!((3 * (x ^ 2)) + (2 * x) + 1);
500 let den = Expression::mul(vec![expr!(x - 1), expr!((x ^ 2) + 1)]).simplify();
501 let result = integrate_rational(&num, &den, &x);
502
503 println!("∫ (3x² + 2x + 1)/((x-1)(x²+1)) dx:");
504 println!(" Polynomial part: {}", result.polynomial_part);
505 println!(" Log terms: {:?}", result.logarithmic_terms);
506 println!(" Remaining: {:?}", result.remaining);
507
508 let assembled = assemble_integral(&result);
509 println!(" Assembled: {}", assembled);
510 }
511}