diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 070dcb02e..cee0dcaa1 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -654,6 +654,20 @@ that `sec(x)^2/(a + b sin(x))` is rational in them rather than declined at once
| `"tan(x)^4/(a + a*cos(x))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `tan(x/2)` |
| `"csc(x)^2/(a + a*cos(x))".ToEntity().Integrate("x")` | `integral(...)` | `(tan(x/2)^3/12 + tan(x/2)/2 - 1/(4 tan(x/2)))/a` |
+### A tangent times that of its double is a secant less one
+
+**Answers where there were none.** `sec(2(a + b x))^2 sqrt(c tan(a + b x) tan(2(a + b x)))` was
+declined, with the rest of Rubi's 4.7.7 powers of `c tan(y) tan(2y)` beside the secant or cosine
+of `2y`, some after searches past the budget. `tan(y) tan(2y)` is `sec(2y) - 1`, and written so the
+integrand is in the one argument `2y`, which the half-angle tangent answers; the answer is exact
+where the integrand is real ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"sec(2*(a + b*x))^2*sqrt(c*tan(a + b*x)*tan(2*(a + b*x)))".ToEntity().Integrate("x")` | `integral(...)` | `sgn(tan(a + b x))` times powers of `sqrt(1 - tan(a + b x)^2)` |
+| `"1/sqrt(c*tan(a + b*x)*tan(2*(a + b*x)))".ToEntity().Integrate("x")` | `integral(...)` | logarithms in `tan(a + b x)`, `sgn(tan(a + b x))` in front |
+| `"tan(x)*tan(2*x)".ToEntity().Integrate("x")` | `integral(...)` | `ln((1 + sin(2x))/(1 - sin(2x)))/4 - x` |
+
### A power of `a + i a tan` beside a power of the secant is integrated as an exponential
**Answers where there were none.** `sqrt(a + i a tan(x))/sqrt(c sec(x))` is `sqrt(a/c) e^(i x/2)`
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 120c9f71b..db5f3fcf6 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -4152,6 +4152,56 @@ Entity Integral(ERational power)
return roots < 2 ? answer : answer.Provided((secantKind ? MathS.Cos(argument) : MathS.Sin(argument)) >= Number.Integer.Zero);
}
+ ///
+ /// tan(y) tan(2y) written sec(2y) - 1, and the integrand asked again in the
+ /// one argument 2y.
+ ///
+ ///
+ ///
+ /// tan(y) is (1 - cos(2y))/sin(2y), so tan(y) tan(2y) is
+ /// (1 - cos(2y))/cos(2y), which is sec(2y) - 1 wherever both sides are
+ /// defined; at the zeros of cos(y) the left is -2 as a limit, which the right
+ /// is. Rubi's 4.7.7 has sec(2(a + b x))^k sqrt(c tan(a + b x) tan(2(a + b x))) and
+ /// its kin, a half-odd power of c sec(2y) - c beside powers of the secant or cosine
+ /// of 2y, which
+ /// answers in that one argument; with two arguments nothing read them.
+ ///
+ ///
+ /// The same question in another spelling, so asked as the same question. The answer is
+ /// what that rule gives, exact where the integrand is real.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ ///
+ internal static Entity? SolveByWritingATangentTimesThatOfItsDoubleThroughTheSecant(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ static bool IsTwice(Entity twice, Entity once)
+ => (twice - 2 * once).Expand().InnerSimplified.Evaled is Number.Complex { IsZero: true };
+ var found = false;
+ var rewritten = expr.Replace(node =>
+ {
+ if (node is not Mulf || !node.ContainsNode(x))
+ return node;
+ var factors = Mulf.LinearChildren(node).ToList();
+ var single = factors.FindIndex(factor => factor is Tanf(var y) && y.ContainsNode(x)
+ && factors.Any(other => other is Tanf(var z) && z != y && IsTwice(z, y)));
+ if (single < 0)
+ return node;
+ var once = ((Tanf)factors[single]).Argument;
+ var twice = factors.FindIndex(other => other is Tanf(var z) && z != once && IsTwice(z, once));
+ var doubled = ((Tanf)factors[twice]).Argument;
+ Entity others = Number.Integer.One;
+ for (var i = 0; i < factors.Count; i++)
+ if (i != single && i != twice)
+ others = others == Number.Integer.One ? factors[i] : others * factors[i];
+ found = true;
+ // Spread over the difference, so that a root of it reads as `c sec(2y) - c`.
+ return others == Number.Integer.One ? MathS.Sec(doubled) - 1 : others * MathS.Sec(doubled) - others;
+ });
+ if (!found)
+ return null;
+ return Integration.ComputeAsTheSameQuestion(rewritten, x, integrateByParts);
+ }
+
///
/// as what it is a product of beside , and
/// the sum of the exponents of each base in it, read through products, quotients and
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 9f1119d6b..494173d9e 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -867,6 +867,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
if ((answer = IndefiniteIntegralSolver.SolveSymbolicPowersOfOnePlusMinusASineThroughTheSine(expr, x, integrateByParts)) is { }) return answer;
// And a half-odd power of a +- a sec(y), which is that square over cos(y): by the half-angle
// tangent, in which the whole is rational beside one root.
+ // And `tan(y) tan(2y)` written `sec(2y) - 1` first, so that the rule reads one argument.
+ if ((answer = IndefiniteIntegralSolver.SolveByWritingATangentTimesThatOfItsDoubleThroughTheSecant(expr, x, integrateByParts)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveByTheHalfAngleTangentBesideAHalfOddPowerOfOnePlusASecant(expr, x, integrateByParts)) is { }) return answer;
// And `a ± a cosh(y)` under a fractional power: `2a cosh(y/2)^2`, `-2a sinh(y/2)^2`.
if ((answer = IndefiniteIntegralSolver.SolveByTheHalfAngleWhereOnePlusAHyperbolicCosineIsASquare(expr, x, integrateByParts)) is { }) return answer;
diff --git a/Sources/Tests/UnitTests/Calculus/TangentTimesThatOfItsDoubleIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/TangentTimesThatOfItsDoubleIntegralTest.cs
new file mode 100644
index 000000000..1a3eb0a2b
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/TangentTimesThatOfItsDoubleIntegralTest.cs
@@ -0,0 +1,51 @@
+//
+// 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
+{
+ ///
+ /// tan(y) tan(2y) is sec(2y) - 1, and written so a power of
+ /// c tan(y) tan(2y) beside the secant or cosine of 2y is in one argument, which
+ /// the half-angle tangent answers. Rubi's 4.7.7.
+ /// #718
+ ///
+ ///
+ /// Checked by differentiating back with a = 0.4, b = 1.1, c = 1.3 where the
+ /// integrand is real, cos(2(a + b x)) positive, on both sides of the zero of
+ /// tan(a + b x): the rule the rewriting reaches is exact there.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class TangentTimesThatOfItsDoubleIntegralTest
+ {
+ [Theory]
+ [InlineData("sec(2*(a + b*x))^2*sqrt(c*tan(a + b*x)*tan(2*(a + b*x)))")]
+ [InlineData("cos(2*(a + b*x))*(c*tan(a + b*x)*tan(2*(a + b*x)))^(3/2)")]
+ [InlineData("1/sqrt(c*tan(a + b*x)*tan(2*(a + b*x)))")]
+ [InlineData("sec(2*(a + b*x))/(c*tan(a + b*x)*tan(2*(a + b*x)))^(3/2)")]
+ [InlineData("tan(x)*tan(2*x)")]
+ public void IsAPowerOfASecantLessOne(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+ Entity Pinned(Entity e) => e.Substitute("a", 0.4).Substitute("b", 1.1).Substitute("c", 1.3);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ foreach (var at in new[] { -0.9, -0.7, -0.5, 0.05, 0.2 })
+ {
+ 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}");
+ }
+ }
+ }
+}