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
5 changes: 4 additions & 1 deletion BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,16 @@ powers of `x` found it.
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.
unevaluated too. An argument real near the destination is read as before. The value at the destination is not read
off an expression holding such a step either, which is how `x / floor(arcsin(x))` was answered.

| 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 |
| `limitright(x / floor(arcsin(x)), x, 1)` | `1`, where it is `(1 + i)/2` | unevaluated |
| `limitleft(x / floor(arcsin(x)), x, -1)` | `1/2`, where it is `(2 - i)/5` | unevaluated |

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,14 +548,11 @@ bool IsRealJustOff()
ApproachFrom.Right => new[] { 1 },
_ => new[] { -1, 1 },
};
if (dist == Real.PositiveInfinity || dist == Real.NegativeInfinity)
return AtALargeDecimal(dist == Real.PositiveInfinity ? argument : argument.Substitute(x, -x), x) is Real;
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)
if (argument.Substitute(x, dist + Rational.Create(offset, 1000000)).Evaled is not Real)
return false;
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ internal static class LimitSolvers
// https://github.com/asc-community/AngouriMath/issues/2266
if (res.Nodes.Any(SitsOnAJump))
return null;
// Nor where a step's argument is real at the destination and complex just short of it, which the node
// declines too (LimitOfAStepFunction): x / floor(arcsin(x)) at 1 from the right is x / floor(arcsin(1 + 1/t))
// here, whose arcsin(1) = pi/2 has the floor 1, where the floor all the way in is 1 - i, and the quotient was
// answered 1 where it is (1 + i)/2.
if (expr.Nodes.Any(node => node is Floorf or Ceilf or Roundf && node.ContainsNode(x)
&& IsComplexJustShortOfInfinity(node.DirectChildren[0], x)))
return null;
if (res.Evaled is Complex limit)
{
MultithreadingFunctional.ExitIfCancelled();
Expand Down Expand Up @@ -77,6 +84,11 @@ internal static class LimitSolvers
return null;
}

/// <summary>Whether <paramref name="argument"/> is real at +oo and not at a large finite point short of it.</summary>
private static bool IsComplexJustShortOfInfinity(Entity argument, Variable x)
=> argument.Substitute(x, Infinity).Evaled is Real { IsNaN: false }
&& LimitFunctional.AtALargeDecimal(argument, x) is Complex and not Real;

/// <summary>
/// Whether <paramref name="node"/> is a step function whose argument evaluates to one of its jumps: an integer for
/// <c>floor</c> and <c>ceil</c>, a half-integer for <c>round</c>, 0 for <c>sgn</c>. Taken on the real part, and on the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,15 @@ internal static bool IsDeterminate(Entity combined, Variable x)
return null;
}

/// <summary>
/// <paramref name="expr"/> at the decimal 10^6 with the downcasting off, so that a factorial or a power of the
/// variable is taken in decimals: at the integer 10^6, 1/x! built a number of five and a half million digits, and
/// asking whether floor(1/x!) was real short of +oo took four seconds.
/// </summary>
internal static Entity AtALargeDecimal(Entity expr, Variable x)
=> MathS.Settings.DowncastingEnabled.As(false,
() => expr.Substitute(x, Real.Create(PeterO.Numbers.EDecimal.FromString("1E6"))).Evaled);

/// <summary>
/// The sign an expression keeps throughout a punctured one-sided neighbourhood of the
/// destination, or <see langword="null"/> where it cannot be read off.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// 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>
/// A floor or a ceil whose argument is real at the destination and complex just off it declines its own limit since
/// #2355, and the substitution that reads an expression's value at the destination answered the expression it is in
/// anyway: <c>x / floor(arcsin(x))</c> at 1 from the right was <c>1</c>, the value at 1, where the floor all the way in
/// is <c>1 - i</c> and the quotient <c>(1 + i)/2</c>. And #2355 asked whether such an argument was real at the integer
/// 10^6 on the way to an infinity, which for <c>1/x!</c> built a number of five and a half million digits. Found by
/// limitcheck at <c>DEPTH=3</c>, <c>SEED=19</c>; no issue was filed.
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ASubstitutionDeclinesAStepItsNodeDeclinesTest
{
[Theory]
[InlineData("x / floor(arcsin(x))", "1", ApproachFrom.Right)]
[InlineData("x / floor(arcsin(x))", "-1", ApproachFrom.Left)]
public void IsNotTheValueAtTheDestination(string input, string destination, ApproachFrom side)
=> Assert.IsType<Limitf>(MathS.FromString(input).Limit("x", destination, side));

[Theory]
[InlineData("x / floor(x + 1/2)", "2", ApproachFrom.Left, "1")]
[InlineData("floor(arctan(x))", "+oo", ApproachFrom.BothSides, "1")]
[InlineData("floor(arctan(x))", "-oo", ApproachFrom.BothSides, "-2")]
[InlineData("floor(x^x)", "2", ApproachFrom.Left, "3")]
public void ARealStepIsReadAsBefore(string input, string destination, ApproachFrom side, string expected)
=> Assert.Equal(MathS.FromString(expected), MathS.FromString(input).Limit("x", destination, side));

// The cap is far from both: the limit takes about 0.2 s, and took 3.9 s building 10^6!.
[Fact]
public void AFactorialOnTheWayToInfinityIsNotBuilt()
{
var task = Task.Factory.StartNew(() => MathS.FromString("floor(1/x!)").Limit("x", "+oo", ApproachFrom.BothSides),
TaskCreationOptions.LongRunning);
Assert.True(task.Wait(TimeSpan.FromSeconds(2)), "the limit of floor(1/x!) at +oo did not finish in two seconds");
}
}
}
Loading