mathhook_core/calculus/
limits.rs

1//! Limit computation and analysis
2//!
3//! Implements symbolic limit computation including one-sided limits,
4//! limits at infinity, and indeterminate form resolution with complete
5//! step-by-step educational explanations.
6//!
7//! Preserves order for noncommutative expressions (matrices, operators, quaternions).
8pub mod educational;
9pub mod methods;
10
11use crate::core::polynomial::PolynomialProperties;
12use crate::core::{Expression, Number, Symbol};
13use methods::LimitMethods;
14
15/// Direction for limit computation
16#[derive(Debug, Clone, Copy, PartialEq)]
17pub enum LimitDirection {
18    /// Two-sided limit
19    Both,
20    /// Left-sided limit (approaching from below)
21    Left,
22    /// Right-sided limit (approaching from above)
23    Right,
24}
25
26/// Trait for limit operations
27pub trait Limits {
28    /// Compute two-sided limit
29    ///
30    /// # Examples
31    ///
32    /// ```rust
33    /// use mathhook_core::{Expression, symbol};
34    /// use mathhook_core::calculus::Limits;
35    ///
36    /// let x = symbol!(x);
37    /// let expr = Expression::mul(vec![
38    ///     Expression::symbol(x.clone()),
39    ///     Expression::function("sin", vec![Expression::symbol(x.clone())])
40    /// ]);
41    /// let point = Expression::integer(0);
42    /// let result = expr.limit(&x, &point);
43    /// ```
44    fn limit(&self, variable: &Symbol, point: &Expression) -> Expression;
45
46    /// Compute one-sided limit
47    ///
48    /// # Examples
49    ///
50    /// ```rust
51    /// use mathhook_core::{Expression, symbol};
52    /// use mathhook_core::calculus::{Limits, LimitDirection};
53    ///
54    /// let x = symbol!(x);
55    /// let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1));
56    /// let point = Expression::integer(0);
57    /// let result = expr.limit_directed(&x, &point, LimitDirection::Right);
58    /// ```
59    fn limit_directed(
60        &self,
61        variable: &Symbol,
62        point: &Expression,
63        direction: LimitDirection,
64    ) -> Expression;
65
66    /// Compute limit at infinity
67    ///
68    /// # Examples
69    ///
70    /// ```rust
71    /// use mathhook_core::{Expression, symbol};
72    /// use mathhook_core::calculus::Limits;
73    ///
74    /// let x = symbol!(x);
75    /// let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1));
76    /// let result = expr.limit_at_infinity(&x);
77    /// ```
78    fn limit_at_infinity(&self, variable: &Symbol) -> Expression;
79
80    /// Compute limit at negative infinity
81    ///
82    /// # Examples
83    ///
84    /// ```rust
85    /// use mathhook_core::{Expression, symbol};
86    /// use mathhook_core::calculus::Limits;
87    ///
88    /// let x = symbol!(x);
89    /// let expr = Expression::function("exp", vec![Expression::symbol(x.clone())]);
90    /// let result = expr.limit_at_negative_infinity(&x);
91    /// ```
92    fn limit_at_negative_infinity(&self, variable: &Symbol) -> Expression;
93}
94
95impl Limits for Expression {
96    fn limit(&self, variable: &Symbol, point: &Expression) -> Expression {
97        // Check for rational function pattern BEFORE substitution
98        // This handles f(x)/g(x) written as f(x) * g(x)^(-n)
99        match self {
100            Expression::Mul(factors) if factors.len() == 2 => {
101                // Check for rational function: numerator * denominator^(negative)
102                // This handles x^(-1), x^(-2), etc.
103                if let Expression::Pow(denom, exp) = &factors[1] {
104                    if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
105                        if *n < 0 {
106                            // Convert: numer * denom^(-n) → numer / denom^n
107                            let positive_exp = Expression::integer(-n);
108                            let denominator_positive =
109                                Expression::pow(denom.as_ref().clone(), positive_exp);
110                            return LimitMethods::rational_limit(
111                                &factors[0],
112                                &denominator_positive,
113                                variable,
114                                point,
115                            );
116                        }
117                    }
118                }
119                if let Expression::Pow(denom, exp) = &factors[0] {
120                    if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
121                        if *n < 0 {
122                            // Convert: denom^(-n) * numer → numer / denom^n
123                            let positive_exp = Expression::integer(-n);
124                            let denominator_positive =
125                                Expression::pow(denom.as_ref().clone(), positive_exp);
126                            return LimitMethods::rational_limit(
127                                &factors[1],
128                                &denominator_positive,
129                                variable,
130                                point,
131                            );
132                        }
133                    }
134                }
135            }
136            _ => {}
137        }
138
139        // For other cases, try substitution first
140        let substituted = LimitMethods::substitute_and_evaluate(self, variable, point);
141
142        if !LimitMethods::is_indeterminate_form(&substituted, variable, point) {
143            return substituted;
144        }
145
146        match self {
147            Expression::Mul(_factors) => LimitMethods::trigonometric_limit(self, variable, point),
148            Expression::Function { name: _, args: _ } => {
149                LimitMethods::trigonometric_limit(self, variable, point)
150            }
151            _ => Expression::function(
152                "limit",
153                vec![
154                    self.clone(),
155                    Expression::symbol(variable.clone()),
156                    point.clone(),
157                ],
158            ),
159        }
160    }
161
162    fn limit_directed(
163        &self,
164        variable: &Symbol,
165        point: &Expression,
166        direction: LimitDirection,
167    ) -> Expression {
168        let direction_expr = match direction {
169            LimitDirection::Both => Expression::symbol("both"),
170            LimitDirection::Left => Expression::symbol("left"),
171            LimitDirection::Right => Expression::symbol("right"),
172        };
173
174        Expression::function(
175            "limit_directed",
176            vec![
177                self.clone(),
178                Expression::symbol(variable.clone()),
179                point.clone(),
180                direction_expr,
181            ],
182        )
183    }
184
185    fn limit_at_infinity(&self, variable: &Symbol) -> Expression {
186        match self {
187            Expression::Number(_) | Expression::Constant(_) => self.clone(),
188
189            Expression::Symbol(s) if s == variable => Expression::infinity(),
190
191            Expression::Function { name, args } => {
192                // ln(x), log(x) → infinity
193                if (name.as_ref() == "ln" || name.as_ref() == "log") && args.len() == 1 {
194                    if let Expression::Symbol(s) = &args[0] {
195                        if s == variable {
196                            return Expression::infinity();
197                        }
198                    }
199                }
200
201                Expression::function(
202                    "limit",
203                    vec![
204                        self.clone(),
205                        Expression::symbol(variable.clone()),
206                        Expression::infinity(),
207                    ],
208                )
209            }
210
211            Expression::Pow(base, exp) => {
212                // (1 + 1/x)^x → e
213                if let Expression::Symbol(exp_sym) = exp.as_ref() {
214                    if exp_sym == variable {
215                        let base_limit = base.limit_at_infinity(variable);
216                        if base_limit == Expression::integer(1) {
217                            return Expression::e();
218                        }
219                    }
220                }
221
222                if let Expression::Symbol(s) = base.as_ref() {
223                    if s == variable {
224                        match exp.as_ref() {
225                            Expression::Number(Number::Integer(n)) if *n > 0 => {
226                                return Expression::infinity();
227                            }
228                            Expression::Number(Number::Integer(n)) if *n < 0 => {
229                                return Expression::integer(0);
230                            }
231                            _ => {}
232                        }
233                    }
234                }
235
236                Expression::function(
237                    "limit",
238                    vec![
239                        self.clone(),
240                        Expression::symbol(variable.clone()),
241                        Expression::infinity(),
242                    ],
243                )
244            }
245
246            Expression::Add(_terms) => {
247                if let Some(degree) = self.degree(variable) {
248                    let lc = self.leading_coefficient(variable);
249                    if degree > 0 {
250                        return Expression::infinity();
251                    } else if degree == 0 {
252                        return lc;
253                    }
254                }
255
256                Expression::function(
257                    "limit",
258                    vec![
259                        self.clone(),
260                        Expression::symbol(variable.clone()),
261                        Expression::infinity(),
262                    ],
263                )
264            }
265
266            Expression::Mul(factors) => {
267                let mut numer_factors = Vec::new();
268                let mut denom_factors = Vec::new();
269
270                for factor in factors.as_ref() {
271                    match factor {
272                        Expression::Pow(base, exp) if *exp.as_ref() == Expression::integer(-1) => {
273                            denom_factors.push(base.as_ref().clone());
274                        }
275                        _ => {
276                            numer_factors.push(factor.clone());
277                        }
278                    }
279                }
280
281                if !denom_factors.is_empty() {
282                    let numerator = if numer_factors.is_empty() {
283                        Expression::integer(1)
284                    } else if numer_factors.len() == 1 {
285                        numer_factors[0].clone()
286                    } else {
287                        Expression::mul(numer_factors)
288                    };
289
290                    let denominator = if denom_factors.len() == 1 {
291                        denom_factors[0].clone()
292                    } else {
293                        Expression::mul(denom_factors)
294                    };
295
296                    let num_degree = numerator.degree(variable);
297                    let den_degree = denominator.degree(variable);
298
299                    match (num_degree, den_degree) {
300                        (Some(nd), Some(dd)) if nd == dd => {
301                            let num_lc = numerator.leading_coefficient(variable);
302                            let den_lc = denominator.leading_coefficient(variable);
303                            return Expression::mul(vec![
304                                num_lc,
305                                Expression::pow(den_lc, Expression::integer(-1)),
306                            ]);
307                        }
308                        (Some(nd), Some(dd)) if nd > dd => {
309                            return Expression::infinity();
310                        }
311                        (Some(nd), Some(dd)) if nd < dd => {
312                            return Expression::integer(0);
313                        }
314                        (None, Some(_)) | (Some(_), None) => {
315                            return LimitMethods::rational_limit_at_infinity(
316                                &numerator,
317                                &denominator,
318                                variable,
319                            );
320                        }
321                        _ => {}
322                    }
323                }
324
325                if let Some(degree) = self.degree(variable) {
326                    if degree > 0 {
327                        return Expression::infinity();
328                    } else if degree == 0 {
329                        return self.clone();
330                    }
331                }
332
333                Expression::function(
334                    "limit",
335                    vec![
336                        self.clone(),
337                        Expression::symbol(variable.clone()),
338                        Expression::infinity(),
339                    ],
340                )
341            }
342
343            _ => Expression::function(
344                "limit",
345                vec![
346                    self.clone(),
347                    Expression::symbol(variable.clone()),
348                    Expression::infinity(),
349                ],
350            ),
351        }
352    }
353
354    fn limit_at_negative_infinity(&self, variable: &Symbol) -> Expression {
355        Expression::function(
356            "limit",
357            vec![
358                self.clone(),
359                Expression::symbol(variable.clone()),
360                Expression::mul(vec![Expression::integer(-1), Expression::infinity()]),
361            ],
362        )
363    }
364}
365
366#[cfg(test)]
367mod tests {
368    use super::*;
369    use crate::symbol;
370
371    #[test]
372    fn test_polynomial_limit() {
373        let x = symbol!(x);
374        let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
375        let point = Expression::integer(3);
376        let result = expr.limit(&x, &point);
377
378        assert_eq!(result, Expression::integer(9));
379    }
380
381    #[test]
382    fn test_rational_limit_continuous() {
383        let x = symbol!(x);
384        let numerator =
385            Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(1)]);
386        let denominator =
387            Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(2)]);
388        let expr = Expression::mul(vec![
389            numerator,
390            Expression::pow(denominator, Expression::integer(-1)),
391        ]);
392        let point = Expression::integer(1);
393        let result = expr.limit(&x, &point);
394
395        assert_eq!(result, Expression::rational(2, 3));
396    }
397
398    #[test]
399    fn test_trigonometric_limit() {
400        let x = symbol!(x);
401        let sin_x = Expression::function("sin", vec![Expression::symbol(x.clone())]);
402        let expr = Expression::mul(vec![
403            sin_x,
404            Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1)),
405        ]);
406        let point = Expression::integer(0);
407        let result = expr.limit(&x, &point);
408
409        assert_eq!(result, Expression::integer(1));
410    }
411
412    #[test]
413    fn test_limit_at_infinity_constant() {
414        let x = symbol!(x);
415        let expr = Expression::integer(5);
416        let result = expr.limit_at_infinity(&x);
417
418        assert_eq!(result, Expression::integer(5));
419    }
420
421    #[test]
422    fn test_limit_at_infinity_variable() {
423        let x = symbol!(x);
424        let expr = Expression::symbol(x.clone());
425        let result = expr.limit_at_infinity(&x);
426
427        assert_eq!(result, Expression::infinity());
428    }
429
430    #[test]
431    fn test_limit_at_infinity_inverse() {
432        let x = symbol!(x);
433        let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1));
434        let result = expr.limit_at_infinity(&x);
435
436        assert_eq!(result, Expression::integer(0));
437    }
438
439    #[test]
440    fn test_limit_at_infinity_polynomial() {
441        let x = symbol!(x);
442        let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(3));
443        let result = expr.limit_at_infinity(&x);
444
445        assert_eq!(result, Expression::infinity());
446    }
447
448    #[test]
449    fn test_limit_at_infinity_rational_same_degree() {
450        let x = symbol!(x);
451        let numerator = Expression::mul(vec![
452            Expression::integer(3),
453            Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
454        ]);
455        let denominator = Expression::mul(vec![
456            Expression::integer(2),
457            Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
458        ]);
459        let expr = Expression::mul(vec![
460            numerator,
461            Expression::pow(denominator, Expression::integer(-1)),
462        ]);
463        let result = expr.limit_at_infinity(&x);
464
465        assert_eq!(result, Expression::rational(3, 2));
466    }
467
468    #[test]
469    fn test_limit_at_infinity_rational_num_greater() {
470        let x = symbol!(x);
471        let numerator = Expression::pow(Expression::symbol(x.clone()), Expression::integer(3));
472        let denominator = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
473        let expr = Expression::mul(vec![
474            numerator,
475            Expression::pow(denominator, Expression::integer(-1)),
476        ]);
477        let result = expr.limit_at_infinity(&x);
478
479        assert_eq!(result, Expression::infinity());
480    }
481
482    #[test]
483    fn test_limit_at_infinity_rational_den_greater() {
484        let x = symbol!(x);
485        let numerator = Expression::pow(Expression::symbol(x.clone()), Expression::integer(1));
486        let denominator = Expression::pow(Expression::symbol(x.clone()), Expression::integer(3));
487        let expr = Expression::mul(vec![
488            numerator,
489            Expression::pow(denominator, Expression::integer(-1)),
490        ]);
491        let result = expr.limit_at_infinity(&x);
492
493        assert_eq!(result, Expression::integer(0));
494    }
495}