From d8c79cd2af78a7a6ae9003a2498228fb6bf1d8bd Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Tue, 1 Sep 2026 10:18:19 +0000 Subject: [PATCH] Pin the limits #231 asked for, including the one still unticked All three cases the issue lists answer on master, `lim e^x - x` among them, whose box is still unchecked. Measured rather than read: the issue is old, and this workspace has had recorded verdicts turn out stale seven times. Pinned as tests because an issue's list is not one. Closing #231 on the strength of three passing examples would leave nothing that fails if a limit stops working, and a limit solver is a search over several methods where which one answers depends on the shape -- so three cases say almost nothing about the capability. Hence the wider sweep: the two remarkable limits with their constants moved, so that a rule is exercised rather than a value recognised; an indeterminate power; a difference of infinities; polynomial against exponential; a root cancelling a linear term; an oscillation damped by its argument. Twenty in all. `x * ln(x)` and `x^x` at zero answer NaN two-sided and answer correctly one side at a time. That is a decision rather than a gap -- `ln(x)` is not real to the left of zero, so there is no two-sided limit to report -- and it is pinned in both directions, so that "does not answer" and "answers correctly" cannot later be mistaken for one another. Part of #231. --- Sources/.editorconfig | 3 + .../Calculus/LimitsTheIssueListedTest.cs | 108 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 Sources/Tests/UnitTests/Calculus/LimitsTheIssueListedTest.cs diff --git a/Sources/.editorconfig b/Sources/.editorconfig index c8722ca8a..bf67ac6e5 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -51,6 +51,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [AngouriMath/Functions/Algebra/Polynomials/KroneckerFactorization.cs] file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n +[Tests/UnitTests/Calculus/LimitsTheIssueListedTest.cs] +file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n + [AngouriMath/Functions/Algebra/Polynomials/FractionFreeDeterminant.cs] file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n diff --git a/Sources/Tests/UnitTests/Calculus/LimitsTheIssueListedTest.cs b/Sources/Tests/UnitTests/Calculus/LimitsTheIssueListedTest.cs new file mode 100644 index 000000000..9b1261ab2 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/LimitsTheIssueListedTest.cs @@ -0,0 +1,108 @@ +// +// 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 AngouriMath.Extensions; +using Xunit; + +namespace AngouriMath.Tests.Calculus +{ + /// + /// The limits #231 asked + /// for, and enough others to say the capability is there rather than the three examples. + /// + /// + /// + /// All three of the issue's cases answer, including lim e^x - x, which its checklist + /// still shows unticked. They are pinned here because an issue's list is not a test: closing + /// it leaves nothing that fails if one of them stops working. + /// + /// + /// The wider sweep is the point of the file. Three cases passing says almost nothing about a + /// limit solver — it is a search over several methods, and which one answers depends on the + /// shape. These cover the standard forms: the two remarkable limits, an indeterminate power, + /// a difference of infinities, polynomial against exponential, a root cancelling a linear + /// term, and an oscillation damped by its argument. + /// + /// + [Trait("Area", "Calculus")] + public sealed class LimitsTheIssueListedTest + { + private static Entity At(string expression, Entity destination) + => expression.ToEntity().Limit("x", destination).Simplify(); + + private static Entity Infinity => Entity.Number.Real.PositiveInfinity; + + /// The three the issue names. + [Fact] + public void TheSecondRemarkableLimitIsE() + => Assert.Equal("e".ToEntity(), At("(1 + 1/x)^x", Infinity)); + + [Fact] + public void TheFirstRemarkableLimitIsOne() + => Assert.Equal(Entity.Number.Integer.One, At("sin(x)/x", 0)); + + /// + /// The one still unticked on the issue. An exponential beats a linear term, so the + /// difference is unbounded rather than indeterminate. + /// + [Fact] + public void AnExponentialLessALinearTermIsUnbounded() + => Assert.Equal(Infinity, At("e^x - x", Infinity)); + + /// + /// The remarkable limits with the constants moved, which is what says a rule was applied + /// rather than a value recognised. + /// + [Theory] + [InlineData("(1 + 2/x)^x", "e ^ 2")] + [InlineData("(1 + 1/x)^(2*x)", "e ^ 2")] + [InlineData("ln(x)/x", "0")] + [InlineData("x^2 / e^x", "0")] + [InlineData("(x^2 + 1)/(2*x^2 - 3)", "1/2")] + [InlineData("sqrt(x^2 + x) - x", "1/2")] + [InlineData("sin(1/x) * x", "1")] + [InlineData("arctan(x)", "pi / 2")] + [InlineData("x - ln(x)", "+oo")] + [InlineData("e^x - x^2", "+oo")] + [InlineData("e^x / x^100", "+oo")] + [InlineData("(1 + 1/x)^(x^2)", "+oo")] + public void AtInfinity(string expression, string expected) + => Assert.Equal(expected.ToEntity().Simplify(), At(expression, Infinity)); + + [Theory] + [InlineData("tan(x)/x", "1")] + [InlineData("(1 - cos(x))/x^2", "1/2")] + [InlineData("(e^x - 1)/x", "1")] + [InlineData("ln(1 + x)/x", "1")] + [InlineData("(sin(x) - x)/x^3", "-1/6")] + [InlineData("(2^x - 1)/x", "ln(2)")] + public void AtZero(string expression, string expected) + => Assert.Equal(expected.ToEntity().Simplify(), At(expression, 0)); + + /// + /// x * ln(x) and x^x at zero are two-sided and + /// answer one side at a time, which is a decision rather than a gap: ln(x) is not + /// real to the left of zero, so there is no two-sided limit to report. Pinned in both + /// directions so that "it does not answer" and "it answers correctly" cannot be confused + /// for one another later. + /// + [Theory] + [InlineData("x * ln(x)", "0")] + [InlineData("x^x", "1")] + public void OneSidedWhereTwoSidedDoesNotExist(string expression, string expected) + { + Assert.Equal(MathS.NaN, At(expression, 0)); + Assert.Equal( + expected.ToEntity(), + expression.ToEntity().Limit("x", 0, ApproachFrom.Right).Simplify()); + Assert.Equal( + expected.ToEntity(), + expression.ToEntity().Limit("x", 0, ApproachFrom.Left).Simplify()); + } + } +}