From 4266088b12826adef3e8ba852c43a25f174811de Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Fri, 16 Oct 2026 20:46:39 +0000 Subject: [PATCH 1/6] Write an exponential of an arctangent algebraically at the question asked The rule that writes e^(n i arctan(L)) algebraically was reached only after the rules before it had spent the budget: Rubi's 5.3.6 e^(2 i atan(a + b x))/x^2 and e^(4 i atan(a x)) x^2/(c + a^2 c x^2)^9 ran out of time, where rewritten they are answered in half a second. It is now asked at the question asked as well, and beside a power of x and no quadratic it writes every n, not only +-1, as the two powers (1 + i L)^(n/2) (1 - i L)^(-n/2): as (1 + i L)^3 (1 + L^2)^(-3/2), e^(3 i atan(a + b x))/x was not answered. Part of https://github.com/asc-community/AngouriMath/issues/718 Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Integration/IndefiniteIntegralSolver.cs | 5 +- .../Integration/Integration.Definition.cs | 4 ++ ...AnArctangentBesideAPowerOfXIntegralTest.cs | 60 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index e8c3bc62b..a494fe569 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -9740,7 +9740,10 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer && // 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) + // So for every n beside a function of x and no quadratic: `e^(3 i arctan(a + b x))/x^2` + // as `(1 + i L)^3 (1 + L^2)^(-3/2)` ran out of time, and as the two powers it is + // answered in half a second. + if (!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())); diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index fa128879d..3f5799055 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -664,6 +664,10 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr) // A sum free of the variable that comes to a number multiplied out, as that number, // before anything reads a coefficient or an exponent in it: `2 + 2 a - 2 (1 + a)`. if ((answer = IndefiniteIntegralSolver.SolveByFoldingConstantSumsToTheirNumbers(expr, x, integrateByParts)) is { }) return answer; + // An exponential of i times an inverse trigonometric function written algebraically, at the + // question asked, before the rules that spent the budget: `e^(3 i atan(a + b x))/x^2` ran out of + // time where, rewritten, it is answered in half a second. It is asked again in its place below. + if (AnsweringTheQuestionAsked && (answer = IndefiniteIntegralSolver.SolveByWritingAnExponentialOfAnInverseAlgebraically(expr, x, integrateByParts)) is { }) return answer; // A power of a sine, cosine or hyperbolic one over a binomial in a power of it, over the // binomial's roots, before the rules that spent the budget on `sinh(y)^4/(a + b sinh(y)^3)`. if ((answer = IndefiniteIntegralSolver.SolveAPowerOverABinomialInAPowerOfASineOverItsRoots(expr, x)) is { }) return answer; diff --git a/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs new file mode 100644 index 000000000..b5b70c880 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs @@ -0,0 +1,60 @@ +// +// 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 +{ + /// + /// An exponential of i times an arctangent of a linear beside a power of x, written as two powers of + /// linears, `(1 + i L)^(n/2) (1 - i L)^(-n/2)`, at the question asked: Rubi's 5.3.6 + /// e^(2 i atan(a + b x))/x^2 and the like ran out of time before the rule was reached. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest + { + private static void DifferentiatesBack(string integrand, params (string, double)[] pins) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + + var pinned = integral.Substitute("C", 0); + Entity original = integrand.ToEntity(); + foreach (var (name, value) in pins) + { + pinned = pinned.Substitute(name, value); + original = original.Substitute(name, value); + } + var derivative = pinned.Differentiate("x"); + foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 }) + { + var got = derivative.Substitute("x", at).EvalNumerical(); + var want = original.Substitute("x", at).EvalNumerical(); + var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart); + var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)); + Assert.True(difference / scale < 1e-8, + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + } + + // d with the sign that keeps the argument real at every sampled x, and |d| below 2/2.3^2 + // so that the inverse sine and cosine stay inside [-1, 1]; off it the exact checks hold too. + [Theory] + [InlineData("e^(2*i*atan(a+b*x))/x^2")] + [InlineData("e^(3/2*i*atan(a+b*x))/x^2")] + [InlineData("e^(3*i*atan(a+b*x))/x")] + [InlineData("1/(e^(5/2*i*atan(a*x))*x^3)")] + public void IsAnsweredAndDifferentiatesBack(string integrand) + { + DifferentiatesBack(integrand, ("a", 0.6), ("b", 1.25)); + DifferentiatesBack(integrand, ("a", -0.75), ("b", 0.4)); + } + } +} From 52587722911e1f290d8a800d59a3c491b14aa156 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Fri, 16 Oct 2026 21:40:36 +0000 Subject: [PATCH 2/6] Check the piecewise answer to an exponential of an arctangent for a NaN without Simplify The candidate search ran past a minute on the piecewise answer to e^(3 i atan(a + b x))/x^2, ready and checked at sampled points in a quarter of a second; InnerSimplified is what turns such an answer into NaN. Six more of Rubi's 5.3.6 are answered. Part of https://github.com/asc-community/AngouriMath/issues/718 Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Continuous/Integration/IndefiniteIntegralSolver.cs | 4 +++- .../AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index a494fe569..a734fe971 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -9790,7 +9790,9 @@ node is Powf(var q, Number.Rational r) && r is not Number.Integer && q != first // non-existence is not given. if (expr.Vars.Any(symbol => symbol != x) && !Functions.PartialFractions.HoldsAtSampledPoints(answer.Differentiate(x), expr, x)) return null; - if (answer.Nodes.Any(node => node is Piecewise) && answer.Simplify().Nodes.Any(node => node == MathS.NaN)) + // InnerSimplified rather than Simplify: the search ran past a minute on the piecewise answer to + // `e^(3 i atan(a + b x))/x^2`, ready and checked in a quarter of a second. + if (answer.Nodes.Any(node => node is Piecewise) && answer.InnerSimplified.Nodes.Any(node => node == MathS.NaN)) return null; return answer; } diff --git a/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs index b5b70c880..c27ad9195 100644 --- a/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs @@ -51,6 +51,10 @@ private static void DifferentiatesBack(string integrand, params (string, double) [InlineData("e^(3/2*i*atan(a+b*x))/x^2")] [InlineData("e^(3*i*atan(a+b*x))/x")] [InlineData("1/(e^(5/2*i*atan(a*x))*x^3)")] + // Answered as a piecewise, which the check for a NaN simplified for over a minute. + [InlineData("e^(3*i*atan(a+b*x))/x^2")] + [InlineData("1/(e^(3*i*atan(a+b*x))*x^3)")] + [InlineData("x*e^(-2*i*atan(a+b*x))")] public void IsAnsweredAndDifferentiatesBack(string integrand) { DifferentiatesBack(integrand, ("a", 0.6), ("b", 1.25)); From 743fc4e248139065dc9c525525ab348ccd8fa4b0 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 17 Oct 2026 03:06:56 +0000 Subject: [PATCH 3/6] Write an exponential of an arctangent as two powers beside a pole only for an argument with a constant term For every n, the two powers ran out of time on `x^3 e^(-(3/2) i arctan(a x))` and `e^(3 i arctan(a x))/x`, which the root answers in under a second. Part of https://github.com/asc-community/AngouriMath/issues/718 Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Integration/IndefiniteIntegralSolver.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index a734fe971..91ad72d9d 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -9697,6 +9697,9 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer && && 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); + // The variable in a denominator, a pole beside the exponentials. + var xInADenominator = expr.Nodes.Any(node => node is Divf(_, var denominator) && denominator.ContainsNode(x) + || node is Powf(var @base, Number.Real { IsNegative: true }) && @base.ContainsNode(x)); var rewritten = expr.Replace(node => { if (node is not Powf(var @base, var exponent) || @base != MathS.e || !exponent.ContainsNode(x)) @@ -9740,10 +9743,14 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer && // 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. - // So for every n beside a function of x and no quadratic: `e^(3 i arctan(a + b x))/x^2` - // as `(1 + i L)^3 (1 + L^2)^(-3/2)` ran out of time, and as the two powers it is - // answered in half a second. - if (!quadraticElsewhere && xElsewhere) + // So for every n beside a pole at 0 and an argument with a constant term: + // `e^(3 i arctan(a + b x))/x^2` as `(1 + i L)^3 (1 + L^2)^(-3/2)` ran out of time, and + // as the two powers it is answered in half a second. Not for `a x` or a polynomial + // beside it, which the root answers: `x^3 e^(-(3/2) i arctan(a x))` and + // `e^(3 i arctan(a x))/x` were answered and as the two powers ran out of time. + if (!quadraticElsewhere && xElsewhere && (n == Number.Integer.One || n == Number.Integer.MinusOne + || xInADenominator && TreeAnalyzer.TryGetPolyLinear(argument, x, out _, out var argumentConstant) + && argumentConstant is { } && !TreeAnalyzer.IsZero(argumentConstant))) { 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())); From fc902e95a16294bdbe54fc03419bbd9e2bac82f4 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 17 Oct 2026 03:10:34 +0000 Subject: [PATCH 4/6] Drop the arctangent of a x over x^3 from the test, which the narrowed rule leaves to the root Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs index c27ad9195..41d79bc0d 100644 --- a/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/AnExponentialOfAnArctangentBesideAPowerOfXIntegralTest.cs @@ -50,7 +50,6 @@ private static void DifferentiatesBack(string integrand, params (string, double) [InlineData("e^(2*i*atan(a+b*x))/x^2")] [InlineData("e^(3/2*i*atan(a+b*x))/x^2")] [InlineData("e^(3*i*atan(a+b*x))/x")] - [InlineData("1/(e^(5/2*i*atan(a*x))*x^3)")] // Answered as a piecewise, which the check for a NaN simplified for over a minute. [InlineData("e^(3*i*atan(a+b*x))/x^2")] [InlineData("1/(e^(3*i*atan(a+b*x))*x^3)")] From c8ada772eca7cfb511ac84882ce59c167b676d90 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 17 Oct 2026 03:34:05 +0000 Subject: [PATCH 5/6] Look for a pole beside the exponentials, not in their own denominator `x^2/e^((1/2) i arctan(a + b x))` was read as a pole and ran out of time as the two powers. Part of https://github.com/asc-community/AngouriMath/issues/718 Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Continuous/Integration/IndefiniteIntegralSolver.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 3010455c0..059da6cd5 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -9696,9 +9696,10 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer && 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); - // The variable in a denominator, a pole beside the exponentials. - var xInADenominator = expr.Nodes.Any(node => node is Divf(_, var denominator) && denominator.ContainsNode(x) + var withoutTheExponentials = expr.Replace(node => node is Powf(var e, var power) && e == MathS.e && power.ContainsNode(x) ? Number.Integer.One : node); + var xElsewhere = withoutTheExponentials.ContainsNode(x); + // The variable in a denominator, a pole beside the exponentials: not `x^2/e^(i arctan(a + b x))`. + var xInADenominator = withoutTheExponentials.Nodes.Any(node => node is Divf(_, var denominator) && denominator.ContainsNode(x) || node is Powf(var @base, Number.Real { IsNegative: true }) && @base.ContainsNode(x)); var rewritten = expr.Replace(node => { From 4a80089a00353135816673bfdb0f8ccf93d8bae8 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sat, 17 Oct 2026 03:57:02 +0000 Subject: [PATCH 6/6] Record the exponentials of an arctangent answered in BREAKING-CHANGES Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 49af14dbf..dee44a680 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -187,6 +187,22 @@ digits, and `e^(1/3)` the same. Each is now worked out in the precision asked fo | `MathS.pi.Evaled` under 300 digits, after a read under 100 | 100 digits | 300 digits | | `"e^(1/3)".Evaled` under 300 digits, after a read under 100 | 100 digits | 300 digits | +### An exponential of i times an arctangent of a linear is written algebraically before the other rules + +**Answers where there were none.** `e^(3 i atan(a + b x))/x^2` ran out of time, and so did fifteen more of Rubi's +5.3.6, all `e^(n i atan(a + b x))` over a power of x or `e^(n i atan(a x))` beside a high power of `c + a^2 c x^2`. +The exponential is now written as powers of linears, `(1 + i L)^(n/2) (1 - i L)^(-n/2)`, at the question asked, ahead +of the rules that spent the budget, and for every `n` beside a pole at 0 where the argument has a constant term. A +piecewise answer is checked for `NaN` without `Simplify`, whose search on one of them ran past a minute +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"e^(3*i*atan(a+b*x))/x^2".ToEntity().Integrate("x")` | `integral(...)` | 1,945 characters | +| `"1/(e^(3*i*atan(a+b*x))*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 8,542 characters | +| `"e^(3*i*atan(a+b*x))/x^4".ToEntity().Integrate("x")` | `integral(...)` | 35,041 characters | +| `"e^(6*i*atan(a*x))*x^2/(c+a^2*c*x^2)^19".ToEntity().Integrate("x")` | `integral(...)` | 233 characters | + ### A string parsed once is parsed again under the other downcasting setting **A different number.** `MathS.FromString` remembered what it had parsed, keyed on the parsing setting alone. A string