diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index ccb8530f8..d286091ff 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -2381,6 +2381,30 @@ family 3 at twenty a file and a sample of the others are answered alone as they
|---|---|---|
| `"li(b*x)/x".Integrate("x")` | `li * b * x + C`, with `li` a variable | `li(b * x) * ln(b * x) - b * x + C` |
+### A special function of a logarithm of a monomial is integrated, through `Ei` of a complex argument
+
+`(e x)^m Si(d (a + b ln(c x^n)))` is by parts against `(e x)^m`, and what is left is a power of `x`
+times `sin(d (a + b ln(c x^n)))/(a + b ln(c x^n))`. Two rules were missing on the way
+([#1501](https://github.com/asc-community/AngouriMath/issues/1501)):
+
+- **A function of one logarithm of a monomial.** A power of `x` times `G(ln(c x^n))` is integrated
+ under `t = ln(c x^n)`, with `x^(m + 1)` written as `K e^((m + 1) t/n)` and
+ `K = x^(m + 1) (c x^n)^(-(m + 1)/n)`, whose derivative is 0 wherever it is defined. That is an
+ antiderivative wherever the integrand is real. For an even `n` that includes negative `x`, where
+ `ln(c x^n)` is not `ln(c) + n ln(x)`. By parts leaves `(d x)^m x/ln(b x)` of `(d x)^m li(b x)`,
+ and this rule answers it with `Ei((m + 2) ln(b x))`.
+- **An exponential beside a sine or cosine, over linears.** Each sine and cosine is written as
+ exponentials, and each term is the exponential integral of a complex argument. The two conjugate
+ terms add up to a real value.
+
+Both columns measured on a build, `v2.5.0` against this change.
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"e^(2*x)*sin(x)/x".Integrate("x")` | `integral(e ^ (2 * x) * sin(x) / x, x)` | `(-1/2 * i) * Ei((2 + i) * x) + 1/2 * i * Ei((2 - i) * x) + C` |
+| `"x*sin(ln(x))/ln(x)".Integrate("x")` | `integral(x * sin(ln(x)) / ln(x), x)` | `(-1/2 * i) * Ei((2 + i) * ln(x)) + 1/2 * i * Ei((2 - i) * ln(x)) + C` |
+| `"cos(a+b*ln(c*x^n))^2".Integrate("x")` | `integral(cos(a + b * ln(c * x ^ n)) ^ 2, x)` | an antiderivative in `sin` and `cos` of `2 (a + b ln(c x^n))`, beside `x (c x^n)^(-1/n)` |
+
### An exponential or a hyperbolic function over several linears is split into partial fractions over them
`e^x/(x (x + 1))` was left unevaluated, where `e^x/x` and `e^x/(x + 1)` were each answered with
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
index 46a7ea811..e04cdb285 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs
@@ -3978,6 +3978,99 @@ bool Read(Entity factor, int times)
return Functions.PartialFractions.Bare((constant * sum).InnerSimplified);
}
+ ///
+ /// An exponential of a linear times sines and cosines of linears, and a polynomial, over
+ /// linears: each sine and cosine is written as exponentials, sin(c x) = (e^(i c x) - e^(-i c x))/(2i),
+ /// the product multiplied out, and every term is an exponential of a linear with a
+ /// complex rate over the linears, which
+ /// and answer with the exponential
+ /// integral of a complex argument. e^(2x) sin(x)/x is
+ /// (Ei((2 + i) x) - Ei((2 - i) x))/(2i), real where x is: the two terms are
+ /// conjugates. What by parts leaves of Si(ln(x)), under t = ln(x), is
+ /// e^t sin(t)/t. Rubi's 8.4, (e x)^m Si(d (a + b ln(c x^n))), answers the same way.
+ /// https://github.com/asc-community/AngouriMath/issues/1501
+ ///
+ ///
+ /// Only with an exponential of a real rate beside the sine or cosine: alone over a linear,
+ /// the sine and cosine integrals are their closed form, and the rule for them answers.
+ ///
+ internal static Entity? SolveAnExponentialTimesATrigonometricOverLinears(Entity expr, Entity.Variable x)
+ {
+ if (expr is not (Divf or Mulf))
+ return null;
+ int exponentialsByShape = 0, trigonometricsByShape = 0;
+ var aPowerOfASumByShape = false;
+ CountFactorsByShape(expr, x, ref exponentialsByShape, ref trigonometricsByShape, ref aPowerOfASumByShape);
+ if (exponentialsByShape == 0 || trigonometricsByShape == 0)
+ return null;
+ Entity constant = Number.Integer.One;
+ Entity? polynomial = null;
+ var linears = new List<(Entity Linear, int Power)>();
+ var exponents = new List();
+ var terms = new List<(Entity Coefficient, int[] Multiples)> { (Number.Integer.One, new int[MostExponents]) };
+ int exponentials = 0, trigonometrics = 0;
+ foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
+ {
+ if (!factor.ContainsNode(x))
+ {
+ constant = underneath ? constant / factor : constant * factor;
+ continue;
+ }
+ var (@base, exponent) = factor is Powf(var raised, Number.Integer whole) && whole.EInteger.CanFitInInt32() && !whole.EInteger.IsZero
+ ? (raised, whole.EInteger.ToInt32Checked()) : (factor, 1);
+ if (underneath && TreeAnalyzer.TryGetPolyLinear(@base, x, out var slopeOfIt, out _) && !TreeAnalyzer.IsZero(slopeOfIt))
+ {
+ var at = linears.FindIndex(pair => pair.Linear == @base);
+ if (at < 0)
+ linears.Add((@base, exponent));
+ else
+ linears[at] = (@base, linears[at].Power + exponent);
+ continue;
+ }
+ if (underneath)
+ return null;
+ if (AsExponentialsOfQuadratics(factor, x, exponents) is { } read)
+ {
+ if (factor.Nodes.Any(node => node is Sinf or Cosf))
+ trigonometrics++;
+ else
+ exponentials++;
+ if (Multiplied(terms, read) is not { } product)
+ return null;
+ terms = product;
+ continue;
+ }
+ if (TreeAnalyzer.TryGetPolynomial(factor, x, out var monomials) && monomials.Keys.All(degree => degree.Sign >= 0 && degree.CompareTo(EInteger.FromInt32(12)) <= 0))
+ {
+ polynomial = polynomial is null ? factor : polynomial * factor;
+ continue;
+ }
+ return null;
+ }
+ if (exponentials == 0 || trigonometrics == 0 || linears.Count == 0 || linears.Count > 4 || linears.Sum(pair => pair.Power) > 12)
+ return null;
+ var below = linears.Aggregate((Entity)Number.Integer.One, (product, pair) => product * MathS.Pow(pair.Linear, pair.Power));
+ var above = polynomial ?? Number.Integer.One;
+ Entity sum = Number.Integer.Zero;
+ foreach (var (coefficient, multiples) in terms)
+ {
+ var (a, b, c) = TheExponent(multiples, exponents);
+ // Linear exponents only: a quadratic left is the Gaussian's, and is another rule's.
+ if (!TreeAnalyzer.IsZero(a))
+ return null;
+ var factorOfTheTerm = TreeAnalyzer.IsZero(c) ? coefficient : coefficient * MathS.Pow(MathS.e, c);
+ var question = TreeAnalyzer.IsZero(b)
+ ? above / below
+ : MathS.Pow(MathS.e, b * x) * above / below;
+ if (((TreeAnalyzer.IsZero(b) ? null
+ : linears.Count == 1 ? SolveAnExponentialOfALinearOverAPowerOfALinear(question, x) : SolveAnExponentialOverSeveralLinears(question, x))
+ ?? Integration.ComputeIndefiniteIntegral(question, x, false)) is not { } integral)
+ return null;
+ sum += factorOfTheTerm * integral;
+ }
+ return Functions.PartialFractions.Bare((constant * sum).InnerSimplified);
+ }
+
/// Whether a factor of is a sum with an exponential of the variable in it.
private static bool HasASumOfExponentials(Entity expr, Entity.Variable x) => expr switch
{
@@ -6614,6 +6707,93 @@ node is Logf(var @base, var argument) && !@base.ContainsNode(x) && @base != Math
: null;
}
+ ///
+ /// A power of x times a function of one logarithm of a monomial, x^m G(ln(c x^n)),
+ /// under t = ln(c x^n): dx = x dt/n, and x^(m + 1) is
+ /// K e^((m + 1) t/n) with K = x^(m + 1) (c x^n)^(-(m + 1)/n), whose derivative
+ /// is 0 wherever it is defined. So the answer is K/n times the integral of
+ /// e^((m + 1) t/n) G(t) at t = ln(c x^n), an antiderivative on the whole of
+ /// the real line where the integrand is real -- for an even n that includes negative
+ /// x, where ln(c x^n) is not ln(c) + n ln(x). A power of a monomial,
+ /// (e x)^p, is x^p times a factor of the same kind. What by parts leaves of
+ /// (e x)^m Si(d (a + b ln(c x^n))) is this, with G(t) a sine over a linear in
+ /// t. Rubi's answers to 8.3 to 8.5 are written in exactly this K.
+ /// https://github.com/asc-community/AngouriMath/issues/1501
+ ///
+ ///
+ /// After , which answers ln(x) alone.
+ ///
+ internal static Entity? SolveByAPowerAndALogarithmOfAMonomial(Entity expr, Entity.Variable x, bool integrateByParts)
+ {
+ if (!expr.Nodes.Any(node => node is Logf))
+ return null;
+ Entity constant = Number.Integer.One;
+ Entity m = Number.Integer.Zero;
+ Entity locallyConstant = Number.Integer.One;
+ Entity rest = Number.Integer.One;
+ foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
+ {
+ if (!factor.ContainsNode(x))
+ {
+ constant = underneath ? constant / factor : constant * factor;
+ continue;
+ }
+ // x, a power of x, or a power of a monomial (k x)^p, p free of x.
+ var (@base, power) = factor is Powf(var raised, var exponent) && !exponent.ContainsNode(x) ? (raised, exponent) : (factor, (Entity)Number.Integer.One);
+ if (TheMonomial(@base, x) is var (coefficientOfIt, degreeOfIt) && degreeOfIt == Number.Integer.One)
+ {
+ var p = underneath ? -power : power;
+ m = m + p;
+ if (coefficientOfIt != Number.Integer.One)
+ locallyConstant = locallyConstant * MathS.Pow(@base, p) * MathS.Pow(x, -p);
+ continue;
+ }
+ rest = underneath ? rest / factor : rest * factor;
+ }
+ // One logarithm of a monomial in what is left.
+ var logarithms = rest.Nodes.OfType().Where(node => node.Base == MathS.e && node.ContainsNode(x)).Distinct().ToList();
+ if (logarithms.Count != 1 || TheMonomial(logarithms[0].Antilogarithm, x) is not var (c, n) || TreeAnalyzer.IsZero(n))
+ return null;
+ var t = Variable.CreateUnique(expr, "t_log");
+ var inT = rest.Substitute(logarithms[0], t);
+ if (inT.ContainsNode(x))
+ return null;
+ var mPlusOne = (m + Number.Integer.One).InnerSimplified;
+ // One quotient, as the logarithm substitution writes its own: `e^((m + 2) t)/t` is the
+ // exponential integral, and as a product with `t^(-1)` it went to integration by parts.
+ // https://github.com/asc-community/AngouriMath/issues/1646
+ var integrand = Functions.SingleQuotient.Combine(inT * MathS.Pow(MathS.e, (mPlusOne / n).InnerSimplified * t)).InnerSimplified;
+ if (integrand is Providedf(var bare, _))
+ integrand = bare;
+ if (Integration.ComputeIndefiniteIntegral(integrand, t, integrateByParts) is not { } inTIntegral)
+ return null;
+ // Of ln(x) itself, K is 1, and written as x^(m + 1) x^(-(m + 1)) it is not seen to be.
+ var k = logarithms[0].Antilogarithm == x ? Number.Integer.One
+ : MathS.Pow(x, mPlusOne) * MathS.Pow(logarithms[0].Antilogarithm, (-mPlusOne / n).InnerSimplified);
+ var back = constant * locallyConstant * k / n * inTIntegral.Substitute(t, logarithms[0]);
+ return back.Nodes.Any(node => node == MathS.NaN) ? null : back;
+ }
+
+ ///
+ /// as c x^n, c and n free of ,
+ /// or .
+ ///
+ private static (Entity Coefficient, Entity Degree)? TheMonomial(Entity expr, Entity.Variable x)
+ {
+ if (expr == x)
+ return (Number.Integer.One, Number.Integer.One);
+ if (expr is Powf(var @base, var power) && @base == x && !power.ContainsNode(x))
+ return (Number.Integer.One, power);
+ if (expr is Mulf(var left, var right))
+ {
+ if (!left.ContainsNode(x) && TheMonomial(right, x) is var (c, n))
+ return (left * c, n);
+ if (!right.ContainsNode(x) && TheMonomial(left, x) is var (c2, n2))
+ return (right * c2, n2);
+ }
+ return null;
+ }
+
///
/// A quotient of two homogeneous polynomials in sin(u) and cos(u),
/// integrated by t = tan(u) — which turns it into a rational function of t
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
index 5be10b342..5889a488e 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs
@@ -646,6 +646,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// And either over several linears, split into partial fractions over them first, as the
// trigonometric rule below splits.
if ((answer = IndefiniteIntegralSolver.SolveAnExponentialOverSeveralLinears(expr, x)) is { }) return answer;
+ // And with sines and cosines beside the exponential, written as exponentials: each term
+ // is then the exponential's, with a complex rate.
+ if ((answer = IndefiniteIntegralSolver.SolveAnExponentialTimesATrigonometricOverLinears(expr, x)) is { }) return answer;
// Sines and cosines of a linear over a power of a linear, onto Si and Ci under u = the
// linear, where the search would take the quotient by parts without end.
if ((answer = IndefiniteIntegralSolver.SolveATrigonometricOfALinearOverAPowerOfALinear(expr, x)) is { }) return answer;
@@ -838,6 +841,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// is why the general substitution does not find it and why it pays: the integrator
// answers an exponential times almost anything.
if ((answer = IndefiniteIntegralSolver.SolveByLogarithmSubstitution(expr, x, integrateByParts)) is { }) return answer;
+ // And a power of x times a function of one logarithm of a monomial, under t = that logarithm.
+ if ((answer = IndefiniteIntegralSolver.SolveByAPowerAndALogarithmOfAMonomial(expr, x, integrateByParts)) is { }) return answer;
// And the inverse trigonometric functions' own, beside the logarithm's and for the
// same reason: `x = sin(u)` removes the `x` that substituting for `arcsin(x)` leaves.
if ((answer = IndefiniteIntegralSolver.SolveByInverseTrigonometricSubstitution(expr, x, integrateByParts)) is { }) return answer;
diff --git a/Sources/Tests/UnitTests/Calculus/ExponentialIntegralIntegrationTest.cs b/Sources/Tests/UnitTests/Calculus/ExponentialIntegralIntegrationTest.cs
index 18fff93ee..7190702a9 100644
--- a/Sources/Tests/UnitTests/Calculus/ExponentialIntegralIntegrationTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/ExponentialIntegralIntegrationTest.cs
@@ -62,6 +62,34 @@ private static readonly (string, string)[] Pins =
public void AnExponentialOfALinearOverAPowerOfALinear(string integrand)
=> DifferentiatesBack(integrand, AroundZero, Pins);
+ ///
+ /// Beside a sine or a cosine of a linear, the sine and cosine are written as exponentials,
+ /// and each term is the exponential's above with a complex rate: e^(2x) sin(x)/x is
+ /// (Ei((2 + i) x) - Ei((2 - i) x))/(2i), two conjugate terms whose sum is real.
+ ///
+ [Theory]
+ [InlineData("e^(2*x)*sin(x)/x")]
+ [InlineData("e^x*cos(x)/(1 + x)")]
+ [InlineData("x*e^x*sin(2*x)/(x - 1)")]
+ public void AnExponentialTimesASineOrCosineOverALinear(string integrand)
+ => DifferentiatesBack(integrand, AroundZero, Pins);
+
+ ///
+ /// A power of x times a function of a logarithm of a monomial, under
+ /// t = ln(c x^n), with x^(m + 1) written as K e^((m + 1) t/n) and
+ /// K = x^(m + 1) (c x^n)^(-(m + 1)/n) locally constant. With an even n the
+ /// integrand is real at negative x as well, and the answer is checked there too,
+ /// where ln(c x^n) is not ln(c) + n ln(x).
+ ///
+ [Theory]
+ [InlineData("x*sin(ln(x))/ln(x)")]
+ [InlineData("Si(d*(a + b*ln(c*x^n)))")]
+ [InlineData("x*Ci(d*(a + b*ln(c*x^n)))")]
+ [InlineData("(k*x)^m*Ei(d*(a + b*ln(c*x^n)))")]
+ public void APowerTimesAFunctionOfALogarithmOfAMonomial(string integrand)
+ => DifferentiatesBack(integrand, new[] { -2.1, -0.7, 0.6, 1.7 },
+ ("a", "2/5"), ("b", "13/10"), ("c", "7/10"), ("d", "19/10"), ("k", "11/10"), ("m", "1/2"), ("n", "2"));
+
///
/// Over several linears, the quotient is split into partial fractions over them first, and
/// each term is the question above: e^x/(x (x + 1)) is Ei(x) - Ei(x + 1)/e. The
@@ -103,6 +131,7 @@ public void AnOddNegativeMomentOfTheGaussian(string integrand)
[InlineData("(1 + x)/ln(x)")]
[InlineData("x^2/ln(c*(d + k*x^3)^n)")]
[InlineData("x^8/ln(c*(d + k*x^3)^n)^2")]
+ [InlineData("(k*x)^m*x/ln(b*x)")]
public void APowerOverAPowerOfALogarithm(string integrand)
=> DifferentiatesBack(integrand, PastOne, Pins);
diff --git a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs
index 4139e5635..0ba2167aa 100644
--- a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs
@@ -87,6 +87,15 @@ public void TheLogarithmicIntegralIsByParts(string integrand)
public void TheLogarithmicIntegralOverXIsOneRoundOfParts()
=> DifferentiatesBackAt(new[] { 0.35, 1.45, 2.3 }, "li(b*x)/x", ("b", "13/10"));
+ ///
+ /// Beside a power of a monomial, the remainder is b (d x)^m x/((m + 1) ln(b x)), a power
+ /// of x over a logarithm of a monomial, which is Ei((m + 2) ln(b x)) times a
+ /// locally constant factor. Rubi's 8.3 row 269.
+ ///
+ [Fact]
+ public void TheLogarithmicIntegralBesideAPowerOfAMonomial()
+ => DifferentiatesBackAt(new[] { 0.35, 1.45, 2.3 }, "(d*x)^m*li(b*x)", ("b", "13/10"), ("d", "19/10"), ("m", "1/2"));
+
///
/// Against a power of x, the special function is the factor differentiated: what is
/// left is the power times e^(-u^2), e^u/u, sin(u)/u and the like.