Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,20 @@ positive on both sides. `x^x` is still `NaN` at 0.
| `"(x^2)^x".ToEntity().Limit("x", 0)` | `NaN` | `1` |
| `"(x*x)^(x - 1)".ToEntity().Limit("x", 0)` | `NaN` | `+oo` |

### A 0^0 limit whose l'Hopital chain doubles its powers has its value

**Silent: a different answer.** l'Hopital's rule was turned away where a
quotient grew in nodes, and a chain whose divisor is a reciprocal power, `x^n / (1 / g^k)`, grows in its exponents
instead: the derivative of `1/g^k` is over `g^(2k)`, so k doubled at each step and the chain never ended. The whole
exponents are measured as the nodes are now, with the same room, and the limits below have their value. No issue was
filed; a check of limits against values near the destination found it.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `limitright((log(x, 1/2))^(exp(x) - sgn(x)), x, 0)` | `0` | `1` |

The same spelled `(-ln(2)/ln(x))^(exp(x) - 1)` was `1` in 2.6.0 and did not return after it; it is `1` again.

### A limit of a logarithm that is not decided is left unevaluated

**Not silent: what overflowed the stack now returns.** A limit of a logarithm whose argument has no limit, as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,23 @@ private static bool AlreadyBeingDifferentiated(Entity quotient)
/// unchanged at 112/117 and, in total time, within noise of where it was.
/// </remarks>
private static bool GrewTooMuch(Entity quotient, Entity applied)
=> applied.Nodes.Count() > Math.Max(quotient.Nodes.Count() + 8, quotient.Nodes.Count() * 8 / 5);
=> applied.Nodes.Count() > Math.Max(quotient.Nodes.Count() + 8, quotient.Nodes.Count() * 8 / 5)
|| WholePowers(applied) > Math.Max(WholePowers(quotient) + 8, WholePowers(quotient) * 8 / 5);

/// <summary>
/// The whole exponents of <paramref name="expr"/>'s powers, added up: the growth a node count does not see.
/// </summary>
/// <remarks>
/// <c>(e^(-1/x) - 1)^12</c> and <c>(e^(-1/x) - 1)^24</c> have the same nodes, and a chain whose divisor is
/// a reciprocal power, <c>x^n / (1 / g^k)</c>, doubles k at every step, since the derivative of
/// <c>1/g^k</c> is over <c>g^(2k)</c>: the limit of <c>(-ln(2)/ln(x))^(e^x - 1)</c> at 0 from the right
/// went 6, 12, 24, ... and did not finish in ten minutes, the inner limits expanding the powers. A
/// derivative lowers the degrees of a chain that is getting somewhere; so the same room as for the
/// nodes, the larger of eight more and three fifths again.
/// </remarks>
private static int WholePowers(Entity expr)
=> expr.Nodes.Sum(static node => node is Powf(_, Integer { EInteger: var exponent }) && exponent.CanFitInInt32()
? Math.Abs(exponent.ToInt32Unchecked()) : 0);

[ThreadStatic] private static bool suppresslHopital;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// 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;
using static AngouriMath.Entity;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// l'Hopital's rule was turned away where a quotient grew in nodes, and a chain whose divisor is a reciprocal power,
/// <c>x^n / (1 / g^k)</c>, grows in its exponents instead: the derivative of <c>1/g^k</c> is over <c>g^(2k)</c>, so k
/// went 6, 12, 24, ..., and the limit of <c>(-ln(2)/ln(x))^(e^x - 1)</c> at 0 from the right did not finish in ten
/// minutes. It is 1, as 2.6.0 answered; the spelling with <c>log(x, 1/2)</c> and <c>sgn(x)</c>, 2.6.0 answered 0. Found by
/// limitcheck at <c>DEPTH=3</c>, <c>SEED=23</c>; no issue was filed.
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AQuotientWhosePowersGrowIsNotDifferentiatedTest
{
[Theory]
[InlineData("(-ln(2)/ln(x))^(exp(x) - 1)")]
[InlineData("(log(x, 1/2))^(exp(x) - sgn(x))")]
public void IsOneAtZeroFromTheRight(string input)
{
var task = Task.Factory.StartNew(() => MathS.FromString(input).Limit("x", 0, ApproachFrom.Right),
TaskCreationOptions.LongRunning);
// Far from both: the limit takes about 6 s here and past a minute on CI's Windows runner, whose suite runs
// three times as long, and before the fix it did not finish in ten minutes.
Assert.True(task.Wait(TimeSpan.FromMinutes(5)), $"the limit of {input} at 0 from the right did not finish in five minutes");
Assert.Equal(Number.Integer.One, task.Result);
}

[Theory]
[InlineData("1/x^2 - 1/sin(x)^2", "-1/3")]
[InlineData("(1 - cos(x)) / x^2", "1/2")]
[InlineData("x^3 / (x - sin(x))", "6")]
public void AChainThatGetsSomewhereIsAsBefore(string input, string expected)
=> Assert.Equal(MathS.FromString(expected), MathS.FromString(input).Limit("x", 0));
}
}
Loading