diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 36fbccf52..5d77674d5 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -543,6 +543,24 @@ that `sec(x)^2/(a + b sin(x))` is rational in them rather than declined at once
| `"tan(x)^4/(a + a*cos(x))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `tan(x/2)` |
| `"csc(x)^2/(a + a*cos(x))".ToEntity().Integrate("x")` | `integral(...)` | `(tan(x/2)^3/12 + tan(x/2)/2 - 1/(4 tan(x/2)))/a` |
+### A power of `a + i a tan` beside a power of the secant is integrated as an exponential
+
+**Answers where there were none.** `sqrt(a + i a tan(x))/sqrt(c sec(x))` is `sqrt(a/c) e^(i x/2)`
+wherever the cosine is positive, and was declined, with every other pair of such powers, one of them
+not whole, whose exponents add up to a whole number: Rubi's 4.3.1.2 has 43 of them. `a + i a tan(z)` is
+`a sec(z) e^(i z)` on the real line, so the integrand is a constant on every interval where it is
+continuous times a power of the secant and an exponential, which in `w = e^(i z)` is rational in a root
+of `w`. Three of the 43, `(c sec(x))^p/(a + i a tan(x))^p` for `p` a half, three halves and five, were
+answered on the unreleased master through the power of a product taken apart, wrong by a constant
+wherever the cosine is negative; they are answered with the rest now
+([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"sqrt(a + i*a*tan(x))/sqrt(c*sec(x))".ToEntity().Integrate("x")` | `integral(...)` | the integrand times `2/i`, through `e^(i x)` |
+| `"sqrt(c*sec(x))*sqrt(a + i*a*tan(x))".ToEntity().Integrate("x")` | `integral(...)` | the integrand times an antiderivative in `e^(i x/2)` over its derivative |
+| `"(c*sec(x))^(5/2)/(a + i*a*tan(x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | the integrand times a power of `e^(i x)` |
+
### A rational function with complex coefficients is integrated through its real and imaginary parts
**Answers where there were none.** `1/((1 + i x)^2 (1 + x^2))` was declined: the rational
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 2f20ae968..d7b12536b 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -23594,6 +23594,136 @@ static bool IsAWholeNumberOfSteps(ERational difference)
return Integration.ComputeAsTheSameQuestion(inExponentials.InnerSimplified, x, integrateByParts);
}
+ ///
+ /// A power of A + i A tan(z) beside a power of the secant of the same argument, one
+ /// of them not whole and the two adding up to a whole number k, integrated as the
+ /// exponential it is: A + i A tan(z) is A sec(z) e^(i z) on the real line, so
+ /// (A + i A tan(z))^n (c sec(z))^m is a constant on every interval where it is
+ /// continuous times sec(z)^k e^(i n z), which in w = e^(i z) is
+ /// (2 w/(w^2 + 1))^k w^n, and dz is dw/(i w).
+ ///
+ ///
+ ///
+ /// sqrt(a + i a tan(x))/sqrt(c sec(x)) is sqrt(a/c) e^(i x/2) wherever the
+ /// cosine is positive, and was declined, with every other power of the pair whose exponents
+ /// add up to a whole number: a root of a sum with the imaginary unit in it beside a root of the
+ /// secant is read by no rule, and e^(i x/2)/cos(x), what the positive sums leave, is
+ /// declined as well. Rubi's 4.3.1.2.
+ ///
+ ///
+ /// The constant is not written: the answer is the integrand times the antiderivative in
+ /// w over what that antiderivative differentiates back to, sec(z)^k (e^(i z))^n
+ /// times i and the slope, a quotient whose logarithmic derivative is zero -- both
+ /// halves have n (tan(z) + i) z' -- so it is constant wherever it is continuous, and
+ /// the answer holds on every interval where the integrand is.
+ /// https://github.com/asc-community/AngouriMath/issues/718
+ ///
+ ///
+ internal static Entity? SolveAPowerOfAnImaginaryTangentBesideAPowerOfTheSecant(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!expr.Nodes.Any(node => node is Secantf) || !expr.Nodes.Any(node => node is Tanf))
+ return null;
+ Entity? argument = null;
+ var plus = false;
+ Number.Rational? tangentPower = null, secantPower = null;
+ Entity constant = Number.Integer.One;
+ Entity varying = Number.Integer.One;
+ foreach (var (factor, underneath) in 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);
+ if (underneath)
+ power = (Number.Rational)(-power);
+ if (TryReadAnImaginaryTangent(@base, x, out var tangentOf, out var isPlus))
+ {
+ if (tangentPower is not null || argument is not null && argument != tangentOf)
+ return null;
+ (tangentPower, argument, plus) = (power, tangentOf, isPlus);
+ continue;
+ }
+ var secant = @base switch
+ {
+ Secantf => @base,
+ Mulf(var left, Secantf right) when !left.ContainsNode(x) => right,
+ Mulf(Secantf left, var right) when !right.ContainsNode(x) => left,
+ _ => null
+ };
+ if (secant is not Secantf(var secantOf) || secantPower is not null || argument is not null && argument != secantOf)
+ return null;
+ (secantPower, argument) = (power, secantOf);
+ }
+ if (tangentPower is null || secantPower is null || argument is null
+ || tangentPower is Number.Integer && secantPower is Number.Integer
+ || (tangentPower + secantPower) is not Number.Integer { EInteger: var whole } || !whole.CanFitInInt32()
+ || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || TreeAnalyzer.IsZero(slope))
+ return null;
+ var k = whole.ToInt32Checked();
+ var phase = plus ? tangentPower : (Number.Rational)(-tangentPower);
+ // In w = e^(i z): sec(z)^k e^(i n z) dz is (2 w)^k (w^2 + 1)^(-k) w^(n - 1) dw / i.
+ var w = Variable.CreateUnique(expr, "w_exp");
+ var inW = k >= 0
+ ? MathS.Pow(2, k) * MathS.Pow(w, (phase + k - 1).InnerSimplified) / MathS.Pow(MathS.Sqr(w) + 1, k)
+ : MathS.Pow(2, k) * MathS.Pow(MathS.Sqr(w) + 1, -k) * MathS.Pow(w, (phase + k - 1).InnerSimplified);
+ if (Integration.ComputeAsAQuestionOfItsOwn(inW.InnerSimplified, w, integrateByParts) is not { } inWAnswer
+ || inWAnswer.Nodes.Any(node => node == MathS.NaN))
+ return null;
+ var exponential = MathS.Pow(MathS.e, MathS.i * argument);
+ var differentiatesBackTo = MathS.Pow(MathS.Sec(argument), k) * MathS.Pow(exponential, phase);
+ return constant * varying * inWAnswer.Substitute(w, exponential) / (MathS.i * slope * differentiatesBackTo);
+ }
+
+ ///
+ /// A + i A tan(z) or A - i A tan(z), with a constant A: the argument,
+ /// and whether the imaginary unit comes with a plus.
+ ///
+ private static bool TryReadAnImaginaryTangent(Entity sum, Entity.Variable x, out Entity argument, out bool plus)
+ {
+ (argument, plus) = (Number.Integer.Zero, false);
+ if (sum is not Sumf and not Minusf || !sum.ContainsNode(x))
+ return false;
+ Entity free = Number.Integer.Zero;
+ Entity? coefficient = null;
+ Entity? tangentOf = null;
+ foreach (var term in Sumf.LinearChildren(sum))
+ {
+ if (!term.ContainsNode(x))
+ {
+ free += term;
+ continue;
+ }
+ if (coefficient is not null)
+ return false;
+ Entity factors = Number.Integer.One;
+ foreach (var factor in Mulf.LinearChildren(term))
+ if (factor is Tanf(var inner) && tangentOf is null)
+ tangentOf = inner;
+ else if (factor.ContainsNode(x))
+ return false;
+ else
+ factors *= factor;
+ if (tangentOf is null)
+ return false;
+ coefficient = factors;
+ }
+ if (coefficient is null || tangentOf is null || free == Number.Integer.Zero)
+ return false;
+ var ratio = Functions.PartialFractions.Bare((coefficient / free).InnerSimplified);
+ if (ratio.Evaled is not Number.Complex)
+ ratio = Functions.PartialFractions.Bare(ratio.Simplify());
+ plus = ratio.Evaled == MathS.i.Evaled;
+ if (!plus && ratio.Evaled != (-MathS.i).Evaled)
+ return false;
+ argument = tangentOf;
+ return true;
+ }
+
///
/// A cos(y) + i A sin(y) below the bar, written as the exponential it is:
/// A e^(i y), and A cos(y) - i A sin(y) as A e^(-i y). Beside a power
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 0d7d9f42d..2f968045c 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -669,6 +669,12 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
if ((answer = IndefiniteIntegralSolver.SolveAnExponentialOfAHyperbolicFunctionBesideItsDerivative(expr, x, integrateByParts)) is { }) return answer;
// `A + i A tan(z)` is `A e^(i z)/cos(z)`, which beside a polynomial is a shape the
// closed rules answer, where the imaginary unit in the coefficient is read by none.
+ // The same identity beside a power of the secant, where the powers are not whole and add
+ // up to a whole number: an exponential over a power of the cosine, in e^(i z). Before the
+ // rule below, which writes a power of the identity that is not whole as the power of a
+ // product and takes it apart, and so answers (c sec)^(5/2)/(a + i a tan)^(5/2) with the
+ // wrong constant wherever the cosine is negative.
+ if ((answer = IndefiniteIntegralSolver.SolveAPowerOfAnImaginaryTangentBesideAPowerOfTheSecant(expr, x, integrateByParts)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveByWritingAnImaginaryTangentAsAnExponential(expr, x, integrateByParts)) is { }) return answer;
// And `A cos(z) + i A sin(z)`, which is `A e^(i z)`, where no rotation is real.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAnImaginarySumOfACosineAndASineAsAnExponential(expr, x, integrateByParts)) is { }) return answer;
diff --git a/Sources/Tests/UnitTests/Calculus/ImaginaryTangentBesideTheSecantIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ImaginaryTangentBesideTheSecantIntegralTest.cs
new file mode 100644
index 000000000..f29f5865a
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/ImaginaryTangentBesideTheSecantIntegralTest.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
+{
+ ///
+ /// A power of a + i a tan(x) beside a power of the secant, one of them not whole and
+ /// the two adding up to a whole number: a + i a tan(x) is a sec(x) e^(i x) on
+ /// the real line, so the integrand is a constant on every interval where it is continuous times
+ /// a power of the secant and an exponential. Rubi's 4.3.1.2. The integrands are complex along
+ /// the real line and are compared as complex numbers, where the cosine is negative as well,
+ /// which is where the fourth row's answer on master was off by a constant factor.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class ImaginaryTangentBesideTheSecantIntegralTest
+ {
+ [Theory]
+ [InlineData("sqrt(a + i*a*tan(x))/sqrt(k*sec(x))")]
+ [InlineData("sqrt(k*sec(x))*sqrt(a + i*a*tan(x))")]
+ [InlineData("(a + i*a*tan(x))^(3/2)/(k*sec(x))^(7/2)")]
+ [InlineData("(k*sec(x))^(5/2)/(a + i*a*tan(x))^(5/2)")]
+ [InlineData("(k*sec(c + d*x))^(3/2)*(a - i*a*tan(c + d*x))^(3/2)")]
+ public void AsTheExponentialItIs(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", 1.3).Substitute("k", 0.8).Substitute("c", 0.4).Substitute("d", 1.1);
+ 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}");
+ }
+ }
+}