diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 0f08eed34..1880f261b 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -194,6 +194,24 @@ back after. Each answer is in three cases by the sign of `4 a/c - (b/c)^2`
| `"x^5/(a + b*x + c*x^2)^5".ToEntity().Integrate("x")` | `integral(...)` | 4,066 characters |
| `"x^7/(a + b*x + c*x^2)^5".ToEntity().Integrate("x")` | `integral(...)` | 5,602 characters |
+### A power of x below the bar beside a root of a binomial is rationalised by dividing by the root
+
+**Answers where there were none.** `(a + b x^3)^(1/3)/(x^2 (c + d x^3))` was declined, and so were sixteen more of Rubi's
+1.1.3.4 with `x^2`, `x^5`, `x^8`, `x^11` or `x^14` below the bar beside `(a + b x^3)^(1/3)`, `^(4/3)` or `^(-2/3)`.
+`u = x/(a + b x^3)^(1/3)` makes each rational, and was used where the power of x stood above the bar only:
+`x^(-2)` is `x` times `x^(-3)`, and the second is a function of `x^3` like the rest
+([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(a + b*x^3)^(1/3)/(x^2*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 857 characters |
+| `"(a + b*x^3)^(1/3)/(x^11*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 4,974 characters |
+| `"(a + b*x^3)^(4/3)/(x^2*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,562 characters |
+| `"(a + b*x^3)^(4/3)/(x^14*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 6,092 characters |
+| `"1/(x^2*(a + b*x^3)^(2/3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,586 characters |
+| `"(a + b*x^3)^(1/3)/(x^2*(a*d - b*d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 773 characters |
+| `"1/(x^2*(1 - x^3)^(2/3)*(1 + x^3))".ToEntity().Integrate("x")` | `integral(...)` | 471 characters |
+
### `a + i a tan` beside the tangent's differential is lowered over its conjugate
**Answers where there were none.** `sqrt(a + i a tan(x))/(c + d tan(x))^(3/2)` ran past thirty seconds:
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index ce2c2c176..ff16fe0f5 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -5528,7 +5528,10 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
/// u^j times the same in u, since x^j (c + d x^n)^(-j/n) is
/// u^j. Rubi's 1.1.3.4: x/((a + b x^3)^(2/3) (c + d x^3)) is
/// u/(c - (b c - a d) u^3), and x^4 (a + b x^3)^(1/3)/(c + d x^3) is the
- /// same with x^3 beside it; each was declined.
+ /// same with x^3 beside it; each was declined. A power of x below the bar is read
+ /// the same way, with the multiple of n it leaves going into the rational function:
+ /// (a + b x^3)^(1/3)/(x^2 (c + d x^3)) is x times 1/(x^3 (c + d x^3))
+ /// times (a + b x^3)^(1 - 2/3), Rubi's 1.1.3.4:763.
///
/// https://github.com/asc-community/AngouriMath/issues/718
///
@@ -5577,20 +5580,30 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
// stand-in for x^n where every power of x is a multiple of n, and declined otherwise.
if (j != 0)
{
- // Out of the polynomial above the bar, each of whose powers is then a multiple of n.
+ // Out of the polynomial above the bar together with the power of x below it, each
+ // power then a multiple of n, below zero too: `x^(-2)` is `x` times `x^(-3)`.
var (restAbove, restBelow) = Functions.SingleQuotient.Of(rest);
if (!TreeAnalyzer.TryGetPolynomial(restAbove, x, out var aboveTerms) || aboveTerms.Count == 0)
return null;
+ var belowX = 0;
+ Entity belowOtherwise = Number.Integer.One;
+ foreach (var factor in Mulf.LinearChildren(restBelow))
+ if (factor == x)
+ belowX++;
+ else if (factor is Powf(var xAgain, Number.Integer power) && xAgain == x && power.EInteger.Sign > 0 && power.EInteger.CanFitInInt32())
+ belowX += power.EInteger.ToInt32Checked();
+ else
+ belowOtherwise = belowOtherwise == Number.Integer.One ? factor : belowOtherwise * factor;
Entity lowered = Number.Integer.Zero;
foreach (var term in aboveTerms)
{
- var power = term.Key.Subtract(EInteger.FromInt32(j));
- if (power.Sign < 0 || term.Value.ContainsNode(x))
+ var power = term.Key.Subtract(EInteger.FromInt32(j + belowX));
+ if ((belowX == 0 && power.Sign < 0) || term.Value.ContainsNode(x))
return null;
var monomial = power.IsZero ? term.Value : term.Value * MathS.Pow(x, Number.Integer.Create(power));
lowered = lowered == Number.Integer.Zero ? monomial : lowered + monomial;
}
- rest = restBelow == Number.Integer.One ? lowered : lowered / restBelow;
+ rest = belowOtherwise == Number.Integer.One ? lowered : lowered / belowOtherwise;
}
var xToTheN = Variable.CreateUnique(expr, "x_n");
var inXToTheN = rest.Replace(node =>
diff --git a/Sources/Tests/UnitTests/Calculus/APowerOfXBelowTheBarBesideARootOfABinomialIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APowerOfXBelowTheBarBesideARootOfABinomialIntegralTest.cs
new file mode 100644
index 000000000..ef221ba63
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/APowerOfXBelowTheBarBesideARootOfABinomialIntegralTest.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 below the bar beside a root of a binomial in x^n and a rational function
+ /// of x^n, rationalised by u = x/(a + b x^n)^(1/n): x^(-2) is x
+ /// times x^(-3). Rubi's 1.1.3.4.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class APowerOfXBelowTheBarBesideARootOfABinomialIntegralTest
+ {
+ [Theory]
+ [InlineData("(a + b*x^3)^(1/3)/(x^2*(c + d*x^3))", "0.35 0.6 0.85 1.4")]
+ [InlineData("(a + b*x^3)^(1/3)/(x^5*(c + d*x^3))", "0.35 0.6 0.85 1.4")]
+ [InlineData("(a + b*x^3)^(4/3)/(x^2*(c + d*x^3))", "0.35 0.6 0.85 1.4")]
+ [InlineData("1/(x^2*(a + b*x^3)^(2/3)*(c + d*x^3))", "0.35 0.6 0.85 1.4")]
+ [InlineData("(a + b*x^3)^(1/3)/(x^2*(a*d - b*d*x^3))", "0.35 0.6 1.4 1.8")]
+ [InlineData("1/(x^2*(1 - x^3)^(2/3)*(1 + x^3))", "0.35 0.6 0.8 0.95")]
+ public void DifferentiatesBack(string integrand, string points)
+ {
+ 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", 7);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ foreach (var at in points.Split(' '))
+ {
+ var want = original.Substitute("x", at.ToEntity()).EvalNumerical();
+ var got = derivative.Substitute("x", at.ToEntity()).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}");
+ }
+ }
+ }
+}