From 764400de0da618af94f89bcb5ffdef8a01f99ab0 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 3 Oct 2026 16:51:20 +0000 Subject: [PATCH] The third case of a binomial differential is written back for a negative x too Chebyshev's third case is integrated under x = 1/y, and its root, whose q-th power is b + a/x^n, was written back as (b + a/x^n)^(1/q): that root for a positive x, and for a negative one only under an odd root. 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. It is written back as (a + b x^n)^(1/q)/x^(n/q) now, which has the same q-th power everywhere. Part of #718. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 17 ++++++++++ .../Integration/IndefiniteIntegralSolver.cs | 33 ++++++++++++------- .../Calculus/BinomialDifferentialTest.cs | 31 +++++++++++++++++ 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index ed52626dd..e939e1a87 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -411,6 +411,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 a268e555e..e46e5ab8a 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.