mathhook_core/calculus/integrals/
risch.rs1pub mod differential_extension;
18pub mod helpers;
19pub mod hermite;
20pub mod rational;
21pub mod rde;
22
23use crate::core::{Expression, Number, Symbol};
24
25#[derive(Debug, Clone, PartialEq)]
27pub enum RischResult {
28 Integral(Expression),
30
31 NonElementary,
33
34 Unknown,
36}
37
38pub fn try_risch_integration(expr: &Expression, var: &Symbol) -> Option<Expression> {
63 let extensions = differential_extension::build_extension_tower(expr, var.clone())?;
64
65 let (rational_part, transcendental_part) = hermite::hermite_reduction(expr, &extensions)?;
66
67 let rational_integral = if rational_part != Expression::integer(0) {
68 if let Some((num, den)) = extract_rational_form(&rational_part) {
69 let result = rational::integrate_rational(&num, &den, var);
70 rational::assemble_integral(&result)
71 } else {
72 Expression::function(
73 "integrate",
74 vec![rational_part, Expression::symbol(var.clone())],
75 )
76 }
77 } else {
78 Expression::integer(0)
79 };
80
81 match rde::integrate_transcendental(&transcendental_part, &extensions, var) {
82 RischResult::Integral(result) => {
83 if rational_integral == Expression::integer(0) {
84 Some(result)
85 } else {
86 Some(Expression::add(vec![rational_integral, result]))
87 }
88 }
89 RischResult::NonElementary => None,
90 RischResult::Unknown => None,
91 }
92}
93
94fn extract_rational_form(expr: &Expression) -> Option<(Expression, Expression)> {
98 match expr {
99 Expression::Mul(factors) => {
100 let mut numerator_parts = Vec::new();
101 let mut denominator_parts = Vec::new();
102 let mut found_division = false;
103
104 for factor in factors.iter() {
105 if let Expression::Pow(base, exp) = factor {
106 if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
107 if *n < 0 {
108 denominator_parts.push(base.as_ref().clone());
109 found_division = true;
110 continue;
111 }
112 }
113 }
114 numerator_parts.push(factor.clone());
115 }
116
117 if found_division {
118 let num = if numerator_parts.is_empty() {
119 Expression::integer(1)
120 } else if numerator_parts.len() == 1 {
121 numerator_parts[0].clone()
122 } else {
123 Expression::mul(numerator_parts)
124 };
125
126 let den = if denominator_parts.len() == 1 {
127 denominator_parts[0].clone()
128 } else {
129 Expression::mul(denominator_parts)
130 };
131
132 return Some((num, den));
133 }
134
135 None
136 }
137 _ => None,
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::symbol;
145
146 #[test]
147 fn test_risch_basic_exp() {
148 let x = symbol!(x);
149 let integrand = Expression::function("exp", vec![Expression::symbol(x.clone())]);
150
151 let result = try_risch_integration(&integrand, &x);
152 assert!(result.is_some());
153 }
154
155 #[test]
156 fn test_risch_basic_log_derivative() {
157 let x = symbol!(x);
158 let integrand = Expression::div(Expression::integer(1), Expression::symbol(x.clone()));
159
160 let result = try_risch_integration(&integrand, &x);
161 assert!(result.is_some());
162 }
163
164 #[test]
165 fn test_extract_rational_form() {
166 let x = symbol!(x);
167
168 let expr = Expression::mul(vec![
169 Expression::add(vec![
170 Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
171 Expression::integer(1),
172 ]),
173 Expression::pow(
174 Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(-1)]),
175 Expression::integer(-1),
176 ),
177 ]);
178
179 let result = extract_rational_form(&expr);
180 assert!(result.is_some());
181
182 if let Some((num, den)) = result {
183 println!("Extracted: {} / {}", num, den);
184 }
185 }
186}