diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index b73c79b1d..0f440ae58 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -570,6 +570,20 @@ antiderivative on each side of it, as Rubi's is
| `"1/((2 + 3*x^2)^(1/4)*(4 + 3*x^2))".ToEntity().Integrate("x")` | `integral(...)` | the same in `(2 + 3 x^2)^(1/4)` |
| `"1/((-2 + 3*x^2)*(-1 + 3*x^2)^(1/4))".ToEntity().Integrate("x")` | `integral(...)` | `-(arctan(u) + artanh(u))/(2 sqrt(6))`, `u = sqrt(3) x/(sqrt(2) (-1 + 3 x^2)^(1/4))` |
+### A power of `csc(x) - sin(x)` or `sec(x) - cos(x)` is integrated as sine and cosine powers
+
+**Answers where there were none.** `(csc(x) - sin(x))^(5/2)` was declined, with the rest of Rubi's
+4.7.7 to a power that is not whole of either difference, either way up. `csc(z) - sin(z)` is
+`cos(z)^2/sin(z)` and `sec(z) - cos(z)` is `sin(z)^2/cos(z)`; written so, the power is a product of
+powers of the sine and cosine, which are integrated through the tangent, a quotient of whole powers
+of the six functions read inside a power as it is
+([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(csc(x) - sin(x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | powers of `sin(x)` times `(cos(x)^2/sin(x))^(5/2)` |
+| `"1/(sec(x) - cos(x))^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | a root of `cos(x)` with a logarithm and an arctangent, times `(sin(x)^2/cos(x))^(-3/2)` |
+
### A product of powers of the trigonometric functions, some not whole, is integrated as sine and cosine powers
**Answers where there were none.** `(a sin(x))^(5/2) sqrt(b sec(x))` was declined, and so were
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index fd17457b1..c88422e78 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -9090,33 +9090,38 @@ private static bool TryReadAHomogeneousTrigonometricPolynomial(
var (@base, power) = factor is Powf(var b, var p) && p.Evaled is Number.Rational r
? (b, r)
: (factor, (Number.Rational)Number.Integer.One);
- // A constant multiple of a function, and a whole power of one inside the power:
- // `(b tan(z)^3)^(3/2)`.
- if (@base is Mulf(var left, var right))
- @base = !left.ContainsNode(x) ? right : !right.ContainsNode(x) ? left : @base;
- if (@base is Powf(var inner, Number.Integer innerPower))
- (@base, power) = (inner, (Number.Rational)(power * innerPower));
if (underneath)
power = (Number.Rational)(-power);
- if (power is not Number.Integer)
- notWhole = true;
- // The powers of the sine and of the cosine each function is.
- (Entity Of, int OnSine, int OnCosine)? read = @base switch
- {
- Sinf(var of) => (of, 1, 0),
- Cosf(var of) => (of, 0, 1),
- Tanf(var of) => (of, 1, -1),
- Cotanf(var of) => (of, -1, 1),
- Secantf(var of) => (of, 0, -1),
- Cosecantf(var of) => (of, -1, 0),
- _ => null
- };
- if (read is null || argument is not null && argument != read.Value.Of)
+ // Inside the power, a constant multiple of a product and quotient of whole powers of
+ // the functions: `(b tan(z)^3)^(3/2)`, and `(cos(z)^2/sin(z))^(5/2)`, which is how
+ // `(csc(z) - sin(z))^(5/2)` is written. Not whole where some function's power is not:
+ // `(a csc(z)^2)^(-7/2)` is a whole power of the modulus of the cosecant, which the rule for
+ // an even power under a root answers with its sign.
+ if (!TryReadAMonomialInTheTrigonometricFunctions(@base, x, out var parts))
return null;
- var (functionOf, onSine, onCosine) = read.Value;
- argument = functionOf;
- sine = (Number.Rational)(sine + power * onSine);
- cosine = (Number.Rational)(cosine + power * onCosine);
+ foreach (var (function, times) in parts)
+ {
+ // The powers of the sine and of the cosine each function is.
+ (Entity Of, int OnSine, int OnCosine)? read = function switch
+ {
+ Sinf(var of) => (of, 1, 0),
+ Cosf(var of) => (of, 0, 1),
+ Tanf(var of) => (of, 1, -1),
+ Cotanf(var of) => (of, -1, 1),
+ Secantf(var of) => (of, 0, -1),
+ Cosecantf(var of) => (of, -1, 0),
+ _ => null
+ };
+ if (read is null || argument is not null && argument != read.Value.Of)
+ return null;
+ var (functionOf, onSine, onCosine) = read.Value;
+ argument = functionOf;
+ var weight = (Number.Rational)(power * Number.Integer.Create(times));
+ if (weight is not Number.Integer)
+ notWhole = true;
+ sine = (Number.Rational)(sine + weight * onSine);
+ cosine = (Number.Rational)(cosine + weight * onCosine);
+ }
}
if (argument is null || !notWhole
|| !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || TreeAnalyzer.IsZero(slope))
@@ -9150,6 +9155,87 @@ private static bool TryReadAHomogeneousTrigonometricPolynomial(
}
+ ///
+ /// A difference of a trigonometric function and its reciprocal's partner written as the
+ /// quotient it is: csc(z) - sin(z) is cos(z)^2/sin(z) and sec(z) - cos(z)
+ /// is sin(z)^2/cos(z), so that a power of either is a product of powers of the sine
+ /// and cosine; the integrand so written is asked as the same question.
+ ///
+ ///
+ /// (csc(x) - sin(x))^(5/2) was declined, with the rest of Rubi's 4.7.7 to a power that
+ /// is not whole of either difference, either way up. As the quotient it is a product of
+ /// powers of the sine and cosine, which the rule beside it reads through the tangent.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ internal static Entity? SolveByWritingADifferenceOfReciprocalFunctionsAsAQuotient(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ var rewritten = expr.Replace(node =>
+ {
+ if (!node.ContainsNode(x))
+ return node;
+ // Each term a signed function: `csc(z)`, `-sin(z)`, `-1 * cos(z)`.
+ (Entity Function, int Sign) Signed(Entity term, int sign) => term switch
+ {
+ Mulf(Number.Integer { EInteger: var one }, var f) when one.Equals(EInteger.FromInt32(-1)) => (f, -sign),
+ Mulf(var f, Number.Integer { EInteger: var one }) when one.Equals(EInteger.FromInt32(-1)) => (f, -sign),
+ _ => (term, sign)
+ };
+ List<(Entity Function, int Sign)>? signed = node switch
+ {
+ Minusf(var minuend, var subtrahend) => new() { Signed(minuend, 1), Signed(subtrahend, -1) },
+ Sumf(var left, var right) when left is not (Sumf or Minusf) && right is not (Sumf or Minusf) => new() { Signed(left, 1), Signed(right, 1) },
+ _ => null
+ };
+ if (signed is null)
+ return node;
+ var (first, second) = (signed[0], signed[1]);
+ if (first.Sign == second.Sign)
+ return node;
+ var (positive, negative) = first.Sign > 0 ? (first.Function, second.Function) : (second.Function, first.Function);
+ return (positive, negative) switch
+ {
+ (Cosecantf(var a), Sinf(var b)) when a == b => MathS.Pow(MathS.Cos(a), 2) / MathS.Sin(a),
+ (Sinf(var a), Cosecantf(var b)) when a == b => -MathS.Pow(MathS.Cos(a), 2) / MathS.Sin(a),
+ (Secantf(var a), Cosf(var b)) when a == b => MathS.Pow(MathS.Sin(a), 2) / MathS.Cos(a),
+ (Cosf(var a), Secantf(var b)) when a == b => -MathS.Pow(MathS.Sin(a), 2) / MathS.Cos(a),
+ _ => node
+ };
+ });
+ return rewritten == expr ? null : Integration.ComputeAsTheSameQuestion(rewritten, x, integrateByParts);
+ }
+
+ ///
+ /// read as a constant times a product of whole powers of the six
+ /// trigonometric functions, each with how many times it divides or multiplies; false where
+ /// anything else holds .
+ ///
+ private static bool TryReadAMonomialInTheTrigonometricFunctions(Entity expr, Entity.Variable x, out List<(Entity Function, int Times)> parts)
+ {
+ var read = new List<(Entity Function, int Times)>();
+ parts = read;
+ return Read(expr, 1);
+
+ bool Read(Entity e, int times)
+ {
+ switch (e)
+ {
+ case var constant when !constant.ContainsNode(x):
+ return true;
+ case Mulf(var left, var right):
+ return Read(left, times) && Read(right, times);
+ case Divf(var above, var below):
+ return Read(above, times) && Read(below, -times);
+ case Powf(var inner, Number.Integer { EInteger: var power }) when power.CanFitInInt32():
+ return Read(inner, times * power.ToInt32Unchecked());
+ case Sinf or Cosf or Tanf or Cotanf or Secantf or Cosecantf:
+ read.Add((e, times));
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+
///
/// with a whole power of a product read as the product of the
/// powers, a negative one on the other side of the bar: 1/(cos(x)^(7/2) sqrt(sin(x)))
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 08211ea4b..94ed6ccd9 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -917,6 +917,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// A power of the cotangent that is not whole beside the tangent, written as the
// tangent's with the constant that takes in front, which the substitution below reads.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAPowerOfTheCotangentInTheTangent(expr, x, integrateByParts)) is { }) return answer;
+ // A difference of a function and its reciprocal's partner, `csc(z) - sin(z)`, as the
+ // quotient of powers it is, for the rule after it.
+ if ((answer = IndefiniteIntegralSolver.SolveByWritingADifferenceOfReciprocalFunctionsAsAQuotient(expr, x, integrateByParts)) is { }) return answer;
// A product of powers of the trigonometric functions of one argument, some not whole,
// whose powers of the sine and cosine add up to an even number: the tangent's form
// times a constant, which the substitution below reads.
diff --git a/Sources/Tests/UnitTests/Calculus/DifferenceOfReciprocalFunctionsIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/DifferenceOfReciprocalFunctionsIntegralTest.cs
new file mode 100644
index 000000000..42d5ada2c
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/DifferenceOfReciprocalFunctionsIntegralTest.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
+{
+ ///
+ /// A power that is not whole of csc(x) - sin(x) or sec(x) - cos(x), which are
+ /// cos(x)^2/sin(x) and sin(x)^2/cos(x): written so, a product of powers of the
+ /// sine and cosine, read through the tangent. Rubi's 4.7.7.
+ /// #718
+ ///
+ ///
+ /// Compared as complex numbers on both sides of the zeros of the sine and the cosine, where the
+ /// integrands are complex on some of the intervals.
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class DifferenceOfReciprocalFunctionsIntegralTest
+ {
+ [Theory]
+ [InlineData("(csc(x) - sin(x))^(5/2)")]
+ [InlineData("(csc(x) - sin(x))^(3/2)")]
+ [InlineData("1/(csc(x) - sin(x))^(1/2)")]
+ [InlineData("1/(csc(x) - sin(x))^(7/2)")]
+ [InlineData("(sec(x) - cos(x))^(5/2)")]
+ [InlineData("1/(sec(x) - cos(x))^(3/2)")]
+ [InlineData("(sin(x) - csc(x))^(3/2)")]
+ public void IsAProductOfPowersOfTheSineAndCosine(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+ var derivative = integral.Substitute("C", 0).Differentiate("x");
+ var original = integrand.ToEntity();
+ foreach (var at in new[] { -2.3, -1.1, -0.4, 0.4, 1.1, 1.9, 2.6 })
+ {
+ 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}");
+ }
+ }
+ }
+}