diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 94869eaf4..bb79325a8 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -1212,6 +1212,26 @@ keeps its decline ([#718](https://github.com/asc-community/AngouriMath/issues/71
| `"tan(x)/sqrt(2+tan(x)+tan(x)^2)".Integrate("x")` | left unevaluated | the antiderivative |
| `"tan(x)^2/(2+tan(x)+tan(x)^2)^(3/2)".Integrate("x")` | left unevaluated | the antiderivative |
+### A power of the variable below the bar beside a root of a quadratic
+
+`1/(x^2 sqrt(4 + 3x + 2x^2))` was left as written, while `1/(x sqrt(...))` came out: the single
+factor was read and the repeated one was not. Under `x = 1/t` the integrand is
+`-sgn(t) t^(n - 1)/sqrt(q0 t^2 + q1 t + q2)` -- a polynomial over the root of the quadratic read
+the other way round, which the rules for those answer. That is what one round of parts against
+`arccos(a + b x)/x^4` leaves, so the inverse trigonometric rows over a power of `x` come with it.
+
+The radicand is assembled rather than substituted into, as `Q(1/t)` is `R(t)/t^2` and writing the
+quotient inside the root leaves a nesting nothing reduces; the modulus that leaves is a `sgn(t)`
+in front, which at `t = 1/x` is `sgn(x)`. `SolveByReciprocalSubstitution` is the same substitution
+for a different shape -- a palindromic quartic, where the reciprocal maps the quartic to itself --
+and reads nothing here ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"1/(x^3*sqrt(4+3*x+2*x^2))".Integrate("x")` | left unevaluated | the antiderivative |
+| `"1/(x^2*sqrt(1-(a+b*x)^2))".Integrate("x")` | left unevaluated | the antiderivative |
+| `"acos(a+b*x)/x^4".Integrate("x")` | left unevaluated | the antiderivative |
+
### `binomial(n, k)` is a function
**Addition, not silent.** The binomial coefficient is a node, `Entity.Binomialf`, spelled
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index d3eff5ea0..dae7a7793 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -14431,6 +14431,112 @@ private static (Entity Factor, Entity Inside)? TryTakeACommonLinearFactorOfASum(
return shifted.Nodes.Any(node => node == MathS.NaN) ? null : shifted;
}
+ ///
+ /// A power of the variable below the bar beside a root of a quadratic, by
+ /// x = 1/t: 1/(x^n sqrt(q0 + q1 x + q2 x^2)) is
+ /// -sgn(t) t^(n - 1)/sqrt(q0 t^2 + q1 t + q2), a polynomial over the root of the
+ /// quadratic with its coefficients reversed, which the rules for those answer.
+ ///
+ ///
+ ///
+ /// 1/(x sqrt(1 - (a + b x)^2)) was answered and 1/(x^2 sqrt(...)) was not:
+ /// the repeated factor is what nothing reads, and it is what a round of parts against
+ /// acos(a + b x)/x^4 leaves. is the
+ /// same substitution for a different shape -- a palindromic quartic under the root, where
+ /// the reciprocal maps the quartic to itself -- and reads nothing here.
+ ///
+ ///
+ /// The radicand is assembled rather than substituted into: Q(1/t) is
+ /// R(t)/t^2 with R the reversed quadratic, so the root is
+ /// R^(p/2) |t|^(-p), and writing the quotient inside the root instead leaves a
+ /// nesting nothing downstream reduces. The modulus is a sgn(t) in front for an odd
+ /// p, constant between its zeros; with t = 1/x it is sgn(x), which is
+ /// what the answers of this family carry anyway.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ ///
+ internal static Entity? SolveByTheReciprocalBesideARootOfAQuadratic(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!Integration.AnsweringTheQuestionAskedOrOneBelow)
+ return null;
+ var (numerator, denominator) = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(expr));
+ // A power of x, two or more, below the bar; one root of a quadratic; polynomials else.
+ var powerOfX = 0;
+ Entity? radicand = null;
+ Number.Rational? rootPower = null;
+ Entity rest = Number.Integer.One;
+ foreach (var (side, isBelow) in new[] { (numerator, false), (denominator, true) })
+ foreach (var factor in Mulf.LinearChildren(side))
+ {
+ if (!factor.ContainsNode(x))
+ {
+ rest = isBelow ? rest / factor : rest * factor;
+ continue;
+ }
+ switch (factor)
+ {
+ case Variable bare when bare == x && isBelow:
+ powerOfX += 1;
+ continue;
+ case Powf(Variable bare, Number.Integer whole) when bare == x && isBelow && whole.EInteger.Sign > 0 && whole.EInteger.CanFitInInt32():
+ powerOfX += whole.EInteger.ToInt32Unchecked();
+ continue;
+ case Powf(var inner, Number.Rational power) when power is not Number.Integer && radicand is null:
+ if (!power.ERational.Denominator.Equals(EInteger.FromInt32(2)))
+ return null;
+ radicand = inner;
+ rootPower = isBelow ? Number.Rational.Create(power.ERational.Negate()) : power;
+ continue;
+ default:
+ if (!TreeAnalyzer.TryGetPolynomial(factor, x, out _))
+ return null;
+ rest = isBelow ? rest / factor : rest * factor;
+ continue;
+ }
+ }
+ if (powerOfX < 2 || radicand is null || rootPower is null)
+ return null;
+ if (!TreeAnalyzer.TryGetPolynomial(radicand, x, out var monomials) || monomials.Count == 0
+ || monomials.Keys.Any(k => k.Sign < 0 || k.CompareTo(EInteger.FromInt32(2)) > 0)
+ || !monomials.ContainsKey(EInteger.FromInt32(2)))
+ return null;
+ if (!TreeAnalyzer.TryGetPolynomial(rest, x, out var restMonomials) || restMonomials.Keys.Any(k => k.Sign < 0))
+ return null;
+
+ var t = Variable.CreateUnique(expr, "u_recip");
+ // Q(1/t) is R(t)/t^2 with R the quadratic read the other way round.
+ var q0 = monomials.TryGetValue(EInteger.Zero, out var m0) ? m0 : Number.Integer.Zero;
+ var q1 = monomials.TryGetValue(EInteger.One, out var m1) ? m1 : Number.Integer.Zero;
+ var q2 = monomials[EInteger.FromInt32(2)];
+ var reversed = (q0 * MathS.Sqr(t) + q1 * t + q2).InnerSimplified;
+ if (q0.Evaled is Number.Complex { IsZero: true })
+ return null; // the reversed quadratic is linear, which is the substitution's own shape
+ // The rest at 1/t, term by term, so that no quotient is left nested.
+ Entity restInT = Number.Integer.Zero;
+ foreach (var pair in restMonomials)
+ {
+ if (!pair.Key.CanFitInInt32())
+ return null;
+ var degree = pair.Key.ToInt32Unchecked();
+ restInT += degree == 0 ? pair.Value : pair.Value / MathS.Pow(t, Number.Integer.Create(degree));
+ }
+ // dx = -dt/t^2, x^(-n) = t^n, and the root is R^(p/2) |t|^(-p).
+ var numeratorPower = Number.Integer.Create(rootPower.ERational.Numerator);
+ Entity integrand = -restInT * MathS.Pow(t, Number.Integer.Create(powerOfX - 2))
+ * MathS.Pow(reversed, rootPower) * MathS.Pow(t, (-numeratorPower).InnerSimplified);
+ Entity signs = numeratorPower.EInteger.IsEven ? Number.Integer.One : MathS.Signum(t);
+ // Bare: the reciprocal's own `t != 0` rides along as a condition -- it is `1/x`, so
+ // it says what the integrand already says -- and a condition is not an integrand to
+ // the rules below, which decline it.
+ integrand = Functions.PartialFractions.Bare(integrand.InnerSimplified);
+ if (integrand.ContainsNode(x) || integrand.Nodes.Any(node => node == MathS.NaN))
+ return null;
+ if (Integration.ComputeAsAQuestionOfItsOwn(integrand, t, integrateByParts) is not { } answer)
+ return null;
+ var back = (signs == Number.Integer.One ? answer : signs * answer).Substitute(t, (Number.Integer.One / x).InnerSimplified);
+ return back.Nodes.Any(node => node == MathS.NaN) ? null : back;
+ }
+
///
/// A pair of hyperbolic powers two apart whose coefficients kill the reduction's
/// residual: cosh(y)^p - (p - 1)/p cosh(y)^(p - 2) is
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index d50b5b83e..2f7c62191 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -799,6 +799,10 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// And a root of a palindromic quartic, which is a root of a quadratic in x -+ 1/x:
// Charlwood's `(1 + x^2)/((1 - x^2) sqrt(1 + x^4))` is `-du/(u sqrt(u^2 + 2))`.
if ((answer = IndefiniteIntegralSolver.SolveByReciprocalSubstitution(expr, x)) is { }) return answer;
+ // And a power of the variable below the bar beside a root of a quadratic, by the
+ // same reciprocal: `1/(x^2 sqrt(Q))` is a polynomial over the root of the reversed
+ // quadratic, which the rules for those answer.
+ if ((answer = IndefiniteIntegralSolver.SolveByTheReciprocalBesideARootOfAQuadratic(expr, x, integrateByParts)) is { }) return answer;
// A rational function of x and one cube root of a polynomial: no substitution
// rationalises it, and the elementary ones are logarithms of `L - y` for linear L
// whose cube agrees with the polynomial at the poles, found by an ansatz.
diff --git a/Sources/Tests/UnitTests/Calculus/ReciprocalSubstitutionTest.cs b/Sources/Tests/UnitTests/Calculus/ReciprocalSubstitutionTest.cs
index 8b2c3bbf1..43abf634c 100644
--- a/Sources/Tests/UnitTests/Calculus/ReciprocalSubstitutionTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/ReciprocalSubstitutionTest.cs
@@ -142,5 +142,33 @@ public void WhatIsNotAFunctionOfTheReciprocalVariable(string integrand)
return; // unanswered is a legitimate verdict; a wrong answer is not
DifferentiatesBack(integrand);
}
+
+ ///
+ /// A power of the variable below the bar beside a root of a quadratic, by the same
+ /// reciprocal: 1/(x^n sqrt(q0 + q1 x + q2 x^2)) is
+ /// -sgn(t) t^(n - 1)/sqrt(q0 t^2 + q1 t + q2), a polynomial over the root of the
+ /// quadratic read the other way round. 1/(x sqrt(1 - (a + b x)^2)) was answered
+ /// and the repeated factor was not, which is what a round of parts against
+ /// acos(a + b x)/x^4 leaves.
+ /// #718
+ ///
+ [Theory]
+ [InlineData("1/(x^2*sqrt(1 + x^2))")]
+ [InlineData("1/(x^3*sqrt(4 + 3*x + 2*x^2))")]
+ [InlineData("1/(x^2*sqrt(1 + x + x^2))")]
+ [InlineData("1/(x^4*sqrt(2 - x + x^2))")]
+ public void APowerOfTheVariableBesideARootOfAQuadratic(string integrand) => DifferentiatesBack(integrand);
+
+ ///
+ /// And with symbols among the coefficients, which is the shape Rubi's 5.2 rows leave:
+ /// acos(a + b x)/x^4 by parts is 1/(x^3 sqrt(1 - (a + b x)^2)) up to the
+ /// constants.
+ ///
+ [Theory]
+ [InlineData("1/(x^2*sqrt(1 - (a + b*x)^2))")]
+ [InlineData("1/(x^3*sqrt(1 - (a + b*x)^2))")]
+ [InlineData("acos(a + b*x)/x^4")]
+ public void TheSameWithSymbolicCoefficients(string integrand)
+ => DifferentiatesBack(integrand, ("a", 0.3), ("b", 0.4));
}
}