diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 5d77674d5..560128002 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -313,6 +313,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 d7b12536b..ab2dfa50f 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -15993,6 +15993,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;
@@ -16031,16 +16088,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 2f968045c..7a3acf38b 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -1111,6 +1111,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.
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}");
+ }
+ }
+ }
+}