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 @@ -450,6 +450,23 @@ Two-sided, each side is now taken with its own value, and `floor(x)^x` at 0 is 1
| `"floor(x)^tanh(x)".ToEntity().Limit("x", 0, ApproachFrom.Right)` | unevaluated | `0` |
| `"floor(x)^x".ToEntity().Limit("x", 0)` | unevaluated | `NaN`, the sides differ |

### A function of a quotient tending to oo/oo has the value at the quotient's limit

**`exp(log(1/x, arcsin(x)))` at 0 from the right is `1/e`, where it was left unevaluated.** The logarithm alone is
-1, and it reaches the descent as `ln(arcsin(x)) / ln(1/x)`, a quotient of two infinities. The limit its derivatives'
quotient was taken to came back as the form `0 * +oo / 0`, which has no value, and that was handed on as the
exponent's limit. The whole limit of the quotient was asked only for a 0/0 read off two finite parts. It is now asked
for -oo/+oo too, and a form with no value is kept only where nothing better is found. A sine of the logarithm is
answered as well. A hyperbolic sine took ten seconds to be left unevaluated, and is answered in under three.
`x * ln(x)` at 0 is still `NaN`, since ln(x) has no real values to the left of 0.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"e^log(1/x, arcsin(x))".ToEntity().Limit("x", 0, ApproachFrom.Right)` | `limitright(e ^ log(1 / x, arcsin(x)), x, 0)` | `1 / e` |
| `"sin(log(1/x, arcsin(x)))".ToEntity().Limit("x", 0, ApproachFrom.Right)` | `limitright(sin(log(1 / x, arcsin(x))), x, 0)` | `sin(-1)` |
| `"sinh(log(1/x, arcsin(x)))".ToEntity().Limit("x", 0, ApproachFrom.Right)` | `limitright((e ^ log(1 / x, arcsin(x)) - e ^ (-log(1 / x, arcsin(x)))) / 2, x, 0)` | `(1 / e - e) / 2` |
| `"tanh(log(x, arcsin(x)))".ToEntity().Limit("x", 0, ApproachFrom.Left)` | `limitleft((e ^ (2 * log(x, arcsin(x))) - 1) / (e ^ (2 * log(x, arcsin(x))) + 1), x, 0)` | `(e ^ 2 - 1) / (e ^ 2 + 1)` |

### A function of a quotient tending to 0/0 has the value at the quotient's limit

**Silent: a different answer.** A function of a quotient took the quotient's limit from the descent alone, which reads
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,20 @@ partial record Divf
{
internal override Entity? ComputeLimitDivideEtImpera(Variable x, Entity dist, ApproachFrom side)
{
if (ComputeLimitImpl(this, x, dist, side) is { } lim)
// A form with no value -- 0 * +oo / 0, which is what -x / (sqrt(1 - x^2) * arcsin(x)) at 0
// from the right comes back as -- is the claim that there is no limit, and the parts may
// say otherwise. It is kept for the end, where it was answered before, and the parts are
// read first.
var whole = ComputeLimitImpl(this, x, dist, side);
if (whole is { } lim && lim.Evaled != MathS.NaN)
return lim;
else
{

var dividendLimit = Dividend.ComputeLimitDivideEtImpera(x, dist, side);
var divisorLimit = Divisor.ComputeLimitDivideEtImpera(x, dist, side);
var (dividend, divisor) =
(Dividend.ComputeLimitDivideEtImpera(x, dist, side), Divisor.ComputeLimitDivideEtImpera(x, dist, side)) switch
(dividendLimit, divisorLimit) switch
{
({ } lim1, { } lim2) when IsDeterminate(New(lim1, lim2), x) => (lim1, lim2),
({ } lim1, { } lim2) when lim1.InnerSimplified.IsFinite && lim2.InnerSimplified.IsFinite && lim2.InnerSimplified != 0 => (lim1, lim2),
Expand Down Expand Up @@ -130,11 +137,19 @@ partial record Divf
// written, sent a definite integral astray. Folded, it is 0, and ln(2) * 2^(1/+oo) is
// ln(2), not its decimal. Where it has no
// value, what was answered is kept.
if (substituted?.Evaled == MathS.NaN && IsAnIndeterminateQuotient(dividend, divisor)
&& AsTheWholeLimit(this, x, dist, side) is { } whole && whole.Evaled is Number { IsNaN: false } value)
return whole.InnerSimplified is var folded && folded.Nodes.Any(static node => node is Number { IsFinite: false })
// Two infinite parts are not put in place of the parts above, so the substitution is not
// of the parts' limits, and the form is read off the parts' own limits instead:
// exp(ln(arcsin(x)) / ln(1/x)) at 0 from the right, whose exponent is -oo / +oo and
// tends to -1, was left unevaluated, and so were a sine of it and, after ten seconds, a
// hyperbolic sine.
if ((substituted?.Evaled == MathS.NaN && IsAnIndeterminateQuotient(dividend, divisor)
|| (substituted is null || substituted.Evaled == MathS.NaN)
&& dividendLimit is { } partLimit1 && divisorLimit is { } partLimit2
&& IsAnIndeterminateQuotient(partLimit1, partLimit2))
&& AsTheWholeLimit(this, x, dist, side) is { } asked && asked.Evaled is Number { IsNaN: false } value)
return asked.InnerSimplified is var folded && folded.Nodes.Any(static node => node is Number { IsFinite: false })
? value : folded;
return substituted;
return whole ?? substituted;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// 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>exp(log(1/x, arcsin(x)))</c> at 0 from the right was left unevaluated, where the logarithm alone is -1 and
/// the limit is <c>1/e</c>. The logarithm reaches the descent as <c>ln(arcsin(x)) / ln(1/x)</c>, a quotient of
/// two infinities, and the limit its derivatives' quotient was taken to came back as the form <c>0 * +oo / 0</c>,
/// which has no value; the descent handed that on as the exponent's limit rather than asking the quotient's whole
/// limit, which it asked only for a 0/0 read off two finite parts. A sine of it was left unevaluated too, and a
/// hyperbolic sine after ten seconds. The same on 2.6.0. Found by limitcheck at <c>DEPTH=3</c>, <c>SEED=40</c>;
/// no issue was filed.
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AFunctionOfAQuotientOfInfinitiesTest
{
[Theory]
[InlineData("e^log(1/x, arcsin(x))", "1/e")]
[InlineData("sin(log(1/x, arcsin(x)))", "sin(-1)")]
[InlineData("sinh(log(1/x, arcsin(x)))", "(1/e - e)/2")]
[InlineData("log(1/x, arcsin(x))", "-1")]
public void IsTheLimitFromTheRight(string input, string expected)
=> Assert.Equal(MathS.FromString(expected).Simplify(),
MathS.FromString(input).Limit("x", 0, ApproachFrom.Right).Simplify());

// ln(arcsin(x)) / ln(x) tends to 1 from the left as well, and was left unevaluated there after six seconds.
[Fact]
public void IsTheLimitFromTheLeft()
=> Assert.Equal(MathS.FromString("tanh(1)").Simplify(),
MathS.FromString("tanh(log(x, arcsin(x)))").Limit("x", 0, ApproachFrom.Left).Simplify());

// The two-sided convention is kept: ln(x) has no real values to the left of 0.
[Theory]
[InlineData("x * ln(x)")]
[InlineData("sin(x) * ln(x)")]
public void HasNoTwoSidedLimit(string input)
=> Assert.Equal(MathS.NaN, MathS.FromString(input).Limit("x", 0));
}
}
Loading