diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 1bec563d1..32cb2980c 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -736,6 +736,18 @@ of one argument in it reads a numeric slope and bounds the degree it writes. A s
| `"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 cosecant of a double argument beside the argument is written as a product
+
+**Answers where there were none.** `csc(2x) ln(tan(x))` was declined, Rubi's 4.7.7: the rule that writes
+`sin(2A)` as `2 sin(A) cos(A)` beside functions of `A` declined any other function of `2A`. The cosecant is
+`1/(2 sin(A) cos(A))`, one product as well, and is written so now, which leaves `ln(tan(x))` beside its derivative
+([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"csc(2*x)*ln(tan(x))".ToEntity().Integrate("x")` | `integral(...)` | `1/2 * ln(tan(x)) ^ 2 / 2` |
+| `"csc(2*x)*ln(tan(x))^2".ToEntity().Integrate("x")` | `integral(...)` | `1/2 * ln(tan(x)) ^ 3 / 3` |
+
### 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 8a9d3ef73..869928c13 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -12968,9 +12968,11 @@ node is Powf(var @base, var power) && @base.ContainsNode(u)
}
///
- /// 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.
+ /// A sine or a cosecant 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,
+ /// and csc(2x) ln(tan(x)) is ln(tan(x))/(2 sin(x) cos(x)), which is
+ /// ln(tan(x)) beside its derivative.
///
///
/// The rule that writes multiples of one argument in it reads a numeric slope, and bounds the
@@ -12992,10 +12994,15 @@ node is Powf(var @base, var power) && @base.ContainsNode(u)
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))
+ // Only sines and cosecants of the double, so that each is one product.
+ if (expr.Nodes.Any(node => node is Cosf or Tanf or Cotanf or Secantf && 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);
+ var rewritten = expr.Replace(node => node switch
+ {
+ Sinf(var a) when a == twice => 2 * MathS.Sin(single) * MathS.Cos(single),
+ Cosecantf(var a) when a == twice => 1 / (2 * MathS.Sin(single) * MathS.Cos(single)),
+ _ => node
+ });
return Integration.ComputeAsTheSameQuestion(rewritten, x, integrateByParts);
}
return null;
diff --git a/Sources/Tests/UnitTests/Calculus/ACosecantOfADoubleArgumentIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ACosecantOfADoubleArgumentIntegralTest.cs
new file mode 100644
index 000000000..76a51c2f1
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ACosecantOfADoubleArgumentIntegralTest.cs
@@ -0,0 +1,47 @@
+//
+// 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 cosecant of twice the argument beside functions of the argument, written as
+ /// 1/(2 sin cos): csc(2x) ln(tan(x)) is ln(tan(x)) beside its derivative.
+ /// Rubi's 4.7.7.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ACosecantOfADoubleArgumentIntegralTest
+ {
+ [Theory]
+ [InlineData("csc(2*x)*ln(tan(x))", 200)]
+ [InlineData("csc(2*x)*ln(cot(x))", 200)]
+ [InlineData("csc(2*x)*ln(tan(x))^2", 200)]
+ [InlineData("csc(2*x)*ln(a*tan(x))", 400)]
+ public void DifferentiatesBack(string integrand, int atMost)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < atMost, $"{text.Length} characters of answer for {integrand}");
+ Entity Pinned(Entity e) => e.Substitute("a", 2).Substitute("b", 3).Substitute("c", "5/4".ToEntity());
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ foreach (var at in new[] { "0.35", "0.6", "1.1", "1.4" })
+ {
+ var want = original.Substitute("x", at.ToEntity()).EvalNumerical();
+ var got = derivative.Substitute("x", at.ToEntity()).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}");
+ }
+ }
+ }
+}