From 0abf838ea3b7f773e50e3ae13e1d65305d4cdfa7 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sun, 4 Oct 2026 08:34:13 +0000 Subject: [PATCH 1/2] An exponential of the reciprocal of a linear beside a power of it is integrated F^(a + b/(c + d x)) (c + d x)^2 was declined, and so were the same alone, over c + d x, and with the cube of the reciprocal in the exponent: under u = 1/(c + d x) each is an exponential beside a power of u, and the substitution was made only where the exponent is a Gaussian in u, whose moments the table answers. The reading is shared, and for any other polynomial exponent in u the question is asked in u. Rubi's 2.3. 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 | 68 ++++++++++++++++--- .../Integration/Integration.Definition.cs | 2 + .../ExponentialOfTheReciprocalIntegralTest.cs | 47 +++++++++++++ 4 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 Sources/Tests/UnitTests/Calculus/ExponentialOfTheReciprocalIntegralTest.cs diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index e9ae2c6a1..34c38c5ba 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -277,6 +277,21 @@ too. A correct antiderivative in an unhelpful form, where there was none at all. `e^(x^2)` is still declined, and correctly: its exponent is not linear and it has no elementary antiderivative. +### An exponential of the reciprocal of a linear beside a power of it is integrated + +**Answers where there were none.** `F^(a + b/(c + d x)) (c + d x)^2` was declined, and so were +`F^(a + b/(c + d x))` alone, over `c + d x`, and with the cube of the reciprocal in the exponent: +under `u = 1/(c + d x)` each is an exponential beside a power of `u`, and the substitution was made +only where the exponent is a Gaussian in `u`, whose moments the table answers. It is made for any +polynomial exponent in `u` now, and the question asked in `u`. Rubi's 2.3 +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"F^(a + b/(c + d*x))*(c + d*x)^2".ToEntity().Integrate("x")` | `integral(...)` | an exponential integral and powers of `c + d x` times the exponential | +| `"F^(a + b/(c + d*x))/(c + d*x)".ToEntity().Integrate("x")` | `integral(...)` | an exponential integral | +| `"F^(a + b/(c + d*x)^3)*(c + d*x)^2".ToEntity().Integrate("x")` | `integral(...)` | an exponential integral of the cube, and the exponential | + ### A polynomial over a power of a binomial past the cube is integrated **Answers where there were none.** `P(x)/(a + b x^n)^k` with symbols in the binomial, `n >= 3`, was diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 9965d1608..a29b51ef0 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -15522,6 +15522,63 @@ private static bool TryReadAPowerTimesARadicalQuadratic(Entity expr, Entity.Vari /// that 1/(1/u)^2 is u^2. /// internal static Entity? SolveAGaussianInAReciprocal(Entity expr, Entity.Variable x) + { + if (ReadAnExponentialInTheReciprocalOfALinear(expr, x) is not var (constant, @base, exponentInU, u, linear, slope, power) + || !TreeAnalyzer.TryGetPolyQuadratic(exponentInU, u, out var square, out _, out _) || TreeAnalyzer.IsZero(square)) + return null; + var powerInU = -power - EInteger.FromInt32(2); + if (!powerInU.CanFitInInt32() || powerInU.Abs().CompareTo(EInteger.FromInt32(32)) > 0) + return null; + var gaussian = MathS.Pow(@base, exponentInU); + var inU = powerInU.IsZero ? gaussian : MathS.Pow(u, Number.Integer.Create(powerInU)) * gaussian; + if (IntegralPatterns.TryStandardIntegrals(inU, u) is not { } answerInU) + return null; + return (-constant / slope * answerInU.Substitute(u, 1 / linear)).InnerSimplified; + } + + /// + /// The same substitution where the exponent is not a Gaussian in u = 1/L: + /// L^m F^(a + b/L) or L^m F^(a + b/L^3) is + /// -(1/d) u^(-m - 2) F^(a + b u) or F^(a + b u^3), an exponential beside a + /// power, asked as a question in u and written back with u = 1/L. + /// + /// + /// Rubi's 2.3, F^(a + b/(c + d x)) (c + d x)^2, F^(a + b/(c + d x))/(c + d x) + /// and F^(a + b/(c + d x)^3)/(c + d x), were declined: the substitution was made only + /// for a Gaussian, whose moments the table answers, and an exponential of a linear or a + /// cube in u beside a power of it is the exponential integral's, or a power of the + /// cube's argument under w = u^3, which the integrator answers when asked in + /// u. + /// https://github.com/asc-community/AngouriMath/issues/718 + /// + internal static Entity? SolveAnExponentialInTheReciprocalOfALinear(Entity expr, Entity.Variable x, bool integrateByParts) + { + if (ReadAnExponentialInTheReciprocalOfALinear(expr, x) is not var (constant, @base, exponentInU, u, linear, slope, power) + || !TreeAnalyzer.TryGetPolynomial(exponentInU, u, out var monomials) + || monomials.Keys.Any(k => k.Sign < 0 || k.CompareTo(EInteger.FromInt32(8)) > 0) + || !monomials.Keys.Any(k => k.Sign > 0)) + return null; + var powerInU = -power - EInteger.FromInt32(2); + if (!powerInU.CanFitInInt32() || powerInU.Abs().CompareTo(EInteger.FromInt32(32)) > 0) + return null; + var exponential = MathS.Pow(@base, exponentInU); + var inU = powerInU.IsZero ? exponential : MathS.Pow(u, Number.Integer.Create(powerInU)) * exponential; + if (Integration.ComputeAsAQuestionOfItsOwn(inU, u, integrateByParts) is not { } answerInU + || answerInU.Nodes.Any(node => node == MathS.NaN)) + return null; + return (-constant / slope * answerInU.Substitute(u, 1 / linear)).InnerSimplified; + } + + /// + /// read as a constant times F^E times a whole power of a + /// linear L, with every x in E inside a reciprocal power of L: + /// the constant, F, E written in u = 1/L, u, L, its slope + /// and the power of L. The exponent is rewritten into u a node at a time -- + /// b/L^k is b u^k -- rather than by substituting x = (1/u - c)/d and + /// asking the simplifier to see that 1/(1/u)^2 is u^2. + /// + private static (Entity Constant, Entity Base, Entity ExponentInU, Variable U, Entity Linear, Entity Slope, EInteger Power)? + ReadAnExponentialInTheReciprocalOfALinear(Entity expr, Entity.Variable x) { Powf? exponential = null; Entity? linear = null; @@ -15560,16 +15617,9 @@ private static bool TryReadAPowerTimesARadicalQuadratic(Entity expr, Entity.Vari Powf(var below, Number.Integer { EInteger.Sign: < 0 } k) when below == linear => MathS.Pow(u, -k), _ => node }); - if (exponentInU.ContainsNode(x) || !TreeAnalyzer.TryGetPolyQuadratic(exponentInU, u, out var square, out _, out _) || TreeAnalyzer.IsZero(square)) - return null; - var powerInU = -power - EInteger.FromInt32(2); - if (!powerInU.CanFitInInt32() || powerInU.Abs().CompareTo(EInteger.FromInt32(32)) > 0) + if (exponentInU.ContainsNode(x)) return null; - var gaussian = MathS.Pow(exponential.Base, exponentInU); - var inU = powerInU.IsZero ? gaussian : MathS.Pow(u, Number.Integer.Create(powerInU)) * gaussian; - if (IntegralPatterns.TryStandardIntegrals(inU, u) is not { } answerInU) - return null; - return (-constant / slope * answerInU.Substitute(u, 1 / linear)).InnerSimplified; + return (constant, exponential.Base, exponentInU, u, linear, slope, power); } /// diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index 8cae2ee5f..e0408a243 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -635,6 +635,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) => if ((answer = IndefiniteIntegralSolver.SolveByFlatteningAPowerOfAnExponential(expr, x, integrateByParts)) is { }) return answer; // An exponential of a quadratic in 1/L beside a power of L, onto the Gaussian under u = 1/L. if ((answer = IndefiniteIntegralSolver.SolveAGaussianInAReciprocal(expr, x)) is { }) return answer; + // The same substitution where the exponent in the reciprocal is not a Gaussian. + if ((answer = IndefiniteIntegralSolver.SolveAnExponentialInTheReciprocalOfALinear(expr, x, integrateByParts)) is { }) return answer; // An exponential of a multiple of a logarithm is a power of the argument, which is // how every inverse hyperbolic function under an exponential arrives. if ((answer = IndefiniteIntegralSolver.SolveByFoldingAnExponentialOfALogarithm(expr, x, integrateByParts)) is { }) return answer; diff --git a/Sources/Tests/UnitTests/Calculus/ExponentialOfTheReciprocalIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ExponentialOfTheReciprocalIntegralTest.cs new file mode 100644 index 000000000..4d2324cf6 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/ExponentialOfTheReciprocalIntegralTest.cs @@ -0,0 +1,47 @@ +// +// 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 a power of the reciprocal of a linear, beside a power of the linear, that + /// is not a Gaussian in u = 1/L: F^(a + b/L) L^m is + /// -(1/d) u^(-m - 2) F^(a + b u), an exponential beside a power. Rubi's 2.3. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class ExponentialOfTheReciprocalIntegralTest + { + [Theory] + [InlineData("F^(a + b/(c + d*x))*(c + d*x)^2")] + [InlineData("F^(a + b/(c + d*x))")] + [InlineData("F^(a + b/(c + d*x))/(c + d*x)")] + [InlineData("F^(a + b/(c + d*x)^3)/(c + d*x)")] + [InlineData("F^(a + b/(c + d*x)^3)*(c + d*x)^2")] + public void UnderTheReciprocal(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + Entity Pinned(Entity e) => e.Substitute("F", 2.3).Substitute("a", 0.4).Substitute("b", 1.1) + .Substitute("c", 0.6).Substitute("d", 1.3); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + foreach (var at in new[] { -2.4, -1.5, 0.3, 0.8, 1.5, 2.4 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + 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}"); + } + } + } +} From 45a24d460a418a838d6cca8e54489ff39b92f852 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Sun, 4 Oct 2026 14:20:00 +0000 Subject: [PATCH 2/2] An exponential in the reciprocal of a linear is asked after the ansatz that writes it in the base Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Continuous/Integration/Integration.Definition.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index e0408a243..0e6033fa1 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -635,8 +635,6 @@ private static Entity Normalized(Entity expr, Entity.Variable x) => if ((answer = IndefiniteIntegralSolver.SolveByFlatteningAPowerOfAnExponential(expr, x, integrateByParts)) is { }) return answer; // An exponential of a quadratic in 1/L beside a power of L, onto the Gaussian under u = 1/L. if ((answer = IndefiniteIntegralSolver.SolveAGaussianInAReciprocal(expr, x)) is { }) return answer; - // The same substitution where the exponent in the reciprocal is not a Gaussian. - if ((answer = IndefiniteIntegralSolver.SolveAnExponentialInTheReciprocalOfALinear(expr, x, integrateByParts)) is { }) return answer; // An exponential of a multiple of a logarithm is a power of the argument, which is // how every inverse hyperbolic function under an exponential arrives. if ((answer = IndefiniteIntegralSolver.SolveByFoldingAnExponentialOfALogarithm(expr, x, integrateByParts)) is { }) return answer; @@ -1062,6 +1060,10 @@ private static Entity Normalized(Entity expr, Entity.Variable x) => // an ansatz finds it or nothing does. After the substitutions, which answer the // linear-exponent cases in their own terms. if ((answer = IndefiniteIntegralSolver.SolveByExponentialAnsatz(expr, x)) is { }) return answer; + // An exponential of a polynomial in the reciprocal of a linear that is not a Gaussian, + // under the Gaussian's substitution `u = 1/L`. After the ansatz, which answers some of + // these in the base: `f^(a + b/x)/x^4` is `f^(a + b/x)` times a polynomial in `1/x`. + if ((answer = IndefiniteIntegralSolver.SolveAnExponentialInTheReciprocalOfALinear(expr, x, integrateByParts)) is { }) return answer; // A polynomial times a fractional power of a base that holds a function of x, // answered as a polynomial times the next power of the base: a linear system in // the polynomial's coefficients, exact, and volunteered at any depth.