mathhook_core/calculus/limits/
methods.rs1use super::*;
2use crate::calculus::derivatives::Derivative;
3use crate::simplify::Simplify;
4
5use crate::{Expression, Number, Symbol};
6
7pub struct LimitMethods;
9
10impl LimitMethods {
11 pub fn lhopital_rule(
19 numerator: &Expression,
20 denominator: &Expression,
21 variable: &Symbol,
22 point: &Expression,
23 ) -> Expression {
24 Self::lhopital_rule_recursive(numerator, denominator, variable, point, 0)
25 }
26
27 fn lhopital_rule_recursive(
28 numerator: &Expression,
29 denominator: &Expression,
30 variable: &Symbol,
31 point: &Expression,
32 depth: usize,
33 ) -> Expression {
34 const MAX_DEPTH: usize = 5;
35
36 if depth >= MAX_DEPTH {
37 return Expression::function(
38 "limit",
39 vec![
40 Expression::mul(vec![
41 numerator.clone(),
42 Expression::pow(denominator.clone(), Expression::integer(-1)),
43 ]),
44 Expression::symbol(variable.clone()),
45 point.clone(),
46 ],
47 );
48 }
49
50 let num_derivative = numerator.derivative(variable.clone());
51 let den_derivative = denominator.derivative(variable.clone());
52
53 let num_at_point = Self::substitute_and_evaluate(&num_derivative, variable, point);
54 let den_at_point = Self::substitute_and_evaluate(&den_derivative, variable, point);
55
56 if num_at_point.is_zero() && den_at_point.is_zero() {
57 return Self::lhopital_rule_recursive(
58 &num_derivative,
59 &den_derivative,
60 variable,
61 point,
62 depth + 1,
63 );
64 }
65
66 if den_at_point.is_zero() {
67 return Expression::infinity();
68 }
69
70 Expression::mul(vec![
71 num_at_point,
72 Expression::pow(den_at_point, Expression::integer(-1)),
73 ])
74 .simplify()
75 }
76
77 pub fn lhopital_rule_at_infinity(
82 numerator: &Expression,
83 denominator: &Expression,
84 variable: &Symbol,
85 ) -> Expression {
86 let num_derivative = numerator.derivative(variable.clone());
87 let den_derivative = denominator.derivative(variable.clone());
88
89 let derivative_ratio = Expression::mul(vec![
90 num_derivative,
91 Expression::pow(den_derivative, Expression::integer(-1)),
92 ]);
93
94 derivative_ratio.limit_at_infinity(variable)
95 }
96
97 pub fn polynomial_limit(
99 expr: &Expression,
100 variable: &Symbol,
101 point: &Expression,
102 ) -> Expression {
103 Self::substitute_and_evaluate(expr, variable, point)
104 }
105
106 pub fn rational_limit_at_infinity(
110 numerator: &Expression,
111 denominator: &Expression,
112 variable: &Symbol,
113 ) -> Expression {
114 let num_limit = numerator.limit_at_infinity(variable);
115 let den_limit = denominator.limit_at_infinity(variable);
116
117 if Self::is_infinite(&num_limit) && Self::is_infinite(&den_limit) {
118 return Self::lhopital_rule_at_infinity(numerator, denominator, variable);
119 }
120
121 if den_limit.is_zero() {
122 return Expression::infinity();
123 }
124
125 Expression::mul(vec![
126 num_limit,
127 Expression::pow(den_limit, Expression::integer(-1)),
128 ])
129 }
130
131 pub fn rational_limit(
133 numerator: &Expression,
134 denominator: &Expression,
135 variable: &Symbol,
136 point: &Expression,
137 ) -> Expression {
138 let num_at_point = Self::substitute_and_evaluate(numerator, variable, point);
139 let den_at_point = Self::substitute_and_evaluate(denominator, variable, point);
140
141 match (&num_at_point, &den_at_point) {
142 (_, den) if den.is_zero() => {
143 if num_at_point.is_zero() {
144 Self::lhopital_rule(numerator, denominator, variable, point)
145 } else {
146 Expression::infinity()
147 }
148 }
149 (num, den) => Expression::mul(vec![
150 num.clone(),
151 Expression::pow(den.clone(), Expression::integer(-1)),
152 ])
153 .simplify(),
154 }
155 }
156
157 pub fn trigonometric_limit(
159 expr: &Expression,
160 variable: &Symbol,
161 point: &Expression,
162 ) -> Expression {
163 if let Expression::Mul(factors) = expr {
164 if factors.len() == 2 {
165 let check_sin_over_x = |(func_expr, pow_expr): (&Expression, &Expression)| -> bool {
166 if let (Expression::Function { name, args }, Expression::Pow(base, exp)) =
167 (func_expr, pow_expr)
168 {
169 name.as_ref() == "sin"
170 && args.len() == 1
171 && base.as_ref() == &args[0]
172 && **exp == Expression::integer(-1)
173 && point.is_zero()
174 } else {
175 false
176 }
177 };
178
179 if check_sin_over_x((&factors[0], &factors[1]))
180 || check_sin_over_x((&factors[1], &factors[0]))
181 {
182 return Expression::integer(1);
183 }
184 }
185 }
186
187 Expression::function(
188 "limit",
189 vec![
190 expr.clone(),
191 Expression::symbol(variable.clone()),
192 point.clone(),
193 ],
194 )
195 }
196
197 pub fn substitute_and_evaluate(
199 expr: &Expression,
200 variable: &Symbol,
201 point: &Expression,
202 ) -> Expression {
203 match expr {
204 Expression::Symbol(sym) => {
205 if sym == variable {
206 point.clone()
207 } else {
208 expr.clone()
209 }
210 }
211 Expression::Add(terms) => {
212 let substituted: Vec<Expression> = terms
213 .iter()
214 .map(|term| Self::substitute_and_evaluate(term, variable, point))
215 .collect();
216 Expression::add(substituted).simplify()
217 }
218 Expression::Mul(factors) => {
219 let substituted: Vec<Expression> = factors
220 .iter()
221 .map(|factor| Self::substitute_and_evaluate(factor, variable, point))
222 .collect();
223
224 let has_zero = substituted.iter().any(|f| f.is_zero());
225 let has_division_by_zero = substituted.iter().any(|f| {
226 matches!(f, Expression::Pow(base, exp)
227 if base.as_ref().is_zero() && matches!(exp.as_ref(), Expression::Number(Number::Integer(n)) if *n < 0))
228 });
229 let has_undefined = substituted
230 .iter()
231 .any(|f| matches!(f, Expression::Function { name, .. } if name.as_ref() == "undefined"));
232
233 if has_zero && (has_undefined || has_division_by_zero) {
234 Expression::mul(substituted)
235 } else {
236 Expression::mul(substituted).simplify()
237 }
238 }
239 Expression::Pow(base, exp) => {
240 let sub_base = Self::substitute_and_evaluate(base, variable, point);
241 let sub_exp = Self::substitute_and_evaluate(exp, variable, point);
242 Expression::pow(sub_base, sub_exp).simplify()
243 }
244 Expression::Function { name, args } => {
245 let substituted_args: Vec<Expression> = args
246 .iter()
247 .map(|arg| Self::substitute_and_evaluate(arg, variable, point))
248 .collect();
249 Expression::function(name.clone(), substituted_args).simplify()
250 }
251 _ => expr.clone(),
252 }
253 }
254
255 pub fn is_indeterminate_form(expr: &Expression, variable: &Symbol, point: &Expression) -> bool {
257 let substituted = Self::substitute_and_evaluate(expr, variable, point);
258
259 match &substituted {
260 Expression::Function { name, args: _ } if name.as_ref() == "undefined" => true,
261 Expression::Mul(factors) if factors.len() == 2 => {
262 (factors[0].is_zero() && Self::is_infinite(&factors[1]))
263 || (factors[1].is_zero() && Self::is_infinite(&factors[0]))
264 || (factors[0].is_zero()
265 && matches!(&factors[1], Expression::Pow(base, exp)
266 if base.as_ref().is_zero() && matches!(exp.as_ref(), Expression::Number(Number::Integer(n)) if *n < 0)))
267 || (factors[1].is_zero()
268 && matches!(&factors[0], Expression::Pow(base, exp)
269 if base.as_ref().is_zero() && matches!(exp.as_ref(), Expression::Number(Number::Integer(n)) if *n < 0)))
270 || (factors[0].is_zero()
271 && matches!(&factors[1], Expression::Function { name, .. } if name.as_ref() == "undefined"))
272 || (factors[1].is_zero()
273 && matches!(&factors[0], Expression::Function { name, .. } if name.as_ref() == "undefined"))
274 }
275 Expression::Pow(base, exp)
276 if base.as_ref().is_zero()
277 && matches!(exp.as_ref(), Expression::Number(Number::Integer(n)) if *n < 0) =>
278 {
279 true
280 }
281 _ => false,
282 }
283 }
284
285 pub fn is_infinite(expr: &Expression) -> bool {
287 matches!(
288 expr,
289 Expression::Constant(crate::core::MathConstant::Infinity)
290 )
291 }
292}