mathhook_core/calculus/integrals/risch/
hermite.rs1use super::differential_extension::DifferentialExtension;
8use crate::core::{Expression, Number};
9
10pub fn hermite_reduction(
38 expr: &Expression,
39 _extensions: &[DifferentialExtension],
40) -> Option<(Expression, Expression)> {
41 use super::helpers::extract_division;
42
43 if let Some((num, _den)) = extract_division(expr) {
46 if num == Expression::integer(1) {
47 return Some((Expression::integer(0), expr.clone()));
49 }
50 }
51
52 if is_rational_function(expr) {
55 Some((expr.clone(), Expression::integer(0)))
57 } else {
58 Some((Expression::integer(0), expr.clone()))
60 }
61}
62
63pub fn is_rational_function(expr: &Expression) -> bool {
68 use super::helpers::extract_division;
69
70 match expr {
71 Expression::Number(_) | Expression::Constant(_) | Expression::Symbol(_) => true,
72 Expression::Add(terms) => terms.iter().all(is_rational_function),
73 Expression::Mul(factors) => {
74 if let Some((num, den)) = extract_division(expr) {
77 is_rational_function(&num) && is_rational_function(&den)
79 } else {
80 factors.iter().all(is_rational_function)
82 }
83 }
84 Expression::Pow(base, exp) => {
85 if let Expression::Number(Number::Integer(n)) = &**exp {
87 if *n < 0 {
88 return is_rational_function(base);
90 }
91 }
92 is_rational_function(base) && is_nonnegative_integer(exp)
94 }
95 Expression::Function { name, .. } => {
96 !is_transcendental_function(name)
98 }
99 _ => false,
100 }
101}
102
103fn is_nonnegative_integer(expr: &Expression) -> bool {
105 match expr {
106 Expression::Number(Number::Integer(n)) => *n >= 0,
107 _ => false,
108 }
109}
110
111fn is_transcendental_function(name: &str) -> bool {
113 matches!(
114 name,
115 "exp"
116 | "ln"
117 | "log"
118 | "sin"
119 | "cos"
120 | "tan"
121 | "cot"
122 | "sec"
123 | "csc"
124 | "arcsin"
125 | "arccos"
126 | "arctan"
127 | "sinh"
128 | "cosh"
129 | "tanh"
130 )
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::symbol;
137
138 #[test]
139 fn test_is_rational_polynomial() {
140 let x = symbol!(x);
141 let expr = Expression::add(vec![
142 Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
143 Expression::symbol(x.clone()),
144 Expression::integer(1),
145 ]);
146
147 assert!(is_rational_function(&expr));
148 }
149
150 #[test]
151 fn test_is_rational_fraction() {
152 let x = symbol!(x);
153 let expr = Expression::div(
154 Expression::integer(1),
155 Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(1)]),
156 );
157
158 assert!(is_rational_function(&expr));
159 }
160
161 #[test]
162 fn test_is_not_rational_exponential() {
163 let x = symbol!(x);
164 let expr = Expression::function("exp", vec![Expression::symbol(x.clone())]);
165
166 assert!(!is_rational_function(&expr));
167 }
168
169 #[test]
170 fn test_is_not_rational_logarithm() {
171 let x = symbol!(x);
172 let expr = Expression::function("ln", vec![Expression::symbol(x.clone())]);
173
174 assert!(!is_rational_function(&expr));
175 }
176
177 #[test]
178 fn test_hermite_reduction_logarithmic_derivative() {
179 let x = symbol!(x);
180 let expr = Expression::div(Expression::integer(1), Expression::symbol(x.clone()));
181 let extensions = vec![DifferentialExtension::Rational];
182
183 let result = hermite_reduction(&expr, &extensions);
184 assert!(result.is_some());
185
186 let (rational, transcendental) = result.unwrap();
187 assert_eq!(rational, Expression::integer(0));
189 assert_ne!(transcendental, Expression::integer(0));
190 }
191
192 #[test]
193 fn test_hermite_reduction_polynomial() {
194 let x = symbol!(x);
195 let expr = Expression::add(vec![
197 Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
198 Expression::integer(1),
199 ]);
200 let extensions = vec![DifferentialExtension::Rational];
201
202 let result = hermite_reduction(&expr, &extensions);
203 assert!(result.is_some());
204
205 let (rational, transcendental) = result.unwrap();
206 assert_ne!(rational, Expression::integer(0));
208 assert_eq!(transcendental, Expression::integer(0));
209 }
210
211 #[test]
212 fn test_hermite_reduction_transcendental() {
213 let x = symbol!(x);
214 let expr = Expression::function("exp", vec![Expression::symbol(x)]);
215 let extensions = vec![DifferentialExtension::Rational];
216
217 let result = hermite_reduction(&expr, &extensions);
218 assert!(result.is_some());
219
220 let (rational, transcendental) = result.unwrap();
221 assert_eq!(rational, Expression::integer(0));
222 assert_ne!(transcendental, Expression::integer(0));
223 }
224}