diff --git a/Sources/AngouriMath/Functions/Continuous/Limits/Solvers/Solvers.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Limits/Solvers/Solvers.Definition.cs
index 42eb982b6..c0130ad6e 100644
--- a/Sources/AngouriMath/Functions/Continuous/Limits/Solvers/Solvers.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Limits/Solvers/Solvers.Definition.cs
@@ -34,9 +34,30 @@ internal static partial class LimitFunctional
finally { LeaveApproach(outerApproach); }
}
+ ///
+ /// A power of the reciprocal of the variable as a power of the variable: (1/x)^p as
+ /// x^(-p), and (-1/x)^p as (-1)^p * x^(-p).
+ ///
+ ///
+ /// Exact on the way to +oo, where x is real and positive, and on the principal branch
+ /// for any exponent. A one-sided limit at 0 is taken by putting 0 + 1/x or 0 - 1/x for
+ /// x, so these are what the root of the variable turns into, and nothing rewrites them:
+ /// x^3 * sqrt(1/x) / (x^2 * (1/x)^(-1/2)), which is 1, took 1.6 s, l'Hopital's rule
+ /// differentiating it sixteen deep, and lim x->0+ ln(x) / ln(sqrt(x)) asked it eight
+ /// times on the way to 2 and took 16 s.
+ ///
+ private static Entity AsAPowerOfTheVariable(Entity node, Variable x)
+ => node switch
+ {
+ Powf(Divf(Integer one, var v), var p) when one == 1 && v == x && !p.ContainsNode(x) => MathS.Pow(x, -p),
+ Powf(Divf(Integer minusOne, var v), var p) when minusOne == -1 && v == x && !p.ContainsNode(x)
+ => MathS.Pow(-1, p) * MathS.Pow(x, -p),
+ _ => node
+ };
+
private static Entity? SimplifyAndComputeLimitToInfinityCore(Entity expr, Variable x)
{
- expr = expr.Simplify();
+ expr = expr.Replace(node => AsAPowerOfTheVariable(node, x)).Simplify();
if (expr is Providedf(var expression, _)) expr = expression; // limits operate assuming a continuous expression even though some points may be undefined.
// The second remarkable limit reads a 1^oo, and it ran once, at the top of
diff --git a/Sources/Tests/UnitTests/Calculus/AReciprocalsPowerAtInfinityTest.cs b/Sources/Tests/UnitTests/Calculus/AReciprocalsPowerAtInfinityTest.cs
new file mode 100644
index 000000000..b12343c82
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AReciprocalsPowerAtInfinityTest.cs
@@ -0,0 +1,47 @@
+//
+// Copyright (c) 2019-2026 Angouri.
+// AngouriMath is licensed under MIT.
+// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
+// Website: https://am.angouri.org.
+//
+
+using System;
+using System.Threading.Tasks;
+using AngouriMath.Core;
+using Xunit;
+
+namespace AngouriMath.Tests.Calculus
+{
+ ///
+ /// A one-sided limit at 0 is taken at infinity by putting 1/x for x, so a root of the variable arrives
+ /// there as (1/x)^p, which nothing rewrote. x^3 * sqrt(1/x) / (x^2 * (1/x)^(-1/2)), which is 1, then went
+ /// to l'Hopital's rule sixteen deep, and ln(x) / ln(sqrt(x)) from the right at 0 asked it eight times on the way
+ /// to 2: 13 s, and ln(sin(x)) / ln(sqrt(x)) over 20 s. Found by limitcheck at DEPTH=3,
+ /// SEED=26, as log(sqrt(x), floor(x) + arctan(x)); no issue was filed.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AReciprocalsPowerAtInfinityTest
+ {
+ // The cap is far from both: each takes under 0.2 s, and took 13 to over 20 s.
+ [Theory]
+ [InlineData("ln(x) / ln(sqrt(x))", "0", ApproachFrom.Right, "2")]
+ [InlineData("ln(sin(x)) / ln(sqrt(x))", "0", ApproachFrom.Right, "2")]
+ [InlineData("log(sqrt(x), floor(x) + arctan(x))", "0", ApproachFrom.Right, "2")]
+ [InlineData("ln(1/x) / ln(sqrt(1/x))", "+oo", ApproachFrom.BothSides, "2")]
+ public void IsAnsweredPromptly(string input, string destination, ApproachFrom side, string expected)
+ {
+ var task = Task.Factory.StartNew(() => MathS.FromString(input).Limit("x", destination, side),
+ TaskCreationOptions.LongRunning);
+ Assert.True(task.Wait(TimeSpan.FromSeconds(5)), $"lim {side} x->{destination} {input} did not finish in five seconds");
+ Assert.Equal(MathS.FromString(expected), task.Result);
+ }
+
+ // The reciprocal's power from the left side, which the substitution writes as (-1/x)^p: (-1)^p x^(-p) for x > 0.
+ [Theory]
+ [InlineData("sqrt(-x) / x", "0", ApproachFrom.Left, "-oo")]
+ [InlineData("sqrt(x) * ln(x)", "0", ApproachFrom.Right, "0")]
+ [InlineData("x^(1/3) / sqrt(x)", "0", ApproachFrom.Right, "+oo")]
+ public void IsTheLimit(string input, string destination, ApproachFrom side, string expected)
+ => Assert.Equal(MathS.FromString(expected), MathS.FromString(input).Limit("x", destination, side));
+ }
+}