From 8bc26db401c979428fe892a2559df68208ed5b06 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Wed, 7 Oct 2026 23:13:51 +0000 Subject: [PATCH] e^(i arctan(L)) beside a function of x is written as two powers of linears For a real L, e^(+-i arctan(L)) is (1 + i L)^(+-1/2) (1 - i L)^(-+1/2): powers of two linears, where (1 + i L)/sqrt(1 + L^2) is a root of a quadratic. Part of #718. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 15 ++++++ .../Integration/IndefiniteIntegralSolver.cs | 19 +++++++ ...lOfAnArctangentBesideAPowerIntegralTest.cs | 52 +++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerIntegralTest.cs diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index fd7d5df4d..b4ecc7c5c 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -240,6 +240,21 @@ integrals with `0` ([#1769](https://github.com/asc-community/AngouriMath/issues/ | `"e^(-160)".ToEntity().InnerSimplified` | `0` | `e ^ (-160)` | | `"x^n*((1 - d^2)/x - x)^3*(1 + x^2 - d*x)/(x - d)/pi^2".ToEntity().Simplify()` | `0 provided not x - d = 0 and ...` | `x ^ n * ((1 - d ^ 2) / x - x) ^ 3 * (1 - d * x + x ^ 2) / (pi ^ 2 * (x - d))` | +### `e^(i arctan(L))` beside a function of `x` is written as two powers of linears + +**Answers where there were none, and shorter ones.** `1/(e^(i arctan(a + b x)) x^2)` ran past thirty +seconds: `e^(i arctan(L))` was written `(1 + i L)/sqrt(1 + L^2)`, a root of a quadratic beside the pole. +It is `(1 + i L)^(1/2) (1 - i L)^(-1/2)` as well, for a real `L` -- the two principal powers' arguments are +`arctan(L)/2` each and their moduli cancel -- powers of two linears, and beside a function of `x` it is +written so. Of Rubi's 5.3.6, four integrals are answered that were not, and the 28 answers that change are +a ninth of the length together ([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"1/(e^(i*atan(a + b*x))*x^2)".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 340 characters | +| `"x^3/e^(i*atan(a + b*x))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 373 characters | +| `"x^3/e^(i*atan(a*x))".ToEntity().Integrate("x")` | `integral(...)`; 25,944 characters on the unreleased master | 218 characters | + ### The floor and the ceiling of a rational are exact whatever its size **Wrong answers fixed.** The floor and the ceiling of an exact rational went through a decimal at the diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 06b630a3a..091e18f69 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -8549,6 +8549,11 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer && internal static Entity? SolveByWritingAnExponentialOfAnInverseAlgebraically(Entity expr, Entity.Variable x, bool integrateByParts) { var rewrote = false; + // A power of a quadratic in x outside the exponentials, which `(1 + L^2)^(-n/2)` meets. + var quadraticElsewhere = expr.Nodes.Any(node => node is Powf(var q, _) && q.ContainsNode(x) && !q.Nodes.Any(inner => inner is Arctanf or Arcsinf or Arccosf) + && TreeAnalyzer.TryGetPolyQuadratic(q, x, out var leading, out _, out _) && !TreeAnalyzer.IsZero(leading)); + // The variable outside the exponentials. + var xElsewhere = expr.Replace(node => node is Powf(var e, var power) && e == MathS.e && power.ContainsNode(x) ? Number.Integer.One : node).ContainsNode(x); var rewritten = expr.Replace(node => { if (node is not Powf(var @base, var exponent) || @base != MathS.e || !exponent.ContainsNode(x)) @@ -8583,6 +8588,20 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer && // of different orders that nothing reads. if (inverse is Arctanf) { + // `(1 + i L)^(n/2) (1 - i L)^(-n/2)`: the two principal powers' arguments are + // `(n/2) arctan(L)` each and their moduli cancel, for a real L, so it is + // `e^(n i arctan(L))` exactly, powers of two linears, which the rules for a + // product of linear powers read. For n = 1 or -1 beside a function of x and no + // power of a quadratic in x for the other form to meet: `e^(i arctan(a + b x))/x^2` + // was `(1 + i L)/sqrt(1 + L^2)` over `x^2`, a root of a quadratic beside a pole, and + // ran past thirty seconds, and `x^3 e^(-i arctan(a x))` came out in 26 thousand + // characters where so it is 218. Alone the exponential is answered shorter as the + // root, and for a power not whole the quotient's power is. + if ((n == Number.Integer.One || n == Number.Integer.MinusOne) && !quadraticElsewhere && xElsewhere) + { + var half = Number.Rational.Create(n.ERational.Divide(2)); + return MathS.Pow(1 + MathS.i * argument, half) * MathS.Pow(1 - MathS.i * argument, Number.Rational.Create(half.ERational.Negate())); + } var halfPower = Number.Rational.Create(n.ERational.Negate().Divide(2)); if (n is not Number.Integer) return MathS.Pow((1 + MathS.i * argument) / (1 - MathS.i * argument), Number.Rational.Create(n.ERational.Divide(2))); diff --git a/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerIntegralTest.cs new file mode 100644 index 000000000..45a6dcf52 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerIntegralTest.cs @@ -0,0 +1,52 @@ +// +// 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 +{ + /// + /// e^(+-i arctan(L)) beside a function of x, written as (1 + i L)^(+-1/2) (1 - i L)^(-+1/2): + /// the two principal powers' arguments are arctan(L)/2 each and their moduli cancel, for a real + /// L. Rubi's 5.3.6. The integrands are complex for a real x and are compared as complex numbers. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class AnExponentialOfAnArctangentBesideAPowerIntegralTest + { + [Theory] + [InlineData("1/(e^(i*atan(a + b*x))*x^2)")] + [InlineData("x^3/e^(i*atan(a + b*x))")] + [InlineData("x^3/e^(i*atan(a*x))")] + [InlineData("e^(i*atan(a*x))*x^2")] + public void AsTwoPowersOfLinears(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("b", 0.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}"); + } + } +}