diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 698516842..2fb2558c9 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -740,6 +740,19 @@ inner-simplified now, and declined in seconds where the parts do not separate
| `"x^4*e^(2*i*atan(a + b*x))".ToEntity().Integrate("x")` | `integral(...)` | a polynomial, logarithms and arctangents of `a + b x` |
| `"sec(c + d*x)^2/(a + i*a*tan(c + d*x))".ToEntity().Integrate("x")` | `ln(i a d tan(c + d x) + a d)/(i a d)` | `ln(i tan(c + d x) + 1)/(i a d)`, the same up to a constant |
+### A root of a square in a trigonometric function is its modulus
+
+**Answers where there were none.** `(a + b sin(x)) sqrt(b^2 + 2 a b sin(x) + a^2 sin(x)^2)` was
+declined or past the budget, with the rest of Rubi's 4.7.7 half-odd powers of a perfect square in a
+sine, tangent or secant. The radicand is `a^2 (f + b/a)^2`, so its root is `sqrt(a^2) |f + b/a|`,
+written with the sign of `f + b/a` in front of the integral, as a root of a perfect square in `x`
+already was ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(a + b*sin(x))*sqrt(b^2 + 2*a*b*sin(x) + a^2*sin(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | `sgn(sin(x) + b/a) sqrt(a^2)` times an expression in `x`, `cos(x)` and `sin(2x)`, `provided a^2 > 0` |
+| `"(a + b*tan(x))/sqrt(b^2 + 2*a*b*tan(x) + a^2*tan(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | `sgn(tan(x) + b/a) a/sqrt(a^2)` times the antiderivative of `(a + b tan(x))/(a tan(x) + b)`, `provided a^2 > 0` |
+
### `NaN` was returned as the antiderivative of something that has one
**A wrong answer, not a missing one.** `1/(a*x^2)` came back as `NaN + C`, and `NaN` is this
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index f5c4c2674..b58db0ce0 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -18029,6 +18029,86 @@ bool MayBeARootOfASquare(Entity node, Entity.Variable x)
return assumed == Entity.Boolean.True ? answer : answer.Provided(assumed);
}
+ ///
+ /// A half-odd power of a perfect square in one trigonometric function of
+ /// is that power of the modulus: sqrt(b^2 + 2 a b sin(y) + a^2 sin(y)^2) is
+ /// sqrt(a^2) |sin(y) + b/a|, which is sqrt(a^2) sgn(sin(y) + b/a) (sin(y) + b/a),
+ /// the sign constant between the zeros of the linear in the sine.
+ ///
+ ///
+ ///
+ /// 's reading, with the function for the
+ /// variable: Rubi's 4.7.7 has (a + b sin(d + e x)) sqrt(b^2 + 2 a b sin(d + e x) + a^2 sin(d + e x)^2)
+ /// and the same with a tangent or a secant, at the half-odd powers either way, and they
+ /// were declined or past the budget where with the modulus written the rest is rational in
+ /// the function. At the top only, and only where each such root is a factor of the
+ /// integrand, so that its sign goes in front; a leading coefficient a number or one of
+ /// known sign for a real parameter, whose condition travels with the answer, as that
+ /// rule's does.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ ///
+ internal static Entity? SolveByTakingARootOfAPerfectSquareInATrigonometricFunction(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!Integration.AnsweringTheQuestionAsked)
+ return null;
+ var factors = FactorsOfTheIntegrand(expr).Select(pair => pair.Factor).ToList();
+ var changed = false;
+ var declined = false;
+ Entity assumed = Entity.Boolean.True;
+ Entity signs = Number.Integer.One;
+ var written = expr.Replace(node =>
+ {
+ if (declined || node is not Powf(var radicand, Number.Rational r) || r is Number.Integer
+ || !r.ERational.Denominator.Equals(EInteger.FromInt32(2)) || !radicand.ContainsNode(x)
+ || radicand.Complexity > 40 || TreeAnalyzer.TryGetPolynomial(radicand, x, out _))
+ return node;
+ var functions = radicand.Nodes.Where(inner => inner.ContainsNode(x)
+ && inner is Sinf or Cosf or Tanf or Cotanf or Secantf or Cosecantf).Distinct().ToList();
+ if (functions.Count != 1)
+ return node;
+ var function = functions[0];
+ var w = Variable.CreateUnique(expr, "w_sq");
+ var inW = radicand.Replace(inner => inner == function ? w : inner);
+ if (inW.ContainsNode(x) || !TreeAnalyzer.TryGetPolynomial(inW, w, out var monomials)
+ || monomials.Keys.Max() is not { } degree || !degree.Equals(EInteger.FromInt32(2))
+ || monomials.Keys.Any(k => k.Sign < 0))
+ return node;
+ var a = monomials[degree];
+ var b = monomials.TryGetValue(EInteger.One, out var b1) ? b1 : Number.Integer.Zero;
+ var c = monomials.TryGetValue(EInteger.Zero, out var c0) ? c0 : Number.Integer.Zero;
+ if (b.Evaled is Number.Complex and not Number.Real || c.Evaled is Number.Complex and not Number.Real
+ || !IsAPerfectSquareDiscriminant(a, b, c))
+ return node;
+ if (a.Evaled is not Number.Real { IsPositive: true })
+ {
+ if (!IsPositiveForARealParameter(a, x, out var leadingAssumed, assumed))
+ return node;
+ assumed = leadingAssumed;
+ }
+ // The sign in front of the integral, which needs the root to be a factor of it.
+ if (factors.Count(factor => factor == node) < expr.Nodes.Count(inner => inner == node))
+ {
+ declined = true;
+ return node;
+ }
+ var h = (b / (Number.Integer.Create(2) * a)).InnerSimplified;
+ if (h.Vars.Any())
+ h = Functions.PartialFractions.Bare(h.Simplify());
+ var linear = h == Number.Integer.Zero ? function : (function + h).InnerSimplified;
+ var twoR = Number.Integer.Create(r.ERational.Numerator);
+ signs = signs * MathS.Signum(linear);
+ changed = true;
+ return MathS.Pow(a, r) * (twoR == Number.Integer.One ? linear : MathS.Pow(linear, twoR));
+ });
+ if (declined || !changed)
+ return null;
+ if (Integration.ComputeAsTheSameQuestion(written, x, integrateByParts) is not { } answer)
+ return null;
+ answer = signs * answer;
+ return assumed == Entity.Boolean.True ? answer : answer.Provided(assumed);
+ }
+
///
/// A fractional power of a sum whose every term has x to a power in it,
/// (a x^j + b x^n)^p with 0 < j < n, as K x^(j p) (a + b x^(n - j))^p:
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index d956c6a3d..370bcd288 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -702,6 +702,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
if ((answer = IndefiniteIntegralSolver.SolveAReciprocalOfAnInverseTrigonometricFunction(expr, x, integrateByParts)) is { }) return answer;
// A fractional power of a perfect square is the power of the modulus, sgn(P) P^(2r).
if ((answer = IndefiniteIntegralSolver.SolveByTakingARootOfAPerfectSquare(expr, x, integrateByParts)) is { }) return answer;
+ // And a square in one trigonometric function, at the top: the modulus of the linear in it.
+ if ((answer = IndefiniteIntegralSolver.SolveByTakingARootOfAPerfectSquareInATrigonometricFunction(expr, x, integrateByParts)) is { }) return answer;
// And any power of a square in any power of x, as the power of its root times a factor
// constant where the root is not zero: `x^2 (a^2 + 2 a b x^3 + b^2 x^6)^p`.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAPowerOfASquareAsAPowerOfItsRoot(expr, x, integrateByParts)) is { }) return answer;
diff --git a/Sources/Tests/UnitTests/Calculus/RootOfASquareInATrigonometricFunctionIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/RootOfASquareInATrigonometricFunctionIntegralTest.cs
new file mode 100644
index 000000000..2d6ad6238
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/RootOfASquareInATrigonometricFunctionIntegralTest.cs
@@ -0,0 +1,49 @@
+//
+// 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 half-odd power of b^2 + 2 a b f + a^2 f^2, a perfect square in one trigonometric
+ /// function f, is that power of sqrt(a^2) |f + b/a|, the sign in front of the
+ /// integral. Rubi's 4.7.7.
+ /// #718
+ ///
+ ///
+ /// Checked by differentiating back with a = 1.3, b = 0.7, on both sides of the
+ /// zeros of f + b/a and of zero.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class RootOfASquareInATrigonometricFunctionIntegralTest
+ {
+ [Theory]
+ [InlineData("(a + b*sin(x))*sqrt(b^2 + 2*a*b*sin(x) + a^2*sin(x)^2)")]
+ [InlineData("(a + b*tan(x))/sqrt(b^2 + 2*a*b*tan(x) + a^2*tan(x)^2)")]
+ [InlineData("(a + b*sec(x))*(b^2 + 2*a*b*sec(x) + a^2*sec(x)^2)^(3/2)")]
+ [InlineData("(a + b*sin(g + f*x))/(b^2 + 2*a*b*sin(g + f*x) + a^2*sin(g + f*x)^2)^(3/2)")]
+ public void IsAPowerOfTheModulusOfTheLinear(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("g", 0.4).Substitute("f", 1.1);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ foreach (var at in new[] { -2.3, -1.1, -0.3, 0.4, 1.1, 2.3 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.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) + Math.Abs((double)want.ImaginaryPart)),
+ $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
+ }
+ }
+ }
+}