From d897d3361f4334a084f68151a2db13b980174b2e Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 10 Oct 2026 10:05:44 +0000 Subject: [PATCH] A power of x past the twelfth beside a block is split off Part of #718. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 12 +++++ .../Integration/IndefiniteIntegralSolver.cs | 14 ++++-- .../AHighPowerOfXBesideABlockIntegralTest.cs | 45 +++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 Sources/Tests/UnitTests/Calculus/AHighPowerOfXBesideABlockIntegralTest.cs diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index a6f0a85da..1d9a1ab9e 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -205,6 +205,18 @@ partial fractions divide. It is taken out in front of the integral first | `"(b*d + 2*c*d*x)^9/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute | 76,249 characters, the length the content-free one already had | | `"(b*d + 2*c*d*x)^8/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute | 65,236 characters | +### A power of x past the twelfth beside a block is split off + +**Answers where there were none.** `(c + d x^3 + g x^6 + f x^9)/(x^14 (a + b x^3))` was declined, and so were +`x^13` to `x^17` beside `(a + b x^3)`, its square and its cube, Rubi's 1.1.3.8. The split that writes `N/(x^k B)` as +`P/x^k + R/B`, with `P` the first `k` terms of the series of `N/B` at 0, took `k` up to 12. It takes `k` up to 24 +now, where the series costs one term per power +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"(c + d*x^3 + g*x^6 + f*x^9)/(x^14*(a + b*x^3))".ToEntity().Integrate("x")` | `integral(...)` | the antiderivative, in powers of x and the logarithms and arctangent of `a + b x^3`'s factors | + ### A symbolic quadratic with a square discriminant is split into its linears before the division **Answers where there were none.** `(d + e x)^8/(a d e + (c d^2 + a e^2) x + c d e x^2)^2` ran past a minute, with diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index a825dce58..192346240 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -1241,7 +1241,7 @@ private static (Entity InY, Entity.Variable Y, Entity Shift)? InTheVariableThatD pastTheSecondDegree |= TreeAnalyzer.TryGetPolynomial(written, x, out var terms) && terms.Keys.Any(power => power.CompareTo(EInteger.FromInt32(2)) > 0); } } - if (k == 0 || k > MaximumPowerOfXBesideABlock || !pastTheSecondDegree || !block.ContainsNode(x) || !block.Vars.Any(symbol => symbol != x)) + if (k == 0 || k > MaximumPowerOfXOverABlock || !pastTheSecondDegree || !block.ContainsNode(x) || !block.Vars.Any(symbol => symbol != x)) return null; if (!TreeAnalyzer.TryGetPolynomial(block, x, out var below) || !TreeAnalyzer.TryGetPolynomial(numerator, x, out var above)) return null; @@ -1577,11 +1577,19 @@ Entity InU(Dictionary polynomial) } /// - /// The largest power of x, and the largest degree of the block beside it, that - /// takes: Rubi's suite goes to twelve. + /// The largest degree of a block beside a power of x that + /// takes; also the largest power of a + /// linear beside a block, and of x in blocks of . /// private const int MaximumPowerOfXBesideABlock = 12; + /// + /// The largest power of x that takes. The + /// series it writes has one term per power of x, so the cost grows linearly in it. Rubi's + /// suite asks for up to x^17, beside (a + b x^3)^3. + /// + private const int MaximumPowerOfXOverABlock = 24; + /// /// A polynomial over a power of a binomial, P(x)/(a + b x^n)^k with n >= 3 /// and k >= 2, a power at a time down to P'(x)/(a + b x^n), which diff --git a/Sources/Tests/UnitTests/Calculus/AHighPowerOfXBesideABlockIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AHighPowerOfXBesideABlockIntegralTest.cs new file mode 100644 index 000000000..b1bd235e3 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AHighPowerOfXBesideABlockIntegralTest.cs @@ -0,0 +1,45 @@ +// +// 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 twelfth beside a block with symbols in it is split off as the first + /// terms of the block's series at 0: Rubi's 1.1.3.8 asks for up to x^17 beside + /// (a + b x^3)^3. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class AHighPowerOfXBesideABlockIntegralTest + { + [Theory] + [InlineData("(c + d*x^3 + g*x^6 + f*x^9)/(x^14*(a + b*x^3))")] + [InlineData("(c + d*x^3 + g*x^6 + f*x^9)/(x^17*(a + b*x^3))")] + [InlineData("(c + d*x^3 + g*x^6 + f*x^9)/(x^14*(a + b*x^3)^2)")] + public void DifferentiatesBack(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + Entity Pinned(Entity e) => e.Substitute("a", 2).Substitute("b", 3).Substitute("c", 5) + .Substitute("d", -1).Substitute("g", 7).Substitute("f", 1); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + foreach (var at in new[] { -1.7, -0.6, 0.45, 1.3 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + var got = derivative.Substitute("x", at).EvalNumerical(); + 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}"); + } + } + } +}