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
16 changes: 16 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1479,16 +1479,26 @@ private static bool IsAnEvenRootOfASymbol(Entity expr)
/// and the second is the constant-numerator case above.
/// </summary>
/// <remarks>
/// <para>
/// With <c>a</c> a symbol the constant-numerator case is a piecewise with an arm for
/// <c>a = 0</c>, and on that arm the multiple of the derivative, which divided by
/// <c>a</c>, is not the integrand's: there it is <c>(px + q)/(bx + c)</c>, which is
/// <c>px/b + (q - pc/b) ln(bx + c)/b</c>. Timofeev's <c>(b1 + c1 x)/(a + 2b x + c x^2)</c>
/// had no antiderivative, with the arm declined for the symbol in front.
/// </para>
/// <para>
/// 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: <c>0 * arctan(2x/2)</c> is <c>0</c> only where the arctangent has
/// a value, so <c>x/(x^2 + 1)</c> was answered with <c>provided not 1 + (2x/2)^2 = 0</c>
/// 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
/// </para>
/// </remarks>
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <inheritdoc/>
protected override Entity InnerSimplify(bool isExact) =>
Expand All @@ -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);

/// <inheritdoc/>
protected override Entity InnerSimplify(bool isExact) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,17 @@ private static void DifferentiatesBack(string integrand, int longest)
/// <summary>
/// An even power of <c>x</c>, where the factorisation reads a symbol and splits the
/// biquadratic over it into arms on its sign -- <c>coth(a + 2 ln(x))/x^2</c> 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:
/// <c>x^2 coth(a + 2 ln(x))^2</c> keeps two arctangents and went from 1,128 characters to
/// 1,382 with their two conditions. https://github.com/asc-community/AngouriMath/issues/2256
/// </summary>
[Theory]
[InlineData("coth(a + 2*ln(x))/x^2")]
[InlineData("coth(a + 2*ln(x))^2/x^2")]
[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);
}
}
51 changes: 51 additions & 0 deletions Sources/Tests/UnitTests/Convenience/ArctangentPolesTest.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// <c>arctan</c> and <c>arccot</c> are undefined at their poles, <c>i</c> and <c>-i</c>, where they evaluate to an
/// infinity, as <c>ln(0)</c> does and as the tangent is where its cosine is 0. Their condition said <c>True</c>, and
/// <c>arctan(x) - arctan(x)</c> simplified to <c>0</c>, which has a value at <c>i</c>.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2256">#2256</a>
/// </summary>
[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);
}
}
}
Loading