diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 7a602ae02..5ea70d34e 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -921,6 +921,22 @@ conjugate as `0 - 1i`, in decimals, on the one instance that every `i` is, in ev |---|---|---| | `Complex.ImaginaryOne.Conjugate`, after `(1 / Complex.ImaginaryOne).Evaled` with `DowncastingEnabled` off | `0 - 1i` | `-i` | +### `arctan` and `arccot` are undefined at their poles + +**Silent: a simplification that cancels an `arctan` or an `arccot` carries `not 1 + x ^ 2 = 0`, where it carried +nothing.** Both have poles at `i` and `-i`, where they evaluate to an infinity, as `ln(0)` does, and the tangent's +condition excludes its own poles the same way. Their condition said they were defined everywhere, so +`arctan(x) - arctan(x)` simplified to `0`, which has a value at `i`. An antiderivative that keeps an `arctan` can now +list its poles beside the other points it has no value at, which says nothing new +([#2256](https://github.com/asc-community/AngouriMath/issues/2256)). + +| Input | Was (2.6.0) | Now | +|---|---|---| +| `"arctan(x) - arctan(x)".ToEntity().Simplify()` | `0` | `0 provided not 1 + x ^ 2 = 0` | +| `"arccot(x) - arccot(x)".ToEntity().Simplify()` | `0` | `0 provided not 1 + x ^ 2 = 0` | +| `"arctan(x)".ToEntity().DomainCondition` | `True` | `not 1 + x ^ 2 = 0` | +| `"x^2*coth(a + 2*ln(x))^2".ToEntity().Integrate("x").Simplify()` | `… + C + x ^ 3 / 3` | `… + C + x ^ 3 / 3 provided not x ^ 2 / e ^ (-a) + 1 = 0 and not x ^ 2 / (-e ^ (-a)) + 1 = 0` | + ### `max` and `min` are defined where their arguments are real **Silent: `max(x, x)` and `min(x, x)` simplify to `x provided x in RR`, where they were `x`.** The complex numbers are diff --git a/Sources/.editorconfig b/Sources/.editorconfig index d3925edc9..ac7671178 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -235,6 +235,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [AngouriMath/Core/Budgets/*.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/Convenience/ArctangentPolesTest.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/Algebra/SolveTest/ModulusEquationTest.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/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs index d9b98842a..3fb18a0b9 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs @@ -1479,16 +1479,26 @@ private static bool IsAnEvenRootOfASymbol(Entity expr) /// and the second is the constant-numerator case above. /// /// + /// /// With a a symbol the constant-numerator case is a piecewise with an arm for /// a = 0, and on that arm the multiple of the derivative, which divided by /// a, is not the integrand's: there it is (px + q)/(bx + c), which is /// px/b + (q - pc/b) ln(bx + c)/b. Timofeev's (b1 + c1 x)/(a + 2b x + c x^2) /// had no antiderivative, with the arm declined for the symbol in front. + /// + /// + /// A numerator that is a multiple of the derivative leaves no constant part, and the + /// arctangent it would have is left out rather than multiplied by zero, as the higher + /// powers leave it out: 0 * arctan(2x/2) is 0 only where the arctangent has + /// a value, so x/(x^2 + 1) was answered with provided not 1 + (2x/2)^2 = 0 + /// once the arctangent's poles were in its condition, beside a logarithm that has none + /// there either. https://github.com/asc-community/AngouriMath/issues/2256 + /// /// private static Entity IntegrateLinearOverQuadratic( Entity p, Entity q, Entity a, Entity b, Entity c, Entity denominator, Entity.Variable x) => WithTheArmForAZeroLeadingCoefficient( - IntegrateRationalQuadratic(q - p * b / (2 * a), a, b, c, x), + TreeAnalyzer.IsZero(q - p * b / (2 * a)) ? Entity.Number.Integer.Zero : IntegrateRationalQuadratic(q - p * b / (2 * a), a, b, c, x), a, p / (2 * a) * AntiderivativeLog(denominator), LinearOverPowerOfLinear(p, q, b, c, 1, x)); diff --git a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Trigonometry.Classes.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Trigonometry.Classes.cs index 5f8d48127..6395a3129 100644 --- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Trigonometry.Classes.cs +++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Trigonometry.Classes.cs @@ -228,7 +228,11 @@ Set.Interval interval when IntervalArithmetic.MonotoneOn(interval, -1, 1, increa public partial record Arctanf { - private protected override Entity IntrinsicCondition => Boolean.True; + // arctan(z) = (i/2)(ln(1 - iz) - ln(1 + iz)) has poles where 1 + z^2 = 0, at z = i and z = -i, where it + // evaluates to an infinity, as ln(0) does: undefined there, as the tangent is where its cosine is 0. + // The condition said True, and arctan(x) - arctan(x) simplified to 0, which has a value at i. + // https://github.com/asc-community/AngouriMath/issues/2256 + private protected override Entity IntrinsicCondition => !(1 + Argument.Pow(2)).EqualTo(0); /// protected override Entity InnerSimplify(bool isExact) => @@ -254,7 +258,9 @@ Set.Interval interval when IntervalArithmetic.Monotone(interval, increasing: tru public partial record Arccotanf { - private protected override Entity IntrinsicCondition => Boolean.True; + // arccot(z) = arctan(1/z), so its poles are arctan's, z = i and z = -i. At 0 it is pi/2. + // https://github.com/asc-community/AngouriMath/issues/2256 + private protected override Entity IntrinsicCondition => !(1 + Argument.Pow(2)).EqualTo(0); /// protected override Entity InnerSimplify(bool isExact) => diff --git a/Sources/Tests/UnitTests/Calculus/AConstantExponentialBesideAFoldedLogarithmIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AConstantExponentialBesideAFoldedLogarithmIntegralTest.cs index 239ff0f28..30a39ff35 100644 --- a/Sources/Tests/UnitTests/Calculus/AConstantExponentialBesideAFoldedLogarithmIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/AConstantExponentialBesideAFoldedLogarithmIntegralTest.cs @@ -74,7 +74,10 @@ private static void DifferentiatesBack(string integrand, int longest) /// /// An even power of x, where the factorisation reads a symbol and splits the /// biquadratic over it into arms on its sign -- coth(a + 2 ln(x))/x^2 is 3,577 - /// characters in the symbol and 413 as the exponential, which is kept. + /// characters in the symbol and 413 as the exponential, which is kept. The bound was 1,300 + /// until the arctangent's poles joined the points the answer lists as having no value: + /// x^2 coth(a + 2 ln(x))^2 keeps two arctangents and went from 1,128 characters to + /// 1,382 with their two conditions. https://github.com/asc-community/AngouriMath/issues/2256 /// [Theory] [InlineData("coth(a + 2*ln(x))/x^2")] @@ -82,6 +85,6 @@ private static void DifferentiatesBack(string integrand, int longest) [InlineData("coth(a + 2*ln(x))^2")] [InlineData("x^2*coth(a + 2*ln(x))^2")] [InlineData("tanh(a + 2*ln(x))^2")] - public void AnEvenPowerOfX(string integrand) => DifferentiatesBack(integrand, 1300); + public void AnEvenPowerOfX(string integrand) => DifferentiatesBack(integrand, 1400); } } diff --git a/Sources/Tests/UnitTests/Convenience/ArctangentPolesTest.cs b/Sources/Tests/UnitTests/Convenience/ArctangentPolesTest.cs new file mode 100644 index 000000000..57af0d08f --- /dev/null +++ b/Sources/Tests/UnitTests/Convenience/ArctangentPolesTest.cs @@ -0,0 +1,51 @@ +// +// 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; +using AngouriMath.Extensions; +using Xunit; + +namespace AngouriMath.Tests.Convenience +{ + /// + /// arctan and arccot are undefined at their poles, i and -i, where they evaluate to an + /// infinity, as ln(0) does and as the tangent is where its cosine is 0. Their condition said True, and + /// arctan(x) - arctan(x) simplified to 0, which has a value at i. + /// #2256 + /// + [Trait("Area", "Convenience")] + public sealed class ArctangentPolesTest + { + [Theory] + [InlineData("arctan(x)", "i", false)] + [InlineData("arctan(x)", "-i", false)] + [InlineData("arctan(x)", "2", true)] + [InlineData("arctan(x)", "0", true)] + [InlineData("arctan(x)", "1/2 + 6/5 * i", true)] + [InlineData("arccot(x)", "i", false)] + [InlineData("arccot(x)", "-i", false)] + [InlineData("arccot(x)", "0", true)] + [InlineData("arccot(x)", "-3", true)] + public void ItIsDefinedOffItsPoles(string expression, string at, bool defined) + { + var condition = expression.ToEntity().DomainCondition.Substitute("x", at.ToEntity()).Evaled; + Assert.Equal(defined ? Entity.Boolean.True : Entity.Boolean.False, condition); + var value = expression.ToEntity().Substitute("x", at.ToEntity()).Evaled; + Assert.Equal(defined, value is Entity.Number number && number.IsFinite); + } + + [Theory] + [InlineData("arctan(x) - arctan(x)")] + [InlineData("arccot(x) - arccot(x)")] + public void ASimplificationHasNoValueAtAPole(string expression) + { + var simplified = expression.ToEntity().Simplify(); + Assert.True(simplified.Substitute("x", "i".ToEntity()).Evaled.IsNaN, $"{simplified} at i"); + Assert.Equal(Entity.Number.Integer.Zero, simplified.Substitute("x", 2).Evaled); + } + } +}