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);
+ }
+ }
+}