From c1a4d9e2dd4f2cf2a3755fd12782562e3fbf169b Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Tue, 13 Oct 2026 06:50:08 +0000 Subject: [PATCH 1/4] The root rule reads a numeric repeated quadratic in its split Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Functions/Algebra/Polynomials/PartialFractions.cs | 9 +++++++-- .../Continuous/Integration/IndefiniteIntegralSolver.cs | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs index 7bebb4905..2f440cab6 100644 --- a/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs +++ b/Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs @@ -425,7 +425,12 @@ private static Entity AsFraction(RationalPolynomial numerator, RationalPolynomia /// https://github.com/asc-community/AngouriMath/issues/718 /// internal static bool TrySplitOverWrittenFactors( - Entity numerator, Entity denominator, Variable x, [NotNullWhen(true)] out Entity? decomposition) + Entity numerator, Entity denominator, Variable x, [NotNullWhen(true)] out Entity? decomposition, + // Whether the caller reads a polynomial over a power of a quadratic with numbers in + // it, as the rules beside the square root of a quadratic do: over the radicand's power + // it is a polynomial times a half-odd power of the radicand, and over another + // quadratic's a polynomial over a power of a quadratic beside the root of another. + bool aRepeatedQuadraticIsRead = false) { decomposition = null; @@ -464,7 +469,7 @@ internal static bool TrySplitOverWrittenFactors( // A repeated quadratic has a rule to land on only with a symbol somewhere, // the split by residues below; over the rationals the system would hand it a // cubic over the square, which no rule reads. - if (multiplicity > 1 && degree.ToInt32Unchecked() != 1 && !(numerator + denominator).Vars.Any(v => v != x)) + if (multiplicity > 1 && degree.ToInt32Unchecked() != 1 && !aRepeatedQuadraticIsRead && !(numerator + denominator).Vars.Any(v => v != x)) return false; foreach (var pair in read) if (pair.Key.Sign < 0 || pair.Value.ContainsNode(x)) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 8c828130a..262846620 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -18613,7 +18613,8 @@ static Entity WithoutConditions(Entity coefficient) { if (factors.Count == 1) pieces.Add(properNumerator / factors[0]); - else if (Functions.PartialFractions.TrySplitOverWrittenFactors(properNumerator, linears, x, out var decomposition) && decomposition is not null) + else if (Functions.PartialFractions.TrySplitOverWrittenFactors(properNumerator, linears, x, out var decomposition, aRepeatedQuadraticIsRead: true) + && decomposition is not null) { // The split comes back as a sum over a constant; the constant goes to each term. var (terms, over) = decomposition is Divf(var splitTop, var splitBottom) && !splitBottom.ContainsNode(x) From 57db99ec2c1fb63f3e94e377b715c4d058937603 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Tue, 13 Oct 2026 06:52:19 +0000 Subject: [PATCH 2/4] A half-odd power of the radicand above a power of another quadratic is reduced over the root Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Integration/IndefiniteIntegralSolver.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 262846620..d1ab6c907 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -18271,6 +18271,24 @@ Dictionary PowerOfQ(int j) else return null; } + // The root above the bar: `B^(n/2)` for an odd `n` is `B^((n + 1)/2)/sqrt(B)`, a + // polynomial over the root. `sqrt(3 - x + 2 x^2)/(2 + 3 x + 5 x^2)^2` was declined, + // the reduction reading only a root below (Rubi's 1.2.1.5). + if (radicand is null && quadratic is not null) + { + Entity rest = Number.Integer.One; + foreach (var factor in Mulf.LinearChildren(numerator)) + if (radicand is null && factor is Powf(var @base, Number.Rational { ERational: var exponent }) + && exponent.Denominator.Equals(EInteger.FromInt32(2)) && exponent.Numerator.Sign > 0 + && exponent.Numerator.CanFitInInt32() && @base.ContainsNode(x)) + { + radicand = @base; + rest = rest * MathS.Pow(@base, (exponent.Numerator.ToInt32Unchecked() + 1) / 2); + } + else + rest = rest * factor; + numerator = rest; + } if (radicand is null || quadratic is null || quadratic == radicand || !TreeAnalyzer.TryGetPolyQuadratic(quadratic, x, out var a2, out var a1, out var a0) || !TreeAnalyzer.TryGetPolyQuadratic(radicand, x, out var b2, out var b1, out var b0) From 9b2e58858e60170b25a3178d6c6d60406a7d4104 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Tue, 13 Oct 2026 06:55:10 +0000 Subject: [PATCH 3/4] Test the root above the bar and its radicand repeated below Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Calculus/QuadraticBesideARootIntegralTest.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/Tests/UnitTests/Calculus/QuadraticBesideARootIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/QuadraticBesideARootIntegralTest.cs index e80bfa78b..58a184548 100644 --- a/Sources/Tests/UnitTests/Calculus/QuadraticBesideARootIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/QuadraticBesideARootIntegralTest.cs @@ -110,6 +110,22 @@ public void APolynomialPartBeside(string integrand) public void OverAPowerOfTheQuadratic(string integrand) => DifferentiatesBackPinned(integrand, new[] { 0.3, 0.7, 0.9, -0.5 }, Pins); + /// + /// The root above the bar, B^(n/2) as B^((n + 1)/2) over the root; and a + /// power of the radicand below it beside a power of the other quadratic, where the split + /// has a numeric repeated quadratic in it, a polynomial over its power beside the root + /// that the reductions read. Rubi's 1.2.1.5, declined before. + /// + [Theory] + [InlineData("sqrt(3 - x + 2*x^2)/(2 + 3*x + 5*x^2)^2")] + [InlineData("(3 - x + 2*x^2)^(3/2)/(2 + 3*x + 5*x^2)^3")] + [InlineData("(a + b*x + c*x^2)^(1/2)/(d + k*x + f*x^2)^2")] + [InlineData("1/((3 - x + 2*x^2)^(3/2)*(2 + 3*x + 5*x^2)^2)")] + [InlineData("1/((3 - x + 2*x^2)^(5/2)*(2 + 3*x + 5*x^2))")] + [InlineData("1/((3 - x + 2*x^2)^(5/2)*(1 + x^2))")] + public void TheRootAboveOrItsRadicandRepeated(string integrand) + => DifferentiatesBackPinned(integrand, new[] { 0.3, 0.7, 0.9, -0.5 }, Pins); + /// /// Beside a half-odd power of a + a sec(x), the half-angle tangent leaves a power /// of (c + d) + (d - c) t^2 below the bar, which ran out Rubi's budget in 4.5.2.1. From fcbcb06e5ea929e822e39d8e7b8e561f2450ed82 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Tue, 13 Oct 2026 07:55:38 +0000 Subject: [PATCH 4/4] A quadratic's half-odd power beside a power of another in BREAKING-CHANGES Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index f2b0fc530..27016b206 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -470,6 +470,24 @@ by the linear's coefficients, `(b d + 2c d x)^9`, which ran past a minute | `"(b + 2*c*x)^9/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute; 76,153 characters on the unreleased master | 607 characters | | `"(b*d + 2*c*d*x)^9/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute | 611 characters | +### A quadratic's half-odd power beside a power of another quadratic is integrated + +**Answers where there were none.** `sqrt(3 - x + 2 x^2)/(2 + 3 x + 5 x^2)^2` was declined, and so were +`1/((3 - x + 2 x^2)^(5/2) (2 + 3 x + 5 x^2))` and most of Rubi's 1.2.1.5 past the first powers. Above the bar the root +was not read: `B^(n/2)` is `B^((n + 1)/2)` over `sqrt(B)`, a polynomial over the root, which the reduction over a power +of the other quadratic answers. Below it, the split of the rational part over the two quadratics declined a repeated +quadratic with numbers in it, although beside the root each piece is a shape the rules read +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"sqrt(3 - x + 2*x^2)/(2 + 3*x + 5*x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 1,344 characters | +| `"(3 - x + 2*x^2)^(3/2)/(2 + 3*x + 5*x^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 1,451 characters | +| `"1/((3 - x + 2*x^2)^(5/2)*(2 + 3*x + 5*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 1,380 characters | +| `"1/((3 - x + 2*x^2)^(5/2)*(2 + 3*x + 5*x^2)^3)".ToEntity().Integrate("x")` | `integral(...)` | 1,629 characters | +| `"(2 + 5*x + x^2)*sqrt(3 + 2*x + 5*x^2)/(1 + 4*x - 7*x^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 5,242 characters | +| `"(a + b*x + c*x^2)^(1/2)/(d + e*x + f*x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 50,758 characters, six symbols | + ### A square of a symbolic quadratic beside two linears is answered within the budget **Answers where there were none in time.** `1/((d + k x)(f + g x)(a + b x + c x^2)^2)` was declined by