diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 9efef8955..ddc0fdac4 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -762,6 +762,21 @@ taken apart into pieces each closed the same way
| `"(2 + x)/((2 + 4*x - 3*x^2)*(1 + 3*x + 2*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative |
| `"1/((x^2 + 1)*sqrt(x^2 + x + 1))".ToEntity().Integrate("x")` | `integral(...)` | a logarithm and an arctangent |
+### A rational function of `x^n` beside a power of `x` and the root of a binomial is integrated
+
+**Answers where there were none.** `x/((a + b x^3)^(2/3) (c + d x^3))` and `x^4 (a + b x^3)^(1/3)/(c + d x^3)`,
+Rubi's 1.1.3.4, were declined, while the same without the power of `x` in front was answered under
+`u = x/(a + b x^n)^(1/n)`. That substitution takes `x^j (a + b x^n)^(k - (j + 1)/n)` beside a rational
+function of `x^n` to `u^j` times a rational function of `u`, for any `j` below `n`, and is taken for
+those now. Each answer holds on both sides of zero, an odd root of a negative being real here
+([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"x/((a + b*x^3)^(2/3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | logarithms and an arctangent of `x (a + b x^3)^(-1/3)` |
+| `"x^4*(a + b*x^3)^(1/3)/(c + d*x^3)".ToEntity().Integrate("x")` | `integral(...)` | the same, beside a rational function of it |
+| `"x/((1 + x^3)^(2/3)*(2 + x^3))".ToEntity().Integrate("x")` | `integral(...)` | the same with numbers |
+
### A rational function of `x^n` beside a power of a symbolic binomial is integrated without a search
**Answers where there were none.** A rational function of `x^n` beside `(c + d x^n)^(k - 1/n)` is a
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 858c776e3..130c68cd1 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -4185,6 +4185,14 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
/// The identity holds wherever the root is real, which is where the answer is
/// asked; u goes back in as x (c + d x^n)^(-1/n).
///
+ ///
+ /// And beside a power of x that is not one of x^n: x^j times a
+ /// rational function of x^n times (c + d x^n)^(k - (j + 1)/n) is
+ /// 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.
+ ///
/// https://github.com/asc-community/AngouriMath/issues/718
///
internal static Entity? SolveByDividingByTheRoot(Entity expr, Entity.Variable x)
@@ -4195,6 +4203,8 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
Entity? c = null, d = null;
var n = 0;
var k = EInteger.Zero;
+ // The power of x beside it, below n: the exponent is `k - (j + 1)/n`.
+ var j = 0;
Entity rest = Number.Integer.One;
var (above, below) = Functions.SingleQuotient.Of(expr);
foreach (var (side, isBelow) in new[] { (above, false), (below, true) })
@@ -4206,15 +4216,18 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
if (radicand is not null || !TryReadAsABinomialIn(@base, x, out var readC, out var readD, out var readN)
|| readN < 2 || !signed.Denominator.Equals(EInteger.FromInt32(readN)))
return null;
- // The numerator of the exponent is k n - 1.
- var shifted = signed.Numerator.Add(EInteger.One);
- if (!shifted.Remainder(EInteger.FromInt32(readN)).IsZero)
- return null;
+ // The numerator of the exponent is k n - j - 1, for j from 0 to n - 1.
+ var nAsEInteger = EInteger.FromInt32(readN);
+ var residue = signed.Numerator.Add(EInteger.One).Remainder(nAsEInteger);
+ if (residue.Sign < 0)
+ residue = residue.Add(nAsEInteger);
+ var readJ = residue.IsZero ? 0 : readN - residue.ToInt32Checked();
radicand = @base;
c = readC;
d = readD;
n = readN;
- k = shifted.Divide(EInteger.FromInt32(readN));
+ j = readJ;
+ k = signed.Numerator.Add(EInteger.FromInt32(readJ + 1)).Divide(nAsEInteger);
continue;
}
if (factor.ContainsNode(x) && !IsRationalIn(factor, x))
@@ -4223,8 +4236,25 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
}
if (radicand is null || c is null || d is null || TreeAnalyzer.IsZero(c) || TreeAnalyzer.IsZero(d))
return null;
- // The rest as a rational function of x^n: written in a stand-in for x^n where every
- // power of x is a multiple of n, and declined otherwise.
+ // The rest as a rational function of x^n, x^j taken out of it first: written in a
+ // 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.
+ var (restAbove, restBelow) = Functions.SingleQuotient.Of(rest);
+ if (!TreeAnalyzer.TryGetPolynomial(restAbove, x, out var aboveTerms) || aboveTerms.Count == 0)
+ return null;
+ 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))
+ 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;
+ }
var xToTheN = Variable.CreateUnique(expr, "x_n");
var inXToTheN = rest.Replace(node =>
node is Powf(var xAgain, Number.Integer power) && xAgain == x && power.EInteger.Remainder(EInteger.FromInt32(n)).IsZero
@@ -4243,8 +4273,11 @@ node is Powf(var xAgain, Number.Integer power) && xAgain == x && power.EInteger.
// base is its polynomial in u first, which collects what the substitution writes twice --
// `1 - (-u^2) - u^2` is 1 under a secant's half angle -- and the factors stay as they are
// written, which the rules after this read better than their product expanded.
+ var restInU = inXToTheN.Substitute(xToTheN, c * uToTheN / oneMinusDUToTheN);
+ if (j != 0)
+ restInU = MathS.Pow(u, j) * restInU;
var integrand = WithTheFactorsWrittenOnBothSidesCancelled(u, Functions.SingleQuotient.Combine(
- inXToTheN.Substitute(xToTheN, c * uToTheN / oneMinusDUToTheN)
+ restInU
* (k.IsZero ? Number.Integer.One : MathS.Pow(binomialInU, Number.Integer.Create(k)))
/ oneMinusDUToTheN).InnerSimplified);
if (integrand is Providedf(var inner, _))
diff --git a/Sources/Tests/UnitTests/Calculus/DividedByTheRootIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/DividedByTheRootIntegralTest.cs
index 6c95653a8..2ef444919 100644
--- a/Sources/Tests/UnitTests/Calculus/DividedByTheRootIntegralTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/DividedByTheRootIntegralTest.cs
@@ -65,5 +65,20 @@ public void TimofeevsQuarticBesideAFourthRoot()
[InlineData("1/((a + b*x^2)^(9/2)*(1 + x^2))")]
public void AHalfOddPowerOfASymbolicQuadratic(string integrand)
=> DifferentiatesBack(integrand, new[] { -1.3, -0.4, 0.3, 0.9, 1.7 }, ("a", 1.3), ("b", 0.7));
+
+ ///
+ /// Beside a power of x that is not one of x^n: x^j times a rational function of
+ /// x^n times (a + b x^n)^(k - (j + 1)/n) is u^j times the same in
+ /// u. Rubi's 1.1.3.4; each was declined. On both sides of zero, the cube root of a
+ /// negative being real.
+ ///
+ [Theory]
+ [InlineData("x/((a + b*x^3)^(2/3)*(c + d*x^3))")]
+ [InlineData("x^4/((a + b*x^3)^(2/3)*(c + d*x^3))")]
+ [InlineData("x^7/((a + b*x^3)^(2/3)*(c + d*x^3))")]
+ [InlineData("x^4*(a + b*x^3)^(1/3)/(c + d*x^3)")]
+ [InlineData("x/((1 + x^3)^(2/3)*(2 + x^3))")]
+ public void BesideAPowerOfX(string integrand)
+ => DifferentiatesBack(integrand, new[] { -2.7, -1.3, -0.6, 0.4, 1.3, 2.6 }, ("a", 2.3), ("b", 0.7), ("c", 1.3), ("d", 1.7));
}
}