From 44c6bf193188b8ca2e7864b7622e746d4cbfc200 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Wed, 7 Oct 2026 05:35:20 +0000 Subject: [PATCH] a + i a tan below an odd power of the secant is written over its conjugate The sum times its conjugate is a^2 sec(z)^2, so sec(z)^s/(a + i a tan(z))^n is sec(z)^(s - 2n) (a - i a tan(z))^n/a^(2n), which the rules for the sine and the cosine answer. Part of #718. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 19 +++++++++- .../Integration/IndefiniteIntegralSolver.cs | 37 ++++++++++++++----- ...BesideAnImaginaryTangentSumIntegralTest.cs | 3 ++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 20966b9bd..be218cd26 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -365,6 +365,21 @@ quotient of two such linears the sum is `(b - d t^2)^2 + (c t^2 - a)^2`. Rubi's | `"(c + d*tan(x))^(3/2)/(a + b*tan(x))^3".ToEntity().Integrate("x")` | `integral(...)` | the same | | `"1/((a + b*tan(x))^(3/2)*(c + d*tan(x))^(3/2))".ToEntity().Integrate("x")` | `integral(...)`; past a minute on the unreleased master | in the root of the quotient of the two, in a second | +### `a + i a tan` below an odd power of the secant is written over its conjugate + +**Answers where there were none, and shorter ones.** `sec(x)^7/(a + i a tan(x))^4` was declined, with +the rest of Rubi's 4.3.1.2 that is an odd power of the secant over a whole power of `a + i a tan`. The sum +times `a - i a tan(z)` is `a^2 sec(z)^2`, so `sec(z)^s/(a + i a tan(z))^n` is +`sec(z)^(s - 2 n) (a - i a tan(z))^n/a^(2 n)`, a whole power of the conjugate beside one of the secant, +and it is integrated as that. Those integrated in `u = a + i a tan(z)` before come out a tenth as long +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"sec(x)^7/(a + i*a*tan(x))^4".ToEntity().Integrate("x")` | `integral(...)` | 371 characters | +| `"sec(x)^9/(a + i*a*tan(x))^8".ToEntity().Integrate("x")` | `integral(...)` | 745 characters | +| `"sec(x)^5/(a + i*a*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)`; 2,431 characters on the unreleased master | 225 characters | + ### An odd power of the secant over a whole power of `a + i a tan` is integrated in the sum **Answers where there were none.** `sec(x)^5/(a + i a tan(x))^2` was declined: the rule that integrates @@ -379,8 +394,8 @@ right answers were declined after seconds | Input | Was (2.5.0) | Now | |---|---|---| -| `"sec(x)^5/(a + i*a*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)` | 2,431 characters | -| `"sec(x)^7/(a + i*a*tan(x))^3".ToEntity().Integrate("x")` | `integral(...)` | 3,774 characters | +| `"sec(x)^5/(a + i*a*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)` | 225 characters | +| `"sec(x)^7/(a + i*a*tan(x))^3".ToEntity().Integrate("x")` | `integral(...)` | 289 characters | ### A power of the cosine beside a power of `a + i a tan` is read as one of the secant diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 6407188ca..97b29a3d2 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -27047,10 +27047,15 @@ static Entity SumOfThePowers(Entity first, Entity second) Entity? sumPower = null; Entity secantPower = Number.Integer.Zero; var sawSecant = false; + // Everything but the sum, for the rewrite of a whole power of it below. + Entity others = Number.Integer.One; foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr)) { if (!factor.ContainsNode(x)) + { + others = underneath ? others / factor : others * factor; continue; + } var (@base, power) = factor is Powf(var b, var p) && !p.ContainsNode(x) ? (b, p.Evaled is Number.Rational r ? r : p) : (factor, (Entity)Number.Integer.One); @@ -27077,6 +27082,7 @@ static Entity SumOfThePowers(Entity first, Entity second) if (of is null || argument is not null && argument != of) return null; argument = of; + others = underneath ? others / factor : others * factor; secantPower = sign == 1 ? secantPower + power : secantPower - power; sawSecant = true; } @@ -27084,20 +27090,33 @@ static Entity SumOfThePowers(Entity first, Entity second) || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || TreeAnalyzer.IsZero(slope)) return null; secantPower = secantPower is Number ? secantPower : secantPower.InnerSimplified; - // Whole powers on both are left to the rules for the sine and the cosine, which answer - // `cos(x)^5/(a + i a tan(x))^3` and `sec(x)^3/(a + i a tan(x))^4` in a fraction of a second - // where in u they ran past five -- but for an odd power s of the secant over the sum's n-th - // with s + 2 n = 1. Then u^n (sec(z)^2)^((s - 2)/2) is a whole power of 2 A - u over the root - // of u, which is answered in a second, and `sec(x)^5/(a + i a tan(x))^2` was declined by - // every route but this. - if (sumPower is Number.Integer { EInteger: var whole } && secantPower is Number.Integer { EInteger: var secant } - && !(secant + whole * 2).Equals(EInteger.One)) - return null; Entity constantTerm = Number.Integer.Zero; foreach (var term in Sumf.LinearChildren(sum)) if (!term.ContainsNode(x)) constantTerm += term; var a = constantTerm.InnerSimplified; + // Whole powers on both. An odd power s of the secant over the sum's n-th: the sum times its + // conjugate is A^2 sec(z)^2, so `sec(x)^7/(a + i a tan(x))^4` is + // `sec(x)^(-1) (a - i a tan(x))^4/a^8`, a whole power of the conjugate beside one of the + // secant, which the rules for the sine and the cosine answer in a fraction of a second. In u + // it ran past five, or with s + 2 n = 1 took two seconds for an answer ten times as long. + // An even power is left to those rules as written, which answer `sec(x)^4/(a + i a tan(x))^3` + // shorter than its rewrite, and so is an odd power of the cosine, `cos(x)/(a + i a tan(x))^4`, + // which they answer several times sooner; and a whole power above the bar to them too, but for + // s + 2 n = 1, where u^n (sec(z)^2)^((s - 2)/2) is a whole power of 2 A - u over the root of u. + if (sumPower is Number.Integer { EInteger: var whole } && secantPower is Number.Integer { EInteger: var secant }) + { + if (whole.Sign < 0 && secant.Sign > 0 && !secant.IsEven && whole.CanFitInInt32()) + { + var n = -whole.ToInt32Checked(); + var conjugate = a + (plus ? -MathS.i : MathS.i) * a * MathS.Tan(argument); + return Integration.ComputeAsTheSameQuestion( + (others * MathS.Pow(conjugate, n) * MathS.Pow(MathS.Sec(argument), -2 * n) / MathS.Pow(a, 2 * n)).InnerSimplified, + x, integrateByParts); + } + if (!(secant + whole * 2).Equals(EInteger.One)) + return null; + } var u = Variable.CreateUnique(expr, "u_tan"); var half = ((secantPower - 2) / 2).InnerSimplified; Entity squared = MathS.Pow(u / a, half) * MathS.Pow((2 * a - u) / a, half); diff --git a/Sources/Tests/UnitTests/Calculus/SecantBesideAnImaginaryTangentSumIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/SecantBesideAnImaginaryTangentSumIntegralTest.cs index 0e82d4345..a44ddde99 100644 --- a/Sources/Tests/UnitTests/Calculus/SecantBesideAnImaginaryTangentSumIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/SecantBesideAnImaginaryTangentSumIntegralTest.cs @@ -28,6 +28,9 @@ public sealed class SecantBesideAnImaginaryTangentSumIntegralTest [InlineData("cos(x)^9*(a + i*a*tan(x))^(7/2)")] [InlineData("(m*sec(x))^(2/3)*(a + i*a*tan(x))^(5/3)")] [InlineData("sec(x)^5/(a + i*a*tan(x))^2")] + [InlineData("sec(x)^7/(a + i*a*tan(x))^4")] + [InlineData("sec(x)^9/(a + i*a*tan(x))^8")] + [InlineData("(k*sec(x))^3/(a - i*a*tan(x))^2")] public void InTheSum(string integrand) { var integral = integrand.ToEntity().Integrate("x");