diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index b4ecc7c5c..3c6f937bd 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -208,6 +208,19 @@ after it
|---|---|---|
| `"(a*c + b*c*x)^(-3-2*p)*(f + g*x)*(a^2 + 2*a*b*x + b^2*x^2)^p".ToEntity().Integrate("x")` | `integral(...)`; an answer with no value on the unreleased master | the antiderivative |
+### `a + i a tan` of a shifted linear below the bar is integrated in the linear
+
+**Answers where there were none.** `sqrt(a + i a tan(g + f x)) (A + B tan(g + f x))/sqrt(c - i c tan(g + f x))`
+ran past thirty seconds, where with `x` for the argument it is answered in one: the rule that writes
+`a + i a tan(z)` as an exponential expanded the exponential of `i (g + f x)` and its phase into a search.
+Where every trigonometric function in the integrand is of one linear other than `x`, it integrates in
+`u = g + f x` first ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"sqrt(a + i*a*tan(g + f*x))*(A + B*tan(g + f*x))/sqrt(c - i*c*tan(g + f*x))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 448 characters |
+| `"(A + B*tan(g + f*x))/(sqrt(a + i*a*tan(g + f*x))*(c - i*c*tan(g + f*x))^(3/2))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 597 characters |
+
### A value substituted under a binder is not captured by the name it binds
**Wrong answers fixed.** An integral, a sum, a product, a derivative, a limit, a set builder and the
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 091e18f69..6eeb69950 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -26805,6 +26805,49 @@ 2 when NotWhole(sums[0].Base) != NotWhole(sums[1].Base) => NotWhole(sums[0].Base
/// handed on is the integrand in another spelling.
/// https://github.com/asc-community/AngouriMath/issues/718
///
+ ///
+ /// integrated in u = g + f x where x stands only in trigonometric
+ /// functions of that one linear, other than x itself, and a root of something in x stands: dx = du/f. Null
+ /// otherwise.
+ ///
+ private static Entity? InItsOneShiftedLinear(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ // Beside a root only: whole powers, `(a + i a tan(z))^3 (A + B tan(z))/(c - i c tan(z))^3`, the
+ // exponential's spelling answers sooner of the linear than of x.
+ if (!expr.Nodes.Any(node => node is Powf(var @base, Number.Rational power) && power is not Number.Integer && @base.ContainsNode(x)))
+ return null;
+ Entity? linear = null;
+ foreach (var node in expr.Nodes)
+ {
+ if (node is not (Tanf or Cotanf or Sinf or Cosf or Secantf or Cosecantf) || !node.ContainsNode(x))
+ continue;
+ var argument = node.DirectChildren.First();
+ if (linear is null)
+ linear = argument;
+ else if (linear != argument)
+ return null;
+ }
+ if (linear is null || !TreeAnalyzer.TryGetPolyLinear(linear, x, out var slope, out var offset)
+ || slope.ContainsNode(x) || offset.ContainsNode(x) || TreeAnalyzer.IsZero(slope) || linear == x)
+ return null;
+ var u = Variable.CreateUnique(expr, "u_shifted");
+ var inU = expr.Replace(node => node switch
+ {
+ Tanf(var a) when a == linear => MathS.Tan(u),
+ Cotanf(var a) when a == linear => MathS.Cotan(u),
+ Sinf(var a) when a == linear => MathS.Sin(u),
+ Cosf(var a) when a == linear => MathS.Cos(u),
+ Secantf(var a) when a == linear => MathS.Sec(u),
+ Cosecantf(var a) when a == linear => new Cosecantf(u),
+ _ => node,
+ });
+ if (inU.ContainsNode(x)
+ || Integration.ComputeAsAQuestionOfItsOwn((inU / slope).InnerSimplified, u, integrateByParts) is not { } answer
+ || answer.Nodes.Any(node => node == MathS.NaN))
+ return null;
+ return answer.Substitute(u, linear);
+ }
+
internal static Entity? SolveByWritingAnImaginaryTangentAsAnExponential(Entity expr, Entity.Variable x, bool integrateByParts)
{
// Below the bar only: `(A + i A tan(z))^3` above it is a polynomial in the tangent,
@@ -26872,6 +26915,12 @@ Entity AsAnExponential(Entity node)
var rewritten = below.Replace(AsAnExponential);
if (rewritten == below)
return null;
+ // One linear other than x for every argument, `g + f x`, is integrated in it: the rewriting
+ // below expands an exponential of `i (g + f x)` and its phase into a search that ran past
+ // thirty seconds for `sqrt(a + i a tan(g + f x)) (A + B tan(g + f x))/sqrt(c - i c tan(g + f x))`,
+ // which in `u = g + f x` is answered in a second.
+ if (InItsOneShiftedLinear(expr, x, integrateByParts) is { } inTheLinear)
+ return inTheLinear;
// A root of the tangent or the cotangent of that argument, or of a constant times one, is
// read as written by the substitution `t = tan(z)`, where it is a root of `t`, and by
// nothing in the exponential's spelling: `sqrt(tan(c + d x))/(a + i a tan(c + d x))`
diff --git a/Sources/Tests/UnitTests/Calculus/AnImaginaryTangentSumOfAShiftedLinearIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnImaginaryTangentSumOfAShiftedLinearIntegralTest.cs
new file mode 100644
index 000000000..e96140c65
--- /dev/null
+++ b/Sources/Tests/UnitTests/Calculus/AnImaginaryTangentSumOfAShiftedLinearIntegralTest.cs
@@ -0,0 +1,50 @@
+//
+// 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 + i a tan(g + f x) below the bar, beside other functions of the same shifted linear,
+ /// integrated in u = g + f x. Rubi's 4.3.3.1. The integrands are complex for a real x
+ /// and are compared as complex numbers.
+ /// #718
+ ///
+ [Trait("Area", "Calculus")]
+ public sealed class AnImaginaryTangentSumOfAShiftedLinearIntegralTest
+ {
+ [Theory]
+ [InlineData("sqrt(a + i*a*tan(g + f*x))*(A + B*tan(g + f*x))/sqrt(c - i*c*tan(g + f*x))")]
+ [InlineData("(A + B*tan(g + f*x))/(sqrt(a + i*a*tan(g + f*x))*(c - i*c*tan(g + f*x))^(3/2))")]
+ public void InTheLinear(string integrand)
+ {
+ var integral = integrand.ToEntity().Integrate("x");
+ var text = integral.Stringize();
+ Assert.DoesNotContain("integral(", text);
+ Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
+ Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("c", 0.7).Substitute("g", 0.4).Substitute("f", 1.1).Substitute("A", 0.9).Substitute("B", 1.7);
+ var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
+ var original = Pinned(integrand.ToEntity());
+ var compared = 0;
+ foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.9 })
+ {
+ var want = original.Substitute("x", at).EvalNumerical();
+ var got = derivative.Substitute("x", at).EvalNumerical();
+ if (want.IsNaN)
+ continue;
+ compared++;
+ 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 >= 5, $"only {compared} points could be compared for {integrand}");
+ }
+ }
+}