diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 21b3a5303..cf457fab5 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -426,6 +426,23 @@ antiderivative on each side of it, as Rubi's is
| `"1/((2 + 3*x^2)^(1/4)*(4 + 3*x^2))".ToEntity().Integrate("x")` | `integral(...)` | the same in `(2 + 3 x^2)^(1/4)` |
| `"1/((-2 + 3*x^2)*(-1 + 3*x^2)^(1/4))".ToEntity().Integrate("x")` | `integral(...)` | `-(arctan(u) + artanh(u))/(2 sqrt(6))`, `u = sqrt(3) x/(sqrt(2) (-1 + 3 x^2)^(1/4))` |
+### The third case of a binomial differential is right for a negative `x` too
+
+**Answers where there were none.** Chebyshev's third case, `x^m (a + b x^n)^(p/q)` with
+`(m + 1)/n + p/q` whole, is integrated under `x = 1/y`, and came in after 2.5.0, which declined these.
+On the unreleased master it wrote its root back as `(b + a/x^n)^(1/q)`, which is that root for a
+positive `x`, and for a negative one only when `q` is odd: `1/(1 + x^4)^(5/4)` came out as
+`1/(1 + 1/x^4)^(1/4)`, an even function, whose derivative is the integrand's negative for every
+negative `x`. The root is written
+back as `(a + b x^n)^(1/q)/x^(n/q)` now, which has the same `q`-th power everywhere, and the answers
+are right on both sides of zero ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"1/(1 + x^4)^(5/4)".ToEntity().Integrate("x")` | `integral(...)` | `1 / ((1 + x ^ 4) ^ (1/4) / x)`, which is `x/(1 + x^4)^(1/4)` |
+| `"x^2/(1 + x^4)^(3/4)".ToEntity().Integrate("x")` | `integral(...)` | logarithms and an arctangent of `(1 + x^4)^(1/4)/x` |
+| `"x^6*(3 + 4*x^4)^(1/4)".ToEntity().Integrate("x")` | `integral(...)` | the same in `(3 + 4 x^4)^(1/4)/x` |
+
### A rational function of the sine and cosine with symbols in it is integrated by the half angle
`sin(x)^2/(a + b cos(x))` was left unevaluated while `sin(x)^2/(2 + 3 cos(x))` was answered. Under
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 73456d7c7..68b20e740 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -4470,7 +4470,13 @@ Entity ConstantNegative()
/// x = 1/y turns x^m (a + b x^n)^(p/q) dx into
/// -y^m' (b + a y^n)^(p/q) dy with m' = -m - 2 - n p/q, a whole number, and
/// (m' + 1)/n = -(s + p/q), whole — so it is the second case in y, with the
- /// roles of a and b exchanged, and y = 1/x put back afterwards.
+ /// roles of a and b exchanged. Its u, whose q-th power is
+ /// b + a/x^n, is put back as (a + b x^n)^(1/q)/x^(n/q), which has that power
+ /// at every x. (b + a/x^n)^(1/q) is the same number for a positive x,
+ /// and for a negative one only under an odd root, which is real here:
+ /// 1/(1 + x^4)^(5/4) came out as 1/(1 + 1/x^4)^(1/4), an even function
+ /// whose derivative is the integrand's negative for every negative x, where
+ /// x/(1 + x^4)^(1/4) is right on the whole line.
/// x^6 (3 + 4x^4)^(1/4) and (x^3 - 1)/(2 + x^3)^(1/3) are this. Chebyshev
/// proved there is no fourth case: outside these the integrand has no elementary
/// antiderivative at all, which is worth knowing before anyone goes looking.
@@ -4490,11 +4496,15 @@ Entity ConstantNegative()
if (!TryReadABinomialDifferential(expr, x, out var power, out var exponent,
out var inner, out var free, out var leading, out var factor))
return null;
+ var u = Variable.CreateUnique(expr, "u_binom");
+ var bracket = (Number.Rational.Create(free)
+ + Number.Rational.Create(leading) * MathS.Pow(x, Number.Integer.Create(inner))).InnerSimplified;
+ var root = MathS.Pow(bracket, Number.Rational.Create(EInteger.One, exponent.Denominator));
// The second case: s = (m + 1)/n whole.
if ((power + 1) % inner == 0)
return IntegrateABinomialDifferentialInTheSecondCase(
- power, inner, exponent, free, leading, x, factor);
+ power, inner, exponent, free, leading, u, root, factor);
// The third: s + p/q whole, taken to the second by x = 1/y.
var sPlusP = ERational.Create(power + 1, inner).Add(exponent);
@@ -4504,21 +4514,23 @@ Entity ConstantNegative()
if (!nTimesP.IsInteger() || !nTimesP.Numerator.CanFitInInt32())
return null;
var reflectedPower = -power - 2 - nTimesP.ToLowestTerms().Numerator.ToInt32Unchecked();
- var y = Variable.CreateUnique(expr, "y_binom");
+ var back = root / MathS.Pow(x, Number.Rational.Create(ERational.Create(EInteger.FromInt32(inner), exponent.Denominator)));
if (IntegrateABinomialDifferentialInTheSecondCase(
- reflectedPower, inner, exponent, leading, free, y, Number.Integer.MinusOne) is not { } inY)
+ reflectedPower, inner, exponent, leading, free, u, back, Number.Integer.MinusOne) is not { } reflected)
return null;
- return (factor * inY.Substitute(y, 1 / x)).InnerSimplified;
+ return (factor * reflected).InnerSimplified;
}
///
- /// int factor * v^m (a + b v^n)^(p/q) dv with (m + 1)/n whole: a polynomial
- /// in u = (a + b v^n)^(1/q) expanded term by term for s >= 1, and a rational
- /// function of u handed to the rational integrator for s <= 0.
+ /// int factor * v^m (a + b v^n)^(p/q) dv with (m + 1)/n whole, in
+ /// and then written as , anything whose
+ /// q-th power is a + b v^n: a polynomial in u expanded term by term for
+ /// s >= 1, and a rational function of u handed to the rational integrator for
+ /// s <= 0.
///
private static Entity? IntegrateABinomialDifferentialInTheSecondCase(
int power, int inner, ERational exponent, ERational free, ERational leading,
- Entity.Variable v, Entity factor)
+ Entity.Variable u, Entity back, Entity factor)
{
if ((power + 1) % inner != 0)
return null;
@@ -4527,11 +4539,8 @@ Entity ConstantNegative()
var p = exponent.Numerator.ToInt32Checked();
var a = Number.Rational.Create(free);
var b = Number.Rational.Create(leading);
- var u = Variable.CreateUnique(v + factor, "u_binom");
var outside = Number.Integer.Create(q)
/ (Number.Integer.Create(inner) * MathS.Pow(b, Number.Integer.Create(s)));
- var bracket = (a + b * MathS.Pow(v, Number.Integer.Create(inner))).InnerSimplified;
- var back = MathS.Pow(bracket, Number.Rational.Create(EInteger.One, exponent.Denominator));
if (s < 1)
{
diff --git a/Sources/Tests/UnitTests/Calculus/BinomialDifferentialTest.cs b/Sources/Tests/UnitTests/Calculus/BinomialDifferentialTest.cs
index e4ba4dac3..bc52edaf7 100644
--- a/Sources/Tests/UnitTests/Calculus/BinomialDifferentialTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/BinomialDifferentialTest.cs
@@ -130,6 +130,37 @@ public void TheMonomialCaseIsUnchangedInSubstance(string integrand)
[InlineData("x^2*(1 + x^2)^(-3/2)")]
public void TheThirdCaseThroughTheReciprocal(string integrand) => DifferentiatesBack(integrand);
+ ///
+ /// The third case's u has b + a/x^n for its q-th power, and written
+ /// as (b + a/x^n)^(1/q) it is that root for a positive x, and for a negative
+ /// one only where q is odd: 1/(1 + x^4)^(5/4) came out as
+ /// 1/(1 + 1/x^4)^(1/4), which is even, and its derivative was the integrand's
+ /// negative for every negative x. Written as (a + b x^n)^(1/q)/x^(n/q) it is
+ /// right on both sides of zero under either root, and each of these is real on both.
+ ///
+ [Theory]
+ [InlineData("1/(1 + x^4)^(5/4)")]
+ [InlineData("x^2/(1 + x^4)^(3/4)")]
+ [InlineData("x^6*(3 + 4*x^4)^(1/4)")]
+ [InlineData("x^3/(2 + x^3)^(1/3)")]
+ [InlineData("1/(2 + x^3)^(1/3)")]
+ [InlineData("(x^3 - 1)/(2 + x^3)^(1/3)")]
+ public void TheThirdCaseOnBothSidesOfZero(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+ var derivative = integral.Substitute("C", 0).Differentiate("x");
+ var original = integrand.ToEntity();
+ foreach (var at in new[] { -1.1, -0.7, -0.35, 0.35, 0.83, 1.4 })
+ {
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ var want = original.Substitute("x", at).EvalNumerical();
+ Assert.True(Math.Abs((double)want.ImaginaryPart) < 1e-12, $"the integrand at x = {at} is {want}, not real");
+ Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
+ $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
+ }
+ }
+
///
/// Outside all three of Chebyshev's cases there is — and this is the part worth knowing
/// before anyone goes looking — no elementary antiderivative at all, which he proved.