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
17 changes: 17 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,23 @@ gives is taken. No issue was filed.
| `limit(exp((cos(x) - 1)/x^2), x, 0)` | `NaN` | `e ^ (-1/2)` |
| `limitright(exp(sin(x)/x), x, 0)` | unevaluated | `e` |

### A limit of a modulus reads it as the quotient or product of moduli

**Silent: `abs(1/x^pi)` at 0 is `+oo`, where it was `NaN`, the claim that the limit does not exist.** The modulus took its
argument's limit and nothing else. To the left of 0, `x^pi` is complex, so the sign `1/x^pi`'s divisor vanishes with
could not be read: that side had no limit, and the two-sided limit was `NaN`. Where the argument's limit gives the
modulus no value, the modulus of a quotient is now read as the quotient of the moduli, and of a product as the
product. This holds for complex values too, and read that way the divisor vanishes from above.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"abs(1/x^pi)".ToEntity().Limit("x", 0)` | `NaN` | `+oo` |
| `"abs(1/x^pi)".ToEntity().Limit("x", 0, ApproachFrom.Left)` | `limitleft(abs(1 / x ^ pi), x, 0)` | `+oo` |
| `"abs(1/sqrt(x))".ToEntity().Limit("x", 0)` | `NaN` | `+oo` |
| `"abs(2/x^(3/2))".ToEntity().Limit("x", 0, ApproachFrom.Left)` | `limitleft(abs(2 / x ^ (3/2)), x, 0)` | `+oo` |
| `"abs(log(cos(x), -1))".ToEntity().Limit("x", 0)` | `NaN` | `+oo` |
| `"abs(log(cos(x), -1))".ToEntity().Limit("x", 0, ApproachFrom.Right)` | `limitright(abs(log(cos(x), -1)), x, 0)` | `+oo` |

### A two-sided limit of an expression real on both sides is where its sides agree

**Silent: a two-sided limit that was `NaN` although both sides agree has their value, where the expression is real on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,24 @@ partial record Signumf
partial record Absf
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> Argument.ComputeLimitDivideEtImpera(x, dist, side)?.Abs();
{
var direct = Argument.ComputeLimitDivideEtImpera(x, dist, side)?.Abs();
if (direct is { } found && found.Evaled != MathS.NaN)
return found;
// The modulus of a quotient is the quotient of the moduli, and of a product the product,
// for complex values as for real ones. Read that way, a divisor that vanishes vanishes from
// above: abs(1/x^pi) at 0 from the left had no limit, x^pi being complex there and its
// sign unreadable, so the two-sided limit was NaN where it is +oo, as 1/abs(x)^pi is.
Entity? split = Argument switch
{
Divf(var dividend, var divisor) => dividend.Abs() / divisor.Abs(),
Mulf(var multiplier, var multiplicand) => multiplier.Abs() * multiplicand.Abs(),
_ => null
};
return split?.ComputeLimitDivideEtImpera(x, dist, side) is { } bySplitting && bySplitting.Evaled != MathS.NaN
? bySplitting
: direct;
}
}

/// <summary>
Expand Down
44 changes: 44 additions & 0 deletions Sources/Tests/UnitTests/Calculus/AModulusOfAQuotientTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// 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(1/x^pi)</c> at 0 was NaN -- the claim that the limit does not exist -- where it is +oo from both sides, as
/// <c>1/abs(x)^pi</c> is. The modulus took its argument's limit and nothing else, and to the left of 0 <c>x^pi</c> is
/// complex, so the sign <c>1/x^pi</c>'s divisor vanishes with could not be read: that side had no limit, and the
/// two-sided limit was NaN. The modulus of a quotient is the quotient of the moduli, and of a product the product,
/// for complex values too, and read that way the divisor vanishes from above. The same on 2.6.0. Found by limitcheck
/// at <c>DEPTH=3</c>, <c>SEED=51</c>, and <c>abs(log(cos(x), -1))</c>, which is pi / abs(ln(cos(x))), at
/// <c>SEED=41</c>; no issue was filed.
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AModulusOfAQuotientTest
{
[Theory]
[InlineData("abs(1/x^pi)")]
[InlineData("abs(2/x^(3/2))")]
[InlineData("abs(1/sqrt(x))")]
[InlineData("abs(log(cos(x), -1))")]
public void IsInfiniteFromEachSideAndBoth(string input)
{
foreach (var side in new[] { ApproachFrom.Left, ApproachFrom.Right, ApproachFrom.BothSides })
Assert.Equal(MathS.FromString("+oo"), MathS.FromString(input).Limit("x", 0, side));
}

// A modulus that has a limit as written keeps it.
[Theory]
[InlineData("abs(sin(x)/x)", "0", "1")]
[InlineData("abs(x/abs(x))", "0", "1")]
[InlineData("abs((x^2 - 1)/(x - 1))", "1", "2")]
public void KeepsWhatItHad(string input, string destination, string expected)
=> Assert.Equal(MathS.FromString(expected), MathS.FromString(input).Limit("x", destination));
}
}
Loading