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 @@ -307,6 +307,20 @@ powers of `x` found it.
|---|---|---|
| `"(3x - 4) * sqrt(x) + 3 + 3x - 4x^2 = 0".Solve("x")` | four roots, one of them 1.3233…, which is not a root | the other three |

### A floor or a ceil of a complex argument near a real limit is not the step of the limit

**Silent: a different answer.** The limit of a floor or a ceil read its argument as real along the path. `4^((-1)^x)`
at 1 from the right tends to 1/4 from below the real line, where every floor is `-i`, and the limit was the floor of 1/4,
`0`. An argument that is complex just off the destination now has its imaginary part's side asked, and where that is
not decided the limit is left unevaluated. Here it is not decided, so the side whose answer was right, the left, is left
unevaluated too. An argument real near the destination is read as before.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `limitright(floor(4^((-1)^x)), x, 1)` | `0` | unevaluated |
| `limitleft(ceil(4^((-1)^x)), x, 1)` | `1` | unevaluated |
| `limitleft(floor(4^((-1)^x)), x, 1)` | `0` | unevaluated |

### A step function that has a limit from one side is that limit on that side

**Silent: a limit of a power whose base is a `floor`, `ceil`, `round` or `sgn` that is 0 on the side approached is 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,20 +498,66 @@ partial record Absf
// genuinely complex limit has an imaginary part that can arrive at an integer from
// one side or the other.
//
// Like the Absf and Signumf overrides above, this reads the argument as real-valued
// along the path. An argument that is complex near the destination and real at it
// can still be answered here when it should not be; that is the assumption those
// two already make, and narrowing it wants a way to decide realness that this
// library does not have yet -- https://github.com/asc-community/AngouriMath/issues/721.
// The Absf and Signumf overrides above read the argument as real-valued along the path.
// This one asks: an argument that is complex near the destination and real at it is
// read below by the side its imaginary part arrives from.
if (value is not Complex { RealPart: var real, ImaginaryPart: var imaginary })
return null;
// A real limit of an argument that is not real near the destination has an imaginary
// part tending to 0, which is a jump of its own: floor(4^((-1)^x)) at 1 from the right
// tends to 1/4 from below the real line, where every value's floor is -i, and the
// floor of the limit, 0, was the answer. Where the argument is real near the
// destination -- every argument this read as real before, sqrt(x) at 4 among them --
// nothing is added; where it is not, the side the imaginary part arrives from decides
// the step, and where that is not decided the limit is declined.
Entity imaginaryStep = Integer.Zero;
if (value is Real)
{
if (ImaginaryStep() is not { } step)
return null;
imaginaryStep = step;
}
Entity WithTheImaginaryStep(Entity realAnswer)
=> imaginaryStep is Integer { IsZero: true } ? realAnswer : realAnswer + imaginaryStep * MathS.i;
if (SitsOnAJump(real))
return value is Integer jump && SignNear(argument - jump, x, dist, side) is { } arrivesFrom
? atAJump(jump, arrivesFrom)
? WithTheImaginaryStep(atAJump(jump, arrivesFrom))
: null;
if (value is not Real && SitsOnAJump(imaginary))
return null;
return rebuild(limit);
return WithTheImaginaryStep(rebuild(limit));

// Real near the destination where it is real at a point just off it on each side
// approached: realness is not decided for every shape -- x^x is outside what
// TreeAnalyzer lists -- and a sample only ever sends the limit to the question of the
// imaginary part's side, or to declining, never to an answer of its own.
Entity? ImaginaryStep()
{
if (Functions.TreeAnalyzer.IsRealValued(argument, x) || IsRealJustOff())
return Integer.Zero;
return SignNear(MathS.Im(argument), x, dist, side) is { } arrivesFrom && arrivesFrom != 0
? atAJump(Integer.Zero, arrivesFrom)
: null;
}

bool IsRealJustOff()
{
var offsets = side switch
{
ApproachFrom.Left => new[] { -1 },
ApproachFrom.Right => new[] { 1 },
_ => new[] { -1, 1 },
};
foreach (var offset in offsets)
{
Entity at = dist == Real.PositiveInfinity ? Integer.Create(1000000)
: dist == Real.NegativeInfinity ? Integer.Create(-1000000)
: dist + Rational.Create(offset, 1000000);
if (argument.Substitute(x, at).Evaled is not Real)
return false;
}
return true;
}

static bool SitsOnAJump(Real part) =>
part is Integer || part.EDecimal.CompareTo(part.EDecimal.Floor()) == 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// 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;
using static AngouriMath.Entity;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// The limit of a floor or a ceil read its argument as real along the path. <c>4^((-1)^x)</c> at 1 from the right
/// tends to 1/4 from below the real line, so that every floor near it is <c>-i</c>, and the limit was the floor of
/// 1/4, 0. An argument that is complex just off the destination now has its imaginary part's side asked, and where
/// that is not decided the limit is declined. An argument real there is read as before. Found by limitcheck at
/// <c>DEPTH=3</c>; no issue was filed.
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AStepOfAComplexArgumentNearARealLimitTest
{
[Theory]
[InlineData("floor(4^((-1)^x))", "1", ApproachFrom.Right)]
[InlineData("floor(4^((-1)^x))", "-1", ApproachFrom.Right)]
[InlineData("ceil(4^((-1)^x))", "1", ApproachFrom.Left)]
public void IsNotTheStepOfTheLimit(string input, string destination, ApproachFrom side)
=> Assert.IsType<Limitf>(MathS.FromString(input).Limit("x", destination, side));

[Theory]
[InlineData("floor(sqrt(x))", "4", ApproachFrom.Left, "1")]
[InlineData("floor(sqrt(x))", "4", ApproachFrom.Right, "2")]
[InlineData("floor(x^x)", "2", ApproachFrom.Left, "3")]
[InlineData("floor(x^x)", "1", ApproachFrom.Left, "0")]
[InlineData("floor(ln(x))", "1", ApproachFrom.Left, "-1")]
[InlineData("ceil(x^(1/3))", "1", ApproachFrom.Right, "2")]
public void ARealArgumentIsReadAsBefore(string input, string destination, ApproachFrom side, string expected)
=> Assert.Equal(MathS.FromString(expected), MathS.FromString(input).Limit("x", destination, side));
}
}
Loading