diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 5d77674d5..31abe3799 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -508,6 +508,25 @@ 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 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
+`(d csc(x))^(3/2) sqrt(c sec(x))`, `cos(x)^(7/2)/sin(x)^(7/2)` and the rest of the products of
+powers of the trigonometric functions of one argument, each of which is `sin^M cos^N` times a constant
+on every interval where both are continuous. With the exponents adding up to an even number that is
+`tan^M (1 + tan^2)^(-(M + N)/2)`, a function of the tangent, and with `M` or `N` an odd whole number
+the plain product, which the substitution by the other function answers: Chebyshev's three cases.
+It is integrated so now, and the answer is the integrand times the antiderivative of that form over
+the form, which holds on every such interval, the constants inside the roots whatever their signs.
+Rubi's 4.1.0 to 4.6.0 ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"(a*sin(x))^(5/2)*sqrt(b*sec(x))".ToEntity().Integrate("x")` | `integral(...)` | the integrand times an antiderivative in `sqrt(tan(x))` over `tan(x)^(5/2) (1 + tan(x)^2)^(-1)` |
+| `"cos(x)^(7/2)/sin(x)^(7/2)".ToEntity().Integrate("x")` | `integral(...)` | the same over `tan(x)^(-7/2)` |
+| `"(b*tan(x)^3)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | the same over `tan(x)^(9/2)` |
+| `"(d*sec(x))^(5/2)*sqrt(b*tan(x))".ToEntity().Integrate("x")` | `integral(...)` | the integrand times an antiderivative in `sqrt(sin(x))` over `sin(x)^(1/2) cos(x)^(-3)` |
+
### The third case of a binomial differential is right for a negative `x` too
**Answers where there were none.** Chebyshev's third case, `x^m (a + b x^n)^(p/q)` with
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index d7b12536b..17007530f 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -8795,6 +8795,136 @@ private static bool TryReadAHomogeneousTrigonometricPolynomial(
return true;
}
+ ///
+ /// A product of powers of the trigonometric functions of one argument, some of them not
+ /// whole, which is sin(z)^M cos(z)^N times a constant on every interval where both
+ /// are continuous: with M + N even integrated as
+ /// tan(z)^M (1 + tan(z)^2)^(-(M + N)/2), and with M or N an odd whole
+ /// number as the plain product, Chebyshev's other two cases.
+ ///
+ ///
+ ///
+ /// sqrt(c sin(x))/sqrt(d cos(x)) is sqrt(c/d) sqrt(tan(x)) wherever both
+ /// roots are real, and was declined, with the rest of such products the corpus has, a
+ /// constant inside a root among them: (d csc(x))^(3/2) sqrt(c sec(x)),
+ /// (a sin(x))^(5/2) sqrt(b sec(x)), (d sec(x))^(5/2) sqrt(b tan(x)). Each is
+ /// a power of the sine times a power of the cosine, and with the exponents adding up to an
+ /// even number it is a function of the tangent: sin^M cos^N is tan^M cos^(M + N),
+ /// and cos^(M + N) is (1 + tan^2)^(-(M + N)/2) exactly. Rubi's 4.1.0 to
+ /// 4.6.0.
+ ///
+ ///
+ /// The constant is not written: the answer is the integrand times the antiderivative of the
+ /// tangent's form over that form, a quotient whose logarithmic derivative is zero -- both
+ /// halves have (M cot(z) - N tan(z)) z' -- so it is constant wherever it is
+ /// continuous, and the answer holds on every interval where the integrand is, the constants
+ /// inside the roots whatever their signs.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ ///
+ internal static Entity? SolveAProductOfPowersOfTrigonometricFunctionsThroughTheTangent(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!expr.Nodes.Any(node => node is Powf(var radicand, Number.Rational fraction) && fraction is not Number.Integer && radicand.ContainsNode(x)))
+ return null;
+ Entity? argument = null;
+ Number.Rational sine = Number.Integer.Zero;
+ Number.Rational cosine = Number.Integer.Zero;
+ var notWhole = false;
+ Entity constant = Number.Integer.One;
+ Entity varying = Number.Integer.One;
+ foreach (var (factor, underneath) in ThroughWholePowersOfProducts(FactorsOfTheIntegrand(expr)))
+ {
+ if (!factor.ContainsNode(x))
+ {
+ constant = underneath ? constant / factor : constant * factor;
+ continue;
+ }
+ varying = underneath ? varying / factor : varying * factor;
+ 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)
+ return null;
+ var (functionOf, onSine, onCosine) = read.Value;
+ argument = functionOf;
+ sine = (Number.Rational)(sine + power * onSine);
+ cosine = (Number.Rational)(cosine + power * onCosine);
+ }
+ if (argument is null || !notWhole
+ || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || TreeAnalyzer.IsZero(slope))
+ return null;
+ Entity form;
+ if ((sine + cosine) is Number.Integer { EInteger: var total } && total.IsEven && total.CanFitInInt32())
+ {
+ // The exponents add up to an even number: a function of the tangent.
+ var tangent = MathS.Tan(argument);
+ var half = -total.ToInt32Checked() / 2;
+ form = half == 0
+ ? MathS.Pow(tangent, sine)
+ : MathS.Pow(tangent, sine) * MathS.Pow(1 + MathS.Sqr(tangent), half);
+ }
+ else if (sine is Number.Integer { EInteger: var m } && !m.IsEven || cosine is Number.Integer { EInteger: var n } && !n.IsEven)
+ // An odd whole power of one of them: the plain product, which the substitution by
+ // the other answers, `(d sec(x))^(5/2) sqrt(b tan(x))` being `sin^(1/2) cos^(-3)`
+ // times a constant.
+ form = (sine is Number.Integer { IsZero: true } ? Number.Integer.One : MathS.Pow(MathS.Sin(argument), sine))
+ * (cosine is Number.Integer { IsZero: true } ? Number.Integer.One : MathS.Pow(MathS.Cos(argument), cosine));
+ else
+ return null;
+ form = form.InnerSimplified;
+ // Already the form, and then the substitution's to answer as it stands.
+ if (varying == form || varying.InnerSimplified == form)
+ return null;
+ if (Integration.ComputeAsTheSameQuestion(form, x, integrateByParts) is not { } answer
+ || answer.Nodes.Any(node => node == MathS.NaN))
+ return null;
+ return constant * varying * answer / form;
+ }
+
+
+ ///
+ /// 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)))
+ /// comes back from the polynomial term's c/g as (cos(x)^(7/2) sqrt(sin(x)))^(-1),
+ /// one factor that is no function's power, and the half-angle substitution searched it for
+ /// twenty seconds before the tangent's form was reached.
+ ///
+ private static IEnumerable<(Entity Factor, bool Underneath)> ThroughWholePowersOfProducts(IEnumerable<(Entity Factor, bool Underneath)> factors)
+ {
+ foreach (var (factor, underneath) in factors)
+ {
+ if (factor is not Powf(Mulf product, Number.Integer { EInteger: var power }) || power.IsZero)
+ {
+ yield return (factor, underneath);
+ continue;
+ }
+ var magnitude = Number.Integer.Create(power.Abs());
+ foreach (var child in Mulf.LinearChildren(product))
+ yield return (magnitude == Number.Integer.One ? child : MathS.Pow(child, magnitude), power.Sign < 0 ? !underneath : underneath);
+ }
+ }
+
///
/// A power of the cotangent that is not whole below the bar of an integrand with the
/// tangent of the same argument in it, written as a power of the tangent above it:
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 2f968045c..68d0c3a12 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -901,6 +901,10 @@ 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 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.
+ if ((answer = IndefiniteIntegralSolver.SolveAProductOfPowersOfTrigonometricFunctionsThroughTheTangent(expr, x, integrateByParts)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveByTangentSubstitution(expr, x, integrateByParts)) is { }) return answer;
// A root of a quadratic in the tangent with a linear term, rotated until it has
// none: `1/sqrt(a + b tan(x) + c tan(x)^2)` is `1/sqrt(A + C tan(y)^2)` under
diff --git a/Sources/Tests/UnitTests/Calculus/ProductOfTrigonometricPowersIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ProductOfTrigonometricPowersIntegralTest.cs
new file mode 100644
index 000000000..844c7e5ad
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ProductOfTrigonometricPowersIntegralTest.cs
@@ -0,0 +1,58 @@
+//
+// 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 product of powers of the trigonometric functions of one argument, some not whole, which
+ /// is sin^M cos^N times a constant on every interval where both are continuous, with
+ /// M + N even, the tangent's form tan^M (1 + tan^2)^(-(M + N)/2), or with
+ /// M or N an odd whole number, the plain product. Compared as complex numbers
+ /// on both sides of the zeros, where the integrands leave the reals and the constant is what
+ /// keeps the answer right. Rubi's 4.1.0 to 4.6.0.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ProductOfTrigonometricPowersIntegralTest
+ {
+ [Theory]
+ [InlineData("sqrt(c*sin(x))/sqrt(d*cos(x))")]
+ [InlineData("(d*csc(x))^(3/2)*sqrt(c*sec(x))")]
+ [InlineData("(a*sin(x))^(5/2)*sqrt(b*sec(x))")]
+ [InlineData("(b*tan(x)^3)^(3/2)")]
+ [InlineData("sqrt(c*sin(a + b*x))/(d*cos(a + b*x))^(9/2)")]
+ [InlineData("cos(x)^(7/2)/sin(x)^(7/2)")]
+ [InlineData("(d*sec(x))^(5/2)*sqrt(b*tan(x))")]
+ [InlineData("sqrt(b*tan(x))/(a*sin(x))^(3/2)")]
+ public void AsPowersOfTheSineAndTheCosine(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ Assert.DoesNotContain("integral(", integral.Stringize());
+ Assert.DoesNotContain("NaN", integral.Stringize());
+ Entity Pinned(Entity e) => e.Substitute("a", 0.4).Substitute("b", 1.1).Substitute("c", 1.3).Substitute("d", 0.8);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -2.6, -1.1, -0.4, 0.3, 0.8, 1.2, 2.0 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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}");
+ }
+ Assert.True(compared >= 6, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}