diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 422854f4c..eeb6537f8 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -505,6 +505,21 @@ two more of Rubi's 4.5.2.3. The two sums multiply to `-a c tan^2`, so in `u = a
| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*(c - c*sec(e + f*x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | 469 characters |
| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*sqrt(c - c*sec(e + f*x))".ToEntity().Integrate("x")` | `integral(...)` | 268 characters |
+### Powers of `a + a sec` and `c - c sec` that add to a negative whole number are integrated in the first sum
+
+**Answers where there were none.** `sec(e + f x) (a + a sec(e + f x))^m (c - c sec(e + f x))^(-1 - m)` was declined,
+with two more of Rubi's 4.5.2.3. When the two powers add to `-k`, a whole number up to 12, the integral is the
+integrand over the derivative of `u = a + a sec` times a polynomial in `u` and `a - a sec`, of degree `k + 1`, for a
+symbolic `m` too, and the cosecant's is the same
+([#718](https://github.com/asc-community/AngouriMath/issues/718), PR
+[#1861](https://github.com/asc-community/AngouriMath/pull/1861)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*(c - c*sec(e + f*x))^(-1 - m)".ToEntity().Integrate("x")` | `integral(...)` | 245 characters |
+| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*(c - c*sec(e + f*x))^(-3 - m)".ToEntity().Integrate("x")` | `integral(...)` | 402 characters |
+| `"csc(x)*(2 + 2*csc(x))^m*(3 - 3*csc(x))^(-2 - m)".ToEntity().Integrate("x")` | `integral(...)` | 233 characters |
+
### A secant and a tangent in one sum below the bar are integrated in the half angle
**Answers where there were none.** `1/(a + c sec(x) + b tan(x))` was declined, and so was
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 2aedd1ee0..7bd633b26 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -5223,7 +5223,7 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
var order = sums[0].Exponent is Number.Rational { ERational.Sign: > 0 } && IsHalfOdd(sums[0].Exponent) ? new[] { 0, 1 } : new[] { 1, 0 };
var (half, other) = (sums[order[0]], sums[order[1]]);
if (!IsHalfOdd(half.Exponent))
- return null;
+ return WherePowersOfConjugateSumsAddToANegativeWhole(expr, x, function, sums[0], sums[1]);
var n = ((Number.Rational)half.Exponent).ERational;
// With u the other sum, the half-odd one is c (2 - u/a), whatever the signs.
var u = Variable.CreateUnique(expr, "u_conjugate");
@@ -5246,6 +5246,45 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
return expr * inTermsOfU.Substitute(u, other.Radicand) / inX;
}
+ ///
+ /// sec(y) (a ± a sec(y))^m (c ∓ c sec(y))^n with m + n a negative whole
+ /// number -k and neither half-odd: with u = a ± a sec(y), the antiderivative is
+ /// the integrand over u' times the polynomial
+ /// (2a)^(-k) Σ_j C(k - 1, j) u^(j + 1) (2a - u)^(k - j)/(m + j + 1/2), for any m.
+ ///
+ ///
+ /// Rubi's sec(e + f x) (a + a sec(e + f x))^m (c - c sec(e + f x))^(-1 - m) and its
+ /// -2 - m and -3 - m, 4.5.2.3, were declined. In u the integrand is
+ /// u^(m - 1/2) (2 - u/a)^(n - 1/2) up to a constant, which is v^(m - 1/2) (1 + v)^(k - 1)
+ /// in v = u/(2a - u), a finite sum; written back, the powers of u and
+ /// 2a - u the integrand has divide out and leave the polynomial. For k = 1 that is
+ /// -tan(y) (a + a sec(y))^m (c - c sec(y))^(-1 - m)/(2m + 1). Kept only where it
+ /// differentiates back to the integrand at the sampled points.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ private static Entity? WherePowersOfConjugateSumsAddToANegativeWhole(Entity expr, Entity.Variable x, Entity function,
+ (Entity Radicand, Entity Exponent, Entity A, bool Plus) first, (Entity Radicand, Entity Exponent, Entity A, bool Plus) second)
+ {
+ // Simplified, not InnerSimplified, which leaves `m + -1 - m` as it is.
+ if ((first.Exponent + second.Exponent).Simplify() is not Number.Integer { EInteger.Sign: < 0 } total
+ || total.EInteger.CompareTo(EInteger.FromInt32(-12)) < 0)
+ return null;
+ var k = -total.EInteger.ToInt32Checked();
+ var (u, a) = (first.Radicand, first.A);
+ // `2a - u`, written as the conjugate sum: `a - a sec(y)` for `u = a + a sec(y)`.
+ var conjugate = first.Plus ? a - a * function : a + a * function;
+ Entity polynomial = Number.Integer.Zero;
+ var binomial = EInteger.One;
+ for (var j = 0; j < k; j++)
+ {
+ polynomial += Number.Integer.Create(binomial) * MathS.Pow(u, j + 1) * MathS.Pow(conjugate, k - j)
+ / (first.Exponent + j + Number.Rational.Create(1, 2)).Simplify();
+ binomial = binomial.Multiply(EInteger.FromInt32(k - 1 - j)).Divide(EInteger.FromInt32(j + 1));
+ }
+ var antiderivative = expr / u.Differentiate(x) * (MathS.Pow(2 * a, total) * polynomial).InnerSimplified;
+ return Functions.PartialFractions.HoldsAtSampledPoints(antiderivative.Differentiate(x), expr, x) ? antiderivative : null;
+ }
+
///
/// An odd whole power of tan(y) beside a function of sec(y), integrated in
/// u = sec(y): du = sec(y) tan(y) dy and the even power of the tangent left over
diff --git a/Sources/Tests/UnitTests/Calculus/ConjugateSecantSumsWhosePowersAddToANegativeWholeTest.cs b/Sources/Tests/UnitTests/Calculus/ConjugateSecantSumsWhosePowersAddToANegativeWholeTest.cs
new file mode 100644
index 000000000..10ca4389f
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ConjugateSecantSumsWhosePowersAddToANegativeWholeTest.cs
@@ -0,0 +1,55 @@
+//
+// 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
+{
+ ///
+ /// sec(y) (a + a sec(y))^m (c - c sec(y))^n with m + n a negative whole number,
+ /// for any m: Rubi's 4.5.2.3.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ConjugateSecantSumsWhosePowersAddToANegativeWholeTest
+ {
+ // The two sums multiply to -a c tan(y)^2, so they have one sign only where a and c do not:
+ // with a = 2 and c = -3 the first rows are real where sec(y) > 1. The rest are complex
+ // there, and are checked off the real line the same way.
+ [Theory]
+ [InlineData("sec(e+f*x)*(a+a*sec(e+f*x))^m*(c-c*sec(e+f*x))^(-1-m)", 300, true)]
+ [InlineData("sec(e+f*x)*(a+a*sec(e+f*x))^m*(c-c*sec(e+f*x))^(-2-m)", 400, true)]
+ [InlineData("sec(e+f*x)*(a+a*sec(e+f*x))^m*(c-c*sec(e+f*x))^(-3-m)", 500, true)]
+ [InlineData("sec(x)*(2+2*sec(x))^(5/3)*(3-3*sec(x))^(-8/3)", 200, false)]
+ [InlineData("sec(x)*(1-sec(x))^m*(1+sec(x))^(-1-m)", 200, false)]
+ [InlineData("csc(x)*(2+2*csc(x))^m*(3-3*csc(x))^(-2-m)", 300, false)]
+ public void DifferentiatesBack(string integrand, int atMost, bool real)
+ {
+ 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 At(Entity expression) => expression.Substitute("C", 0).Substitute("m", "1/3".ToEntity())
+ .Substitute("a", 2).Substitute("c", -3).Substitute("e", "1/5".ToEntity()).Substitute("f", "3/2".ToEntity());
+ var derivative = At(integral).Differentiate("x");
+ var original = At(integrand.ToEntity());
+ // Points where sec (or csc) of the argument is past 1: x and 1/5 + 3/2 x in (0, pi/2).
+ foreach (var at in integrand.StartsWith("csc") ? new[] { "1.1", "1.3", "1.5" } : new[] { "0.3", "0.6", "0.9" })
+ {
+ var want = original.Substitute("x", at.ToEntity()).EvalNumerical();
+ if (real)
+ Assert.True(Math.Abs((double)want.ImaginaryPart) < 1e-12, $"{integrand} is not real at x = {at}");
+ 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) + Math.Abs((double)want.ImaginaryPart)),
+ $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
+ }
+ }
+ }
+}