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
13 changes: 13 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,19 @@ power, and the integrand is answered in a fraction of a second
| `"csch(c+d*x)*(a+b*sinh(c+d*x)^4)^2".ToEntity().Integrate("x")` | past fifteen minutes | 426 characters |
| `"csch(c+d*x)*(a+b*sinh(c+d*x)^4)^3".ToEntity().Integrate("x")` | past fifteen minutes | 646 characters |

### A power of a polynomial whose leading coefficient is zero spelled otherwise is not divided by it

**Answers where there were none.** `1/((a (1 + x^2))^2 - (a (1 - x^2))^2)^3` was left unevaluated. That is
`1/(64 a^6 x^6)`: the polynomial's fourth power cancels, and its leading coefficient as read, `a^2 - a^2` spelled
otherwise, was divided by to make it monic. The radical substitution writes this shape for an exponential of an
inverse hyperbolic cotangent ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"1/((a*(1+x^2))^2-(a*(1-x^2))^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 133 characters |
| `"x^2/((a*(1+x^2))^2-(a*(1-x^2))^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 235 characters |
| `"1/((-1/a^2+((1+x^2)/(a*(1-x^2)))^2)^2*(1-x^2)^2)".ToEntity().Integrate("x")` | `integral(...)` | 518 characters |

### An exponential of a whole multiple of an inverse hyperbolic tangent is written algebraically first

**Answers where there were none.** `e^(n artanh(u))` with u linear is written as powers of `1 + u` and `1 - u`, and then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2510,6 +2510,10 @@ Entity InU(Dictionary<EInteger, Entity> polynomial)
else if (factor is Powf(var polynomial, Number.Integer power) && power.EInteger.CompareTo(EInteger.FromInt32(2)) >= 0
&& TreeAnalyzer.TryGetPolynomial(polynomial, x, out var read) && read.Count > 1
&& read.Keys.Max() is { } top && read[top] is var leading && !leading.ContainsNode(x) && leading.Evaled is not Number
// A leading coefficient that is zero spelled otherwise, `-a^2 + a^2` in
// `(a (1 + u^2))^2 - (a (1 - u^2))^2`, is not one: divided by, every other
// coefficient was over zero, and the answer `NaN + C`.
&& Functions.PartialFractions.InLowestTermsOverTheSymbols(leading) is not Number.Complex { IsZero: true }
&& !numerator.Nodes.Concat(denominator.Nodes).Any(node =>
node is Powf(var radicand, Number.Rational r) && r is not Number.Integer && radicand.Nodes.Contains(polynomial)))
{
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 System;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// A power of a polynomial whose leading coefficient is zero spelled otherwise is not made monic by
/// dividing by it: <c>(a (1 + x^2))^2 - (a (1 - x^2))^2</c> is <c>4 a^2 x^2</c>, its fourth power
/// cancels, and over its cube the answer was <c>NaN + C</c>. The radical substitution writes it for
/// Rubi's 7.4.2 <c>1/(e^(arccoth(a x)) (c - a^2 c x^2)^2)</c>.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ALeadingCoefficientThatVanishesIntegralTest
{
[Theory]
[InlineData("1/((a*(1+x^2))^2-(a*(1-x^2))^2)^3")]
[InlineData("x^2/((a*(1+x^2))^2-(a*(1-x^2))^2)^2")]
[InlineData("1/((-1/a^2+((1+x^2)/(a*(1-x^2)))^2)^2*(1-x^2)^2)")]
public void IsNotANaN(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("NaN", integral.Stringize());
Assert.DoesNotContain("integral(", integral.Stringize());
var pinned = integral.Substitute("C", 0).Substitute("a", 1.3);
Entity original = integrand.ToEntity().Substitute("a", 1.3);
var derivative = pinned.Differentiate("x");
foreach (var at in new[] { -2.3, 0.4, 1.9 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
Assert.True(difference / scale < 1e-8,
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}
}
}
Loading