From 4d82266c034e035c6b33bc5b75983cbbe061dacc Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Wed, 21 Oct 2026 23:32:16 +0000 Subject: [PATCH] Leave a power of x past the second beside a high power of a binomial in x^2 to the binomial rules SolveARationalFunctionOverAPowerOfASymbolicQuadratic already declined an odd integrand beside a power of x and a binomial in x^2 to the sixth power or past it, leaving it to the substitution in x^2. An even one is the binomial rules' x^m (a + b x^2)^p, and here it was peeled: Rubi's 1.1.2.2 1/(x^4 (a + b x^2)^10) took the power of x off four times, over ten levels of the recurrence each, and came back in 75,607 characters with its like terms never gathered. Past the second power of x it now declines, and the binomial rules write 8,559; 1/(x^6 (a + b x^2)^8) goes from 33,638 to 6,063. Beside x^2 the binomial rules are shorter at four times the cost, 1/(x^2 (a + b x^2)^10) in 8,322 characters and 0.49 GB against 15,658 and 0.10, and that is left as it was. Chain against master 78cd31bc, before the narrowing to past x^2: pocket of 7,790 rows 7,788 -> 7,789 solved, 0 wrong; every family's sample unchanged; the one move, 1.1.2.2:236, partial -> solved; of 13,328 rows compared for allocation one grew past 1.3x and 300 MB, 1.1.2.2:235, the x^2 trade above, which the narrowing returns to master's answer. Part of #233. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 13 +++++ .../Integration/IndefiniteIntegralSolver.cs | 17 ++++--- ...hPowerOfABinomialInXSquaredIntegralTest.cs | 47 +++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 Sources/Tests/UnitTests/Calculus/APowerOfXBesideAHighPowerOfABinomialInXSquaredIntegralTest.cs diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 76e155998..bef31914d 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -2146,6 +2146,19 @@ is written in closed form. A quadratic that factors over linears is left to the | `"(f + g*x)/((d + k*x)^2*(a + b*x + c*x^2)^3)".ToEntity().Integrate("x")` | `integral(...)` | 21,943 characters | | `"(A + B*x + C*x^2)/((d + k*x)^3*(a + c*x^2)^3)".ToEntity().Integrate("x")` | `integral(...)` | 25,201 characters | +### A power of x beside a high power of a binomial in x^2 is left to the binomial rules + +**Shorter answers.** The rule above took a power of x off `1/(x^4 (a + b x^2)^10)`, Rubi's 1.1.2.2, four times, over ten +levels of its recurrence each, and the answer's like terms were never gathered. Beside a power of x past the second +and a binomial in `x^2` to the sixth power or past it, it now declines, as it already did where the integrand is odd, +and the rules for `x^m (a + b x^2)^p` answer it. Beside `x^2` the two are a trade, shorter there at four times the +cost, and it is left as it was ([#233](https://github.com/asc-community/AngouriMath/issues/233)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"1/(x^4*(a+b*x^2)^10)".ToEntity().Integrate("x")` | `integral(...)`; 75,607 characters on the unreleased master | 8,559 characters | +| `"1/(x^6*(a+b*x^2)^8)".ToEntity().Integrate("x")` | `integral(...)`; 33,638 characters on the unreleased master | 6,063 characters | + ### An odd power of a quadratic's derivative over a power of the quadratic is integrated in the quadratic **Shorter answers, and answers where there were none.** `(b + 2c x)^9/(a + b x + c x^2)^3` was answered through the diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 1ad367eb0..67278fb88 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -25984,14 +25984,19 @@ Entity TakeOut(Entity node) // a second and ten levels of this ran past the budget. if (Functions.PartialFractions.HoldsTheImaginaryUnit(quadratic) || linear is null && VanishesIdentically(a1)) return null; - // Nor beside a power of x where the integrand is odd and the power of the quadratic high: - // it is then x times a function of x^2, the substitution's under u = x^2, and peeling - // fifteen powers of x off `(b + 2 c x^2)/(x^15 (b + c x^2)^8)` here ran past the budget, - // where in u it is two linears. An even one, and a low power, are left here, where they - // were answered. + // Nor beside a power of x where the power of the quadratic is high. Odd, the integrand is + // x times a function of x^2, the substitution's under u = x^2, and peeling fifteen powers + // of x off `(b + 2 c x^2)/(x^15 (b + c x^2)^8)` here ran past the budget, where in u it is + // two linears. Even, and peeled past the second power of x, it is the binomial rules' + // `x^m (a + b x^2)^p`: peeled here four times over ten levels of the recurrence, Rubi's + // 1.1.2.2 `1/(x^4 (a + b x^2)^10)` came back in 75,607 characters, its like terms never + // gathered, and the binomial rules write 8,559. Beside `x^2` the two are a trade, shorter + // there at four times the cost, and it stays here. A low power is left here, where it was + // answered. if (linear is not null && power >= 6 && VanishesIdentically(a1) && TreeAnalyzer.TryGetPolyLinear(linear, x, out _, out var interceptOfTheLinear) && VanishesIdentically(interceptOfTheLinear) - && top.Keys.All(degree => degree.IsEven != (linearPower % 2 == 0))) + && (top.Keys.All(degree => degree.IsEven != (linearPower % 2 == 0)) + || linearPower >= 3 && top.Keys.All(degree => degree.IsEven == (linearPower % 2 == 0)))) return null; // A quadratic that factors over linears is the partial fractions' to take apart, and // they do it cheaply: `(d + e x)^10/((d + e x) (a e + c d x))^4`, written out, went from diff --git a/Sources/Tests/UnitTests/Calculus/APowerOfXBesideAHighPowerOfABinomialInXSquaredIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APowerOfXBesideAHighPowerOfABinomialInXSquaredIntegralTest.cs new file mode 100644 index 000000000..86252ca63 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/APowerOfXBesideAHighPowerOfABinomialInXSquaredIntegralTest.cs @@ -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 +{ + /// + /// A power of x past the second beside a high power of a binomial in x^2 is the binomial rules', even or odd: + /// the reduction over a power of a symbolic quadratic took the power of x off Rubi's 1.1.2.2 + /// 1/(x^4 (a + b x^2)^10) four times over ten levels each, and came back in 75,607 characters + /// with its like terms never gathered, where the binomial rules write 8,559. + /// #233 + /// + [Trait("Area", "Calculus")] + public sealed class APowerOfXBesideAHighPowerOfABinomialInXSquaredIntegralTest + { + [Theory] + [InlineData("1/(x^4*(a+b*x^2)^10)", 12000)] + [InlineData("1/(x^6*(a+b*x^2)^8)", 9000)] + public void IsAnsweredInFewCharacters(string integrand, int characters) + { + var integral = integrand.ToEntity().Integrate("x"); + var text = integral.Stringize(); + Assert.DoesNotContain("integral(", text); + Assert.True(text.Length < characters, $"the antiderivative of {integrand} is {text.Length} characters"); + Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + foreach (var at in new[] { -1.7, -0.6, 0.4, 1.3, 2.2 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + var got = derivative.Substitute("x", at).EvalNumerical(); + Assert.False(got.IsNaN, $"d/dx of the antiderivative of {integrand} has no value at x = {at}, where the integrand is {want}"); + Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) + < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)), + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + } + } +}