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
25 changes: 25 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,31 @@ filed; a check of limits against values near the destination found it.

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.

### The error functions and the exponential and trigonometric integrals have limits

**`erf(x)` at +oo is `1`, where it was left unevaluated.** None of erf, erfc, erfi, Si, Ci, Shi, Chi, Ei or li had a
limit at all, not even at a point where it is continuous: `erf(x)` at 1 was unevaluated too. Their limits at the
infinities are now their values there:
- erf: 1 and -1
- erfc: 0 and 2
- erfi and Shi: +oo and -oo
- Si: pi/2 and -pi/2
- Ci: 0
- Chi and li: +oo
- Ei: +oo and 0

A finite limit of the argument is put in where the function is continuous. For the entire ones that is anywhere.
For Ci, Chi, Ei and li it is only a positive real point, since they are singular at 0 and cut below it, and li is
singular at 1 too.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"erf(x)".ToEntity().Limit("x", "+oo")` | `limit(erf(x), x, +oo)` | `1` |
| `"erf(x)".ToEntity().Limit("x", 1)` | `limit(erf(x), x, 1)` | `erf(1)` |
| `"Si(x)".ToEntity().Limit("x", "+oo")` | `limit(Si(x), x, +oo)` | `pi / 2` |
| `"Ei(x)".ToEntity().Limit("x", "-oo")` | `limit(Ei(x), x, -oo)` | `0` |
| `"erf(x)/x + 1".ToEntity().Limit("x", 0)` | `limit(erf(x) / x + 1, x, 0)` | `2 / sqrt(pi) + 1` |

### 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 @@ -402,6 +402,115 @@ partial record Arccosf
}
}

/// <summary>
/// The limit of an error function or an exponential or trigonometric integral, given its
/// argument's limit: the value it tends to at an infinity, and the function of a finite limit
/// where it is continuous there.
/// </summary>
/// <remarks>
/// None of these had a limit at all, so erf(x) at +oo, which is 1, was left unevaluated, and
/// so was erf(x) at 1. A logarithm of something holding one came to a stop that took more than
/// forty seconds: log(x + pi + erf(x), x^2 + tanh(1)) at +oo, which is 2. At an infinity:
/// erf to 1 and -1, erfc to 0 and 2, erfi, Shi to +oo and -oo, Si to pi/2 and -pi/2, Ci to 0,
/// Chi and li to +oo, and Ei to +oo and 0. Erf, erfc, erfi, Si and Shi are entire, so a finite
/// limit is put in as it is. Ci, Chi, Ei and li have a logarithm's singularity at 0 and its cut
/// below it, and li another at 1, so only a positive real limit other than li's 1 is put in;
/// elsewhere nothing is claimed. Each value at an infinity was checked against mpmath at 10^6 and
/// -10^6, and at 50 and -50 for erfi, Shi and Chi.
/// </remarks>
private static Entity? OfAnErrorOrIntegralFunction(Entity node, Entity argument, Variable x, Entity dist, ApproachFrom side)
{
if (argument.ComputeLimitDivideEtImpera(x, dist, side) is not { } limit)
return null;
if (limit.Evaled is Real { IsFinite: false, IsNaN: false } infinity)
{
var upwards = !infinity.IsNegative;
return node switch
{
Erff => upwards ? Integer.One : Integer.MinusOne,
Erfcf => upwards ? Integer.Zero : Integer.Create(2),
Erfif or Shif => upwards ? Real.PositiveInfinity : Real.NegativeInfinity,
Sif => upwards ? MathS.pi / 2 : -MathS.pi / 2,
Cif => upwards ? Integer.Zero : null,
Chif or Lif => upwards ? Real.PositiveInfinity : null,
Eif => upwards ? Real.PositiveInfinity : Integer.Zero,
_ => null
};
}
if (limit.Evaled is not Number { IsFinite: true, IsNaN: false } value)
return null;
var entire = node is Erff or Erfcf or Erfif or Sif or Shif;
if (!entire && !(value is Real { IsPositive: true } && !(node is Lif && value == Integer.One)))
return null;
return node switch
{
Erff => new Erff(limit),
Erfcf => new Erfcf(limit),
Erfif => new Erfif(limit),
Sif => new Sif(limit),
Cif => new Cif(limit),
Shif => new Shif(limit),
Chif => new Chif(limit),
Eif => new Eif(limit),
Lif => new Lif(limit),
_ => null
};
}

partial record Erff
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Erfcf
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Erfif
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Sif
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Cif
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Shif
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Chif
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Eif
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Lif
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
=> OfAnErrorOrIntegralFunction(this, Argument, x, dist, side);
}

partial record Arctanf
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// 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>
/// The error functions and the exponential and trigonometric integrals had no limit at all: <c>erf(x)</c> at +oo,
/// which is 1, was left unevaluated, and so was <c>erf(x)</c> at 1. Their limits at the infinities are now their
/// values there, and a finite limit of the argument is put in where the function is continuous: anywhere for the
/// entire ones, only at a positive real point for Ci, Chi, Ei and li, which are singular at 0 and cut below it, and
/// li singular at 1 too. The same on 2.6.0. Found by limitcheck at <c>DEPTH=3</c>, <c>SEED=53</c>; no issue was
/// filed.
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ErrorAndIntegralFunctionLimitsTest
{
[Theory]
[InlineData("erf(x)", "+oo", "1")]
[InlineData("erf(x)", "-oo", "-1")]
[InlineData("erfc(x)", "+oo", "0")]
[InlineData("erfc(x)", "-oo", "2")]
[InlineData("erfi(x)", "+oo", "+oo")]
[InlineData("Si(x)", "+oo", "pi/2")]
[InlineData("Si(x)", "-oo", "-pi/2")]
[InlineData("Ci(x)", "+oo", "0")]
[InlineData("Shi(x)", "-oo", "-oo")]
[InlineData("Chi(x)", "+oo", "+oo")]
[InlineData("Ei(x)", "+oo", "+oo")]
[InlineData("Ei(x)", "-oo", "0")]
[InlineData("li(x)", "+oo", "+oo")]
[InlineData("erf(x)", "1", "erf(1)")]
[InlineData("Ci(x)", "2", "Ci(2)")]
[InlineData("erf(x)/x + 1", "0", "2/sqrt(pi) + 1")]
public void HasItsLimit(string input, string destination, string expected)
=> Assert.Equal(MathS.FromString(expected).Simplify(), MathS.FromString(input).Limit("x", destination).Simplify());

[Fact]
public void IsOneSidedWhereItsArgumentDiverges()
{
Assert.Equal(MathS.FromString("1"), MathS.FromString("erf(1/x)").Limit("x", 0, ApproachFrom.Right));
Assert.Equal(MathS.FromString("-pi/2").Simplify(), MathS.FromString("Si(1/x)").Limit("x", 0, ApproachFrom.Left).Simplify());
Assert.Equal(MathS.NaN, MathS.FromString("erf(1/x)").Limit("x", 0));
}

// Nothing is claimed on Ci's cut or at li's pole.
[Theory]
[InlineData("Ci(x)", "-2")]
[InlineData("Ci(x)", "-oo")]
public void IsNotAnsweredOnACut(string input, string destination)
=> Assert.IsType<Entity.Limitf>(MathS.FromString(input).Limit("x", destination));
}
}
Loading