diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 914d0be9a..30ac30458 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -232,6 +232,25 @@ Rubi's `x^m (a + b x^n)^p` and `(a + b x^n)^p (c + d x^n)^q`
| `"1/(2+3/x^2)^3".Integrate("x")`, `1/(a + b/x^3)` | left unevaluated | an antiderivative |
| `"(a+b*x^n)*(c+d*x^n)^3".Integrate("x")` | left unevaluated | written out, eight powers of `x` integrated: `a c^3 x + ... + b d^3 x^(4 n + 1)/(4 n + 1)` |
+### A whole power of a quotient with a symbol in it is integrated as the quotient of the powers
+
+**Answers where there were none, and a slowdown since 2.5.0 undone.** `(c/(a + c x^2))^2` was
+declined, after twenty-six seconds, while `c^2/(a + c x^2)^2`, the same function, was answered at
+once: the table reads the quotient of the powers and nothing read the power of the quotient. It is
+written so now wherever the quotient is of two polynomials in `x` with a symbol in them and what
+it leaves above the bar is a monomial, the table's `x^m/(a + b x^n)^p`; that is exact for a whole
+power. That is
+also how the substitution `u = x^2` writes `x/(a + c x^4)^2` since 2.5.0, having simplified
+`1/(a/c + u^2)^2`, and that integrand took twenty seconds where 2.5.0 took a tenth of one
+([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(c/(a + c*x^2))^2".ToEntity().Integrate("x")` | `integral(...)`, after a minute | a quotient and an arctangent or a logarithm, by the sign of `a c` |
+| `"(1/(a + c*x^2))^2".ToEntity().Integrate("x")` | `integral(...)` | the same |
+| `"(x/(a + c*x^2))^2".ToEntity().Integrate("x")` | `integral(...)` | the same |
+| `"x/(a + c*x^4)^2".ToEntity().Integrate("x")` | the answer, in 0.1 s; 22 s on the unreleased master | the answer, in 0.06 s |
+
### A polynomial with symbols in it that is a binomial or an even quartic in a shifted variable is integrated in it
**Answers where there were none.** `1/(c^2 x^3 + 3 b c x^2 + 3 b^2 x + 3 a b)` was left unevaluated,
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 17ade5dd3..783c208e8 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -17021,6 +17021,44 @@ node is Powf(Mulf(var l, var r) product, Number.Integer power) && product.Contai
return distributed == expr ? null : Integration.ComputeAsTheSameQuestion(distributed, x, integrateByParts);
}
+ ///
+ /// A whole power of a quotient with a symbol in it, written as the quotient of the powers:
+ /// (c/(a + c u^2))^2 is c^2/(a + c u^2)^2, exact for a whole power. That is how
+ /// the substitution u = x^2 writes x/(a + c x^4)^2, having simplified
+ /// 1/(a/c + u^2)^2, and the table reads the quotient of the powers and not the power
+ /// of the quotient: c^2/(a + c x^2)^2 was answered in a few milliseconds where
+ /// (c/(a + c x^2))^2 went to parts and was declined after twenty-six seconds, and
+ /// x/(a + c x^4)^2 was answered after forty, by another route. With numbers only it
+ /// is answered as it is already. Only a quotient of two polynomials in :
+ /// the hyperbolic functions arrive as quotients of exponentials, and the imaginary tangent's
+ /// sum as A e^(i z)/cos(z), and written apart those went to slower routes --
+ /// cosh(c + d x)/(a + b tanh(c + d x)^2) from 17 ms to past five seconds. And only with
+ /// a monomial left above the bar, the table's x^m/(a + b x^n)^p: a sum there is
+ /// multiplied out by the rational rules, and the folded tanh(a + 2 ln(x))^2,
+ /// ((e^(2a) x^4 - 1)/(e^(2a) x^4 + 1))^2, went from five seconds to forty-five that way.
+ /// Asked as the same question.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ internal static Entity? SolveByDistributingWholePowersOfQuotients(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ var distributed = expr.Replace(node =>
+ node is Powf(Divf(var above, var below) quotient, Number.Integer power) && quotient.ContainsNode(x)
+ && quotient.Vars.Any(symbol => symbol != x)
+ && IsAPolynomialIn(above, x) && IsAPolynomialIn(below, x)
+ && IsAMonomialIn(power.EInteger.Sign > 0 ? above : below, x)
+ ? power.EInteger.Sign > 0
+ ? MathS.Pow(above, power) / MathS.Pow(below, power)
+ : MathS.Pow(below, Number.Integer.Create(power.EInteger.Negate())) / MathS.Pow(above, Number.Integer.Create(power.EInteger.Negate()))
+ : node);
+ return distributed == expr ? null : Integration.ComputeAsTheSameQuestion(distributed, x, integrateByParts);
+
+ static bool IsAPolynomialIn(Entity expr, Entity.Variable x)
+ => TreeAnalyzer.TryGetPolynomial(expr, x, out var terms)
+ && terms.Keys.All(power => power.Sign >= 0) && terms.Values.All(coefficient => !coefficient.ContainsNode(x));
+ static bool IsAMonomialIn(Entity expr, Entity.Variable x)
+ => TreeAnalyzer.TryGetPolynomial(expr, x, out var terms) && terms.Count == 1;
+ }
+
///
/// A polynomial in x times a rational function of exponentials of x,
/// by parts against the whole rational function: x tanh(x)^2 is
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 087ac67fb..16e7e4282 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -684,6 +684,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// A whole power of a product of a constant and the variable, as the product of
// the powers, which is how the inverse hyperbolic secant and cosecant arrive.
if ((answer = IndefiniteIntegralSolver.SolveByDistributingWholePowersOfProducts(expr, x, integrateByParts)) is { }) return answer;
+ // And of quotients, which a substitution leaves where it has simplified a sum over
+ // the bar: `(c/(a + c u^2))^2` is read by nothing that reads `c^2/(a + c u^2)^2`.
+ if ((answer = IndefiniteIntegralSolver.SolveByDistributingWholePowersOfQuotients(expr, x, integrateByParts)) is { }) return answer;
// And a fractional or symbolic power of a monomial: `(c x^n)^b` is `c^b x^(n b)`
// for a positive `c`, on the `x > 0` where a symbolic `n` leaves the integrand real.
if ((answer = IndefiniteIntegralSolver.SolveByDistributingAPowerOfAMonomial(expr, x, integrateByParts)) is { }) return answer;
diff --git a/Sources/Tests/UnitTests/Calculus/WholePowerOfAQuotientIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/WholePowerOfAQuotientIntegralTest.cs
new file mode 100644
index 000000000..cd8fa691e
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/WholePowerOfAQuotientIntegralTest.cs
@@ -0,0 +1,53 @@
+//
+// 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 whole power of a quotient with a symbol in it is the quotient of the powers:
+ /// (c/(a + c x^2))^2 is c^2/(a + c x^2)^2, which the table answers, and was
+ /// declined. It is how the substitution u = x^2 writes x/(a + c x^4)^2.
+ /// #718
+ ///
+ ///
+ /// Checked by differentiating back with a = 2.3, b = 0.7, c = 1.3, on
+ /// both sides of zero.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class WholePowerOfAQuotientIntegralTest
+ {
+ [Theory]
+ [InlineData("(c/(a + c*x^2))^2")]
+ [InlineData("(1/(a + c*x^2))^2")]
+ [InlineData("(x/(a + c*x^2))^2")]
+ [InlineData("(c/(a + c*x^2))^3")]
+ [InlineData("((a + c*x^2)/x)^(-2)")]
+ [InlineData("x/(a + c*x^4)^2")]
+ [InlineData("x^2/(a + b*x^6)^2")]
+ public void IsTheQuotientOfThePowers(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+ Assert.DoesNotContain("NaN", integral.Stringize());
+ Entity Pinned(Entity e) => e.Substitute("a", 2.3).Substitute("b", 0.7).Substitute("c", 1.3);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ foreach (var at in new[] { -1.4, -0.6, 0.5, 1.2 })
+ {
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ var want = original.Substitute("x", at).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}");
+ }
+ }
+ }
+}