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
16 changes: 16 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,22 @@ 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 limit of a modulus asks its argument's whole limit

**Silent: `abs(x * ln(x))` at 0 is `0`, where it was `NaN`, the claim that the limit does not exist.** It is real on both
sides. From the left, |x| |ln|x| + i pi| tends to 0 too. The modulus took its argument's limit as the descent reads it,
and the descent reads x * ln(x) as 0 * -oo, which is NaN. Where that reading has no value, the modulus now asks its
argument's whole limit and takes the modulus of a value. `x * ln(x)` itself is still `NaN` two-sided, and so is
`x^x`. `abs(x^x)` is real on both sides, since |x^x| is |x|^x to the left of 0, and it is now `1` there.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"abs(x * ln(x))".ToEntity().Limit("x", 0)` | `NaN` | `0` |
| `"abs(x * ln(x))".ToEntity().Limit("x", 0, ApproachFrom.Right)` | `limitright(abs(x * ln(x)), x, 0)` | `0` |
| `"abs(sin(x) * ln(x))".ToEntity().Limit("x", 0)` | `NaN` | `0` |
| `"abs(x^2 * ln(x))".ToEntity().Limit("x", 0)` | `NaN` | `0` |
| `"abs(x^x)".ToEntity().Limit("x", 0)` | `NaN` | `1` |

### A factor continuous at the point is taken out before l'Hopital's rule

**`sin(x)^tanh(x)` at 0 from the right is `1`, where it was left unevaluated.** Read through its exponent, the power is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,16 @@ partial record Absf
Mulf(var multiplier, var multiplicand) => multiplier.Abs() * multiplicand.Abs(),
_ => null
};
return split?.ComputeLimitDivideEtImpera(x, dist, side) is { } bySplitting && bySplitting.Evaled != MathS.NaN
? bySplitting
: direct;
if (split?.ComputeLimitDivideEtImpera(x, dist, side) is { } bySplitting && bySplitting.Evaled != MathS.NaN)
return bySplitting;
// And the argument's whole limit, where the descent reads a form off its parts: x * ln(x) is
// 0 * -oo there, which is NaN, so abs(x * ln(x)) at 0 had no limit from either side, and the
// two-sided limit was NaN, where it is 0 -- from the left too, |x| |ln|x| + i pi| tending to 0.
// The modulus of a value is taken; x * ln(x) itself keeps the two-sided convention.
if (direct?.Evaled == MathS.NaN
&& AsTheWholeLimit(Argument, x, dist, side) is { } whole && whole.Evaled is Number { IsNaN: false })
return whole.Abs().InnerSimplified;
return direct;
}
}

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 AngouriMath.Core;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// <c>abs(x * ln(x))</c> at 0 was NaN -- the claim that the limit does not exist -- and unevaluated from each side, where
/// it is 0 from both: real on both sides, and from the left |x| |ln|x| + i pi| tends to 0 too. The modulus took its
/// argument's limit as the descent reads it, and the descent reads x * ln(x) as 0 * -oo, which is NaN. The modulus now
/// asks its argument's whole limit where that reading has no value, and takes the modulus of a value.
/// <c>x * ln(x)</c> itself is still NaN two-sided, having no real values to the left of 0. The same on 2.6.0. Found
/// while checking the modulus of a quotient; no issue was filed.
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AModulusAsksItsArgumentsLimitTest
{
[Theory]
[InlineData("abs(x * ln(x))")]
[InlineData("abs(sin(x) * ln(x))")]
[InlineData("abs(x^2 * ln(x))")]
public void IsZeroFromEachSideAndBoth(string input)
{
foreach (var side in new[] { ApproachFrom.Left, ApproachFrom.Right, ApproachFrom.BothSides })
Assert.Equal(MathS.FromString("0"), MathS.FromString(input).Limit("x", 0, side));
}

// Real on both sides, since |x^x| is |x|^x to the left of 0; x^x itself is not, and is NaN below.
[Fact]
public void AModulusOfXToTheXIsOne()
=> Assert.Equal(MathS.FromString("1"), MathS.FromString("abs(x^x)").Limit("x", 0));

[Theory]
[InlineData("x * ln(x)")]
[InlineData("x^x")]
[InlineData("abs(sin(1/x))")]
public void HasNoTwoSidedLimit(string input)
=> Assert.Equal(MathS.NaN, MathS.FromString(input).Limit("x", 0));
}
}
Loading