diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 4b4fbb1e8..db2c34985 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -208,6 +208,18 @@ after it
|---|---|---|
| `"(a*c + b*c*x)^(-3-2*p)*(f + g*x)*(a^2 + 2*a*b*x + b^2*x^2)^p".ToEntity().Integrate("x")` | `integral(...)`; an answer with no value on the unreleased master | the antiderivative |
+### A sine of a double argument beside the argument is written as a product
+
+**Answers where there were none.** `csc(a + b x)^3 sin(2a + 2b x)^7` was declined: the rule that writes multiples
+of one argument in it reads a numeric slope and bounds the degree it writes. A sine of the double is one product,
+`sin(2A) = 2 sin(A) cos(A)`, whatever its power, and is written so beside functions of `A` for any slope
+([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"csc(a + b*x)^3*sin(2*a + 2*b*x)^7".ToEntity().Integrate("x")` | `integral(...)` | 127 characters |
+| `"csc(a + b*x)*sin(2*a + 2*b*x)^8".ToEntity().Integrate("x")` | `integral(...)` | 134 characters |
+
### A sine or a cosine of a shifted argument is written in the other
**Answers where there were none.** `sin(a + b x) sec(c + b x)^3` was declined, with the rest of Rubi's 4.7.1
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 62e7c55c5..aca16ef3c 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -12197,6 +12197,40 @@ node is Powf(var @base, var power) && @base.ContainsNode(u)
return back.Nodes.Any(node => node == MathS.NaN) ? null : back;
}
+ ///
+ /// A sine of twice an argument beside functions of the argument, written as the product it is:
+ /// sin(2A) = 2 sin(A) cos(A), so that every trigonometric function is of A.
+ /// csc(a + b x)^3 sin(2a + 2b x)^7 is 2^7 sin(a + b x)^4 cos(a + b x)^7.
+ ///
+ ///
+ /// The rule that writes multiples of one argument in it reads a numeric slope, and bounds the
+ /// degree it writes; a sine of the double is one product whatever its power, so it is written
+ /// here for any slope. Rubi's 4.7.1.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ internal static Entity? SolveByWritingASineOfADoubleArgumentAsAProduct(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ var arguments = new List();
+ foreach (var node in expr.Nodes)
+ if (node is Sinf or Cosf or Tanf or Cotanf or Secantf or Cosecantf && node.ContainsNode(x)
+ && node.DirectChildren.First() is var argument && !arguments.Contains(argument))
+ arguments.Add(argument);
+ if (arguments.Count != 2)
+ return null;
+ foreach (var (single, twice) in new[] { (arguments[0], arguments[1]), (arguments[1], arguments[0]) })
+ {
+ if (!TreeAnalyzer.TryGetPolyLinear(single, x, out var slope, out _) || slope.ContainsNode(x)
+ || (twice - 2 * single).Expand().InnerSimplified.Evaled is not Number.Complex { IsZero: true })
+ continue;
+ // Only sines of the double, so that each is one product.
+ if (expr.Nodes.Any(node => node is Cosf or Tanf or Cotanf or Secantf or Cosecantf && node.DirectChildren.First() == twice))
+ return null;
+ var rewritten = expr.Replace(node => node is Sinf(var a) && a == twice ? 2 * MathS.Sin(single) * MathS.Cos(single) : node);
+ return Integration.ComputeAsTheSameQuestion(rewritten, x, integrateByParts);
+ }
+ return null;
+ }
+
///
/// An integrand whose trigonometric functions have different multiples of one
/// argument — sin(x)/cos(2x), cos(x)/(sin(x) tan(x/2)) — rewritten so that
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index b8a20f845..f5f23bba3 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -1196,6 +1196,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// And the other way round: not a product of two arguments but anything else built
// from different multiples of one -- `sin(x)/cos(2x)`, `cos(x)/(sin(x) tan(x/2))` --
// rewritten to the one argument every trigonometric rule above reads.
+ // A sine of the double argument beside the argument: one product, for any slope.
+ if ((answer = IndefiniteIntegralSolver.SolveByWritingASineOfADoubleArgumentAsAProduct(expr, x, integrateByParts)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveByUnifyingTrigonometricArguments(expr, x, integrateByParts)) is { }) return answer;
// The exponential substitution beside the other rewrites. It is also what integrates
// the hyperbolic functions, which are not nodes here but quotients of exponentials.
diff --git a/Sources/Tests/UnitTests/Calculus/ASineOfADoubleArgumentIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ASineOfADoubleArgumentIntegralTest.cs
new file mode 100644
index 000000000..b25bdf2db
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ASineOfADoubleArgumentIntegralTest.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 sine of twice an argument beside functions of the argument, written as the product
+ /// sin(2A) = 2 sin(A) cos(A), for a symbolic slope too. Rubi's 4.7.1.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ASineOfADoubleArgumentIntegralTest
+ {
+ [Theory]
+ [InlineData("csc(a + b*x)^3*sin(2*a + 2*b*x)^7")]
+ [InlineData("csc(a + b*x)*sin(2*a + 2*b*x)^8")]
+ public void AsAProduct(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.9 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}