From e14f0ee737c49b3b7951018d55d09dcb2758d211 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Wed, 14 Oct 2026 14:47:23 +0000 Subject: [PATCH 1/2] Split a symbolic power of a product beside a symbolic power of a factor (P Q)^r is K P^r Q^r with K constant between the zeros of P and Q for any exponent free of x, not only a rational one: K is exp(2 pi i n r) for a whole n. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Integration/IndefiniteIntegralSolver.cs | 12 +++- ...roductBesideAPowerOfAFactorIntegralTest.cs | 61 +++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfAProductBesideAPowerOfAFactorIntegralTest.cs diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 11ef427c4..599600d54 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -19236,7 +19236,8 @@ Entity TakeOut(Entity node) } /// - /// A root of a product beside a root of one of its factors, the product split: + /// A root of a product beside a root of one of its factors, or a symbolic power of one + /// beside a symbolic power of a factor, the product split: /// (P Q)^r is K P^r Q^r, where K = (P Q)^r/(P^r Q^r) is constant wherever /// it is defined, for the same reason as in /// . The powers of P @@ -19252,7 +19253,7 @@ Entity TakeOut(Entity node) { if (!Integration.AnsweringTheQuestionAsked || splittingARootOfAProduct) return null; - var roots = new List<(Entity Node, Entity Base, Number.Rational Exponent)>(); + var roots = new List<(Entity Node, Entity Base, Entity Exponent)>(); Gather(expr); if (roots.Count < 2) return null; @@ -19295,7 +19296,12 @@ void Gather(Entity node) Gather(above); Gather(below); break; - case Powf(var @base, Number.Rational exponent) when exponent is not Number.Integer && @base.ContainsNode(x): + // A symbolic exponent as well: K is exp(2 pi i n r) for a whole n constant + // between the zeros of P and Q, whatever r is. Rubi's 1.2.1.2:2389, + // `(d + e x)^(-3 - 2 p) ((d + e x) (a e + c d x))^p`, gathers into + // `(d + e x)^(-3 - p) (a e + c d x)^p`. + case Powf(var @base, var exponent) when exponent is not Number.Integer && !exponent.ContainsNode(x) && @base.ContainsNode(x) + && (exponent is Number.Rational || exponent.Vars.Any()): roots.Add((node, @base, exponent)); break; } diff --git a/Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfAProductBesideAPowerOfAFactorIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfAProductBesideAPowerOfAFactorIntegralTest.cs new file mode 100644 index 000000000..713c98e0b --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfAProductBesideAPowerOfAFactorIntegralTest.cs @@ -0,0 +1,61 @@ +// +// 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 symbolic power of a product of two linears beside a symbolic power of one of them: + /// (P Q)^p is a constant times P^p Q^p, and the powers of P gather. + /// Rubi's 1.2.1.2 (d + e x)^(-3 - 2 p) (a d e + (c d^2 + a e^2) x + c d e x^2)^p, written + /// here with k for e, which is Euler's number. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class ASymbolicPowerOfAProductBesideAPowerOfAFactorIntegralTest + { + private static void DifferentiatesBack(string integrand, params (string, double)[] pins) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + + // Pinned before it is differentiated, with exponents whose denominators are even, so + // that every power of a negative base is read on the principal branch + // (https://github.com/asc-community/AngouriMath/issues/204). + var pinned = integral.Substitute("C", 0); + Entity original = integrand.ToEntity(); + foreach (var (name, value) in pins) + { + pinned = pinned.Substitute(name, value); + original = original.Substitute(name, value); + } + var derivative = pinned.Differentiate("x"); + foreach (var at in new[] { -2.3, -0.7, 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}"); + } + } + + [Theory] + [InlineData("(d+k*x)^(-3-2*p)*(a*d*k+(c*d^2+a*k^2)*x+c*d*k*x^2)^p")] + [InlineData("(d+k*x)^(-5-2*p)*(a*d*k+(c*d^2+a*k^2)*x+c*d*k*x^2)^p")] + [InlineData("(a*d*k+(c*d^2+a*k^2)*x+c*d*k*x^2)^p/(d+k*x)^p")] + public void IsAnsweredAndDifferentiatesBack(string integrand) + { + DifferentiatesBack(integrand, ("a", 2), ("c", 1.5), ("d", 1.25), ("k", 2), ("p", 0.25)); + DifferentiatesBack(integrand, ("a", -1), ("c", 2), ("d", -0.5), ("k", 1.5), ("p", -1.75)); + } + } +} From cc72b31d4131c7056c77cce83eb1410a81a5fcdb Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Wed, 14 Oct 2026 16:44:59 +0000 Subject: [PATCH 2/2] Record the answers a symbolic power of a product split gives Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 3273ba5bf..ae2a54fa6 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -733,6 +733,21 @@ The answers are as large as the split integrands' answers, which are piecewise i | `"(a - b*x^2)^(3/2)/sqrt(a^2 - b^2*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 6,543 characters | | `"1/((a - b*x^2)^(1/2)*sqrt(a^2 - b^2*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 877 characters | +### A symbolic power of a product beside a symbolic power of one of its factors is split + +**Answers where there were none.** `(d + k x)^(-3 - 2p) (a d k + (c d^2 + a k^2) x + c d k x^2)^p` was declined: +the split of a power of a product beside a power of a factor took rational exponents only. `(P Q)^r` is +`K P^r Q^r` with `K` constant between the zeros of `P` and `Q` for any exponent free of x, so a symbolic one is +split too, and the powers of `P` gather ([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"(d + k*x)^(-3 - 2*p)*(a*d*k + (c*d^2 + a*k^2)*x + c*d*k*x^2)^p".ToEntity().Integrate("x")` | not done in 60 seconds | 349 characters | +| `"(d + k*x)^m/(a*d*k + (c*d^2 + a*k^2)*x + c*d*k*x^2)^m".ToEntity().Integrate("x")` | `integral(...)` | 215 characters | +| `"(A + B*x)*(a*c + b*c*x)^m/(a^2 + 2*a*b*x + b^2*x^2)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | 476 characters | +| `"(d + k*x)^m*(f + g*x)^3/(a*d*k + (c*d^2 + a*k^2)*x + c*d*k*x^2)^m".ToEntity().Integrate("x")` | `integral(...)` | 1,817 characters | +| `"(1 - x^4)^n/(1 + x + x^2 + x^3)^n".ToEntity().Integrate("x")` | `integral(...)` | 147 characters | + ### A secant or a cosecant beside a power of a sum of them is read as a power of the cosine or the sine **Answers where there were none.** `sec(x) sqrt(sec(x) + tan(x))` was declined, with the rest of Rubi's 4.7.7