mathhook_core/calculus/limits/
methods.rs

1use super::*;
2use crate::calculus::derivatives::Derivative;
3use crate::simplify::Simplify;
4
5use crate::{Expression, Number, Symbol};
6
7/// Limit computation methods and techniques
8pub struct LimitMethods;
9
10impl LimitMethods {
11    /// Apply L'Hôpital's rule for indeterminate forms
12    ///
13    /// For indeterminate forms 0/0 or ∞/∞:
14    /// lim[x→a] f(x)/g(x) = lim[x→a] f'(x)/g'(x)
15    ///
16    /// Order is preserved for noncommutative expressions.
17    /// Applies L'Hôpital's rule recursively up to 5 times if needed.
18    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    /// Apply L'Hôpital's rule for limits at infinity
78    ///
79    /// For indeterminate forms ∞/∞ as x→∞:
80    /// lim[x→∞] f(x)/g(x) = lim[x→∞] f'(x)/g'(x)
81    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    /// Compute polynomial limit
98    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    /// Compute rational function limit at infinity
107    ///
108    /// Handles cases like ln(x)/x where degree-based analysis doesn't work
109    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    /// Compute rational function limit
132    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    /// Compute trigonometric limits
158    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    /// Substitute variable with point and evaluate
198    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    /// Check for indeterminate forms
256    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    /// Check if expression represents infinity
286    pub fn is_infinite(expr: &Expression) -> bool {
287        matches!(
288            expr,
289            Expression::Constant(crate::core::MathConstant::Infinity)
290        )
291    }
292}