diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 5b1e84346..433a7875c 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -447,6 +447,23 @@ case is most of its length ([#718](https://github.com/asc-community/AngouriMath/
| `"x^4/((a + b*x^2 + c*x^4)*sqrt(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 28,551 characters |
| `"1/(x^2*(a + b*x^2 + c*x^4)*sqrt(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 10,194 characters |
+### Two linears to one power are integrated as one quadratic to that power
+
+**Answers where there were none.** `(2 + 3 x)^4/((1 - 2 x)^(5/2) (3 + 5 x)^(5/2))` ran past any budget, and
+`1/((1 - x)^(1/3) (2 - x)^(1/3) x)` was declined. `L1^p L2^p` for linears with numbers for coefficients and `p` not
+whole, other than `1/2`, is now integrated as `(L1^p L2^p/Q^p)` times the integral over `Q^p`, `Q = L1 L2`. That
+quotient is constant on each interval between the roots, so it stays in the answer as written
+([#718](https://github.com/asc-community/AngouriMath/issues/718)). `e` is Euler's number below.
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"1/((1-x)^(1/3)*(2-x)^(1/3)*x)".ToEntity().Integrate("x")` | `integral(...)` | 381 characters |
+| `"(e*x)^(3/2)/((1-x)^(1/4)*(1+x)^(1/4))".ToEntity().Integrate("x")` | `integral(...)` | 1,403 characters |
+| `"1/((1-x)^(1/4)*(e*x)^(1/2)*(1+x)^(1/4))".ToEntity().Integrate("x")` | `integral(...)` | 1,226 characters |
+| `"(2+3*x)^6/((1-2*x)^(5/2)*(3+5*x)^(5/2))".ToEntity().Integrate("x")` | `integral(...)` | 1,236 characters |
+| `"(2+3*x)^4/((1-2*x)^(5/2)*(3+5*x)^(5/2))".ToEntity().Integrate("x")` | `integral(...)` | 784 characters |
+| `"1/((1-2*x)^(5/2)*(2+3*x)^2*(3+5*x)^(5/2))".ToEntity().Integrate("x")` | `integral(...)` | 382 characters |
+
### A power of an even quadratic over a binomial in it is split over the binomial's roots
**Answers where there were none.** `(1 - x^2)/(a + b (1 - x^2)^4)` was declined. `w^m/(a + b w^n)` for `w = p + q x^2` and
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index dec1de4e6..2be6bf3a8 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -19792,6 +19792,76 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
}
+ ///
+ /// Two linears to one power that is not whole, as one quadratic:
+ /// integral g L1^p L2^p = (L1^p L2^p/Q^p) integral g Q^p for Q = L1 L2.
+ ///
+ ///
+ /// L1^p L2^p/Q^p is constant wherever it is continuous -- it is 1 where both linears
+ /// are positive and a fixed root of unity on each interval between their roots -- so its
+ /// derivative is zero and the product differentiates back. Rubi's 1.1.1.3
+ /// (e x)^(3/2)/((1 - x)^(1/4) (1 + x)^(1/4)) was declined, and
+ /// (2 + 3 x)^4/((1 - 2 x)^(5/2) (3 + 5 x)^(5/2)) took forty-five seconds; over
+ /// (1 - x^2)^(1/4) and (3 - x - 10 x^2)^(5/2) they take half a second.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ internal static Entity? SolveTwoLinearsToOnePowerAsOneQuadratic(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!Integration.AnsweringTheQuestionAsked)
+ return null;
+ // Without a leading `1 *`, which is another spelling to the rules that read the result.
+ static Entity Times(Entity a, Entity b) => a == Number.Integer.One ? b : a * b;
+ static Entity Over(Entity a, Entity b) => a / b;
+ var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
+ var factors = new List<(Entity Base, Number.Rational Power, bool Below, Entity Factor)>();
+ Entity rest = Number.Integer.One;
+ foreach (var (side, below) in new[] { (numerator, false), (denominator, true) })
+ foreach (var factor in Mulf.LinearChildren(side))
+ {
+ if (factor is Powf(var @base, Number.Rational power) && power is not Number.Integer
+ && TreeAnalyzer.TryGetPolyLinear(@base, x, out var slope, out _) && !VanishesIdentically(slope))
+ factors.Add((@base, power, below, factor));
+ else
+ rest = below ? Over(rest, factor) : Times(rest, factor);
+ }
+ Number.Rational Signed((Entity Base, Number.Rational Power, bool Below, Entity Factor) f) => f.Below ? (Number.Rational)(-f.Power) : f.Power;
+ for (var i = 0; i < factors.Count; i++)
+ for (var j = i + 1; j < factors.Count; j++)
+ {
+ var (first, second) = (factors[i], factors[j]);
+ var power = Signed(first);
+ if (power != Signed(second)
+ // Square roots of two linears belong to the rules for Rubi's 1.1.1.6 and
+ // to the combined radicals, which write the sign as sgn; and with symbols in
+ // the linears the rules for each of them answer, in a tenth of the time.
+ || power == Number.Rational.Create(1, 2) || power == Number.Rational.Create(-1, 2)
+ || (first.Base * second.Base).Vars.Any(symbol => symbol != x)
+ || !TreeAnalyzer.TryGetPolyLinear(first.Base, x, out var s1, out var c1)
+ || !TreeAnalyzer.TryGetPolyLinear(second.Base, x, out var s2, out var c2)
+ // Constant multiples of one linear are one linear, not a quadratic.
+ || VanishesIdentically((s1 * c2 - s2 * c1).Expand()))
+ continue;
+ var quadratic = (first.Base * second.Base).Expand().InnerSimplified;
+ Entity others = rest;
+ for (var k = 0; k < factors.Count; k++)
+ if (k != i && k != j)
+ others = factors[k].Below ? Over(others, factors[k].Factor) : Times(others, factors[k].Factor);
+ // Spelled as a quotient where the power is negative, which is how the rules for a
+ // power of a quadratic read it: as `g Q^(-5/2)` it was as slow as the split form.
+ var asked = power.IsNegative ? Over(others, MathS.Pow(quadratic, (Number.Rational)(-power))) : Times(others, MathS.Pow(quadratic, power));
+ if (Integration.ComputeAsAQuestionOfItsOwn(asked, x, integrateByParts) is not { } joined)
+ return null;
+ // What is checked is the joined integral against the joined integrand: the constant
+ // is constant by the argument above, and differentiated it is a sum the sampled
+ // check does not reduce to zero.
+ if (!Functions.PartialFractions.DerivativeHoldsAtSampledPoints(joined, asked, x))
+ return null;
+ var constant = MathS.Pow(first.Base, power) * MathS.Pow(second.Base, power) / MathS.Pow(quadratic, power);
+ return constant * joined;
+ }
+ return null;
+ }
+
///
/// A power of a linear that is not whole, times a linear, over a whole power of a quadratic,
/// with the power of the quadratic lowered one at a time:
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 0b3cbfba1..480b29622 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -665,6 +665,10 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// over each linear once with the constant that takes in front. First, since it answers
// the question asked only, and the polynomial term below writes `1/u` as `u^(-1)` and
// asks that a level down: `1/(e^atanh(a x) (c - c/(a x))^(3/2))` reached it nowhere.
+ // Two linears to one power that is not whole, as one quadratic to that power, before each
+ // linear is written once: `(2 + 3x)^4/((1 - 2x)^(5/2) (3 + 5x)^(5/2))` took forty-five
+ // seconds there, and over `(3 - x - 10x^2)^(5/2)` it takes half of one.
+ if ((answer = IndefiniteIntegralSolver.SolveTwoLinearsToOnePowerAsOneQuadratic(expr, x, integrateByParts)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveByWritingEachLinearOnce(expr, x, integrateByParts)) is { }) return answer;
// A rational function with numbers for coefficients over a denominator past the twelfth
// degree, by Hermite's reduction before anything searches: nothing below answered one.
diff --git a/Sources/Tests/UnitTests/Calculus/TwoLinearsToOnePowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/TwoLinearsToOnePowerIntegralTest.cs
new file mode 100644
index 000000000..128ddf4bd
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/TwoLinearsToOnePowerIntegralTest.cs
@@ -0,0 +1,69 @@
+//
+// 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
+{
+ ///
+ /// Two linears to one power that is not whole, integrated as one quadratic to that power with
+ /// L1^p L2^p/(L1 L2)^p in front, which is constant on each interval it is continuous on.
+ /// Rubi's 1.1.1.3, which declined the quarter powers and took forty-five seconds over the
+ /// powers of 5/2.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class TwoLinearsToOnePowerIntegralTest
+ {
+ private static void DifferentiatesBack(string integrand, Entity[] points, params (string, double)[] pins)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+
+ 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 points)
+ {
+ 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}");
+ }
+ }
+
+ private static readonly Entity[] TheLine = { "-23/10", "-7/10", "-3/10", "2/5", "9/10", "5/2" };
+
+ // Where 1 - 2x and 3 + 5x are positive: outside, the integrand is complex and the answer's
+ // arcsin is past its real interval, which the evaluator reads as NaN at its default
+ // precision; the answer agrees with the integrand there to 2000 digits.
+ private static readonly Entity[] BetweenTheRoots = { "-11/20", "-3/10", "0", "1/5", "9/20" };
+
+ [Theory]
+ [InlineData("(e*x)^(3/2)/((1-x)^(1/4)*(1+x)^(1/4))")]
+ [InlineData("1/((1-x)^(1/4)*(e*x)^(1/2)*(1+x)^(1/4))")]
+ public void TwoQuarterPowers(string integrand)
+ {
+ DifferentiatesBack(integrand, TheLine, ("e", 1.5));
+ DifferentiatesBack(integrand, TheLine, ("e", -0.75));
+ }
+
+ [Theory]
+ [InlineData("(2+3*x)^4/((1-2*x)^(5/2)*(3+5*x)^(5/2))")]
+ [InlineData("1/((1-2*x)^(5/2)*(2+3*x)^2*(3+5*x)^(5/2))")]
+ public void TwoPowersOfFiveHalves(string integrand) => DifferentiatesBack(integrand, BetweenTheRoots);
+ }
+}