diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 9dca6c2ea..a92d42e79 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -149,7 +149,8 @@ as `a - b = 0`, as a statement's own solve reads it: `"x + 1 = 3".Solve("x")` wa
`z = -b ln(F) x^n` and `s = (m + 1)/n`, where `s` is neither whole nor half a whole number, or is past 30, as
`x^31 e^(-x)` is, whose sum of 32 terms is `-gamma(32, x)` now. A definite integral of such an answer that is
undefined at a bound is left as written. And beside a power of a sine and a cosine, which Euler's formula writes as
- those exponentials, the gamma functions of an imaginary argument.
+ those exponentials, the gamma functions of an imaginary argument, and beside a hyperbolic function of a power of
+ `x`, as `x sinh(c + d x^3)`.
([#2075](https://github.com/asc-community/AngouriMath/issues/2075))
| Input | Was (2.6.0) | Now |
diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs
index e9165010c..f43cb1e73 100644
--- a/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs
+++ b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs
@@ -422,23 +422,31 @@ private static bool TakeMomentFactor(Entity factor, bool inverted, Entity.Variab
var exponent = exponentialBelow ? -exponential.Exponent : exponential.Exponent;
var logarithm = exponential.Base == MathS.e ? Entity.Number.Integer.One : MathS.Ln(exponential.Base);
- // The exponent as a + b x^n, the term in x as written.
+ // The exponent as a + b x^n, the term in x as written, or as expanded: -(c + d x^3) is one term as written, and
+ // it is how sinh(c + d x^3) arrives, as (e^(c + d x^3) - e^(-(c + d x^3)))/2.
Entity constant = Entity.Number.Integer.Zero;
Entity? term = null;
Entity? degree = null;
- foreach (var summand in Entity.Sumf.LinearChildren(exponent))
- if (!summand.ContainsNode(x))
- constant += summand;
- else if (term is null && ThePowerOfX(summand, x) is { } read)
- {
- term = summand;
- degree = read;
- }
- else
- {
- term = null;
+ for (var reading = 0; reading < 2 && term is null; reading++)
+ {
+ var asRead = reading == 0 ? exponent : exponent.Expand();
+ if (reading == 1 && asRead == exponent)
break;
- }
+ constant = Entity.Number.Integer.Zero;
+ foreach (var summand in Entity.Sumf.LinearChildren(asRead))
+ if (!summand.ContainsNode(x))
+ constant += summand;
+ else if (term is null && ThePowerOfX(summand, x) is { } read)
+ {
+ term = summand;
+ degree = read;
+ }
+ else
+ {
+ term = null;
+ break;
+ }
+ }
// Or a linear written otherwise, as the negation -(a + b x) is, which is how gamma(2, a + b x) is
// elementary: e^(-(a + b x)) (1 + a + b x). https://github.com/asc-community/AngouriMath/issues/2075
if (term is null && TreeAnalyzer.TryGetPolyLinear(exponent, x, out var slopeOfTheExponent, out var offsetOfTheExponent)
@@ -474,11 +482,11 @@ private static bool TakeMomentFactor(Entity factor, bool inverted, Entity.Variab
// x^(m + 1) z^(-s), constant on each half-line, is K^(-s) for x > 0 with z = K x^n, and its value at -1 for
// x < 0. Where the two are one number it is that number on the whole line, and the answer has a value at 0
// rather than 0 times an infinity there: e^(x^3) is gamma(1/3, -x^3)/3, and its integral from 0 to 1 was NaN.
- if (allOfX && mPlusOne is Entity.Number && degree is Entity.Number
- && (-logarithm * term.Substitute(x, Entity.Number.Integer.One)).InnerSimplified is Entity.Number theRate
- && MathS.Pow(theRate, (-s).InnerSimplified).InnerSimplified is Entity.Number onTheRight
- && constantFactor.Substitute(x, Entity.Number.Integer.MinusOne).InnerSimplified is Entity.Number onTheLeft
- && onTheRight == onTheLeft)
+ // The two are its values at 1 and -1, which need not be numbers: x^(1/3) e^(-1 - 3 x^2) has 3^(-2/3) on both.
+ if (allOfX
+ && constantFactor.Substitute(x, Entity.Number.Integer.One).InnerSimplified is var onTheRight
+ && !onTheRight.ContainsNode(x)
+ && constantFactor.Substitute(x, Entity.Number.Integer.MinusOne).InnerSimplified == onTheRight)
return leading * onTheRight * MathS.IncompleteGamma(s, z);
return leading * constantFactor * MathS.IncompleteGamma(s, z);
}
diff --git a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs
index a318cef0a..34b64892e 100644
--- a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs
+++ b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs
@@ -84,6 +84,8 @@ public void AHighWholePowerIsOneGammaFunction()
[Theory]
[InlineData("integral(e^(x^3), x, 0, 1)", 1.34190441797742)]
[InlineData("integral(e^(-x^3), x, 0, 1)", 0.807511182139671)]
+ // 3^(-2/3) on both half-lines, which is not a number but one constant.
+ [InlineData("integral(x^(1/3)*e^(-(1 + 3*x^2)), x, 0, 1)", 0.1169389395696306439)]
public void ADefiniteIntegralFromZero(string integral, double value)
{
var result = integral.ToEntity().Simplify().EvalNumerical();
@@ -110,6 +112,17 @@ public void APositiveRateIsTakenOutOfThePower()
[InlineData("sin(c + d*x^3)^2")]
public void APowerBesideASineOrACosine(string integrand) => DifferentiatesBack(integrand);
+ ///
+ /// And a hyperbolic function of a power of x, which arrives as e^u and e^(-u): the second's exponent
+ /// is read expanded. Rubi's 6.1.3.
+ ///
+ [Theory]
+ [InlineData("x*sinh(c + d*x^3)")]
+ [InlineData("(f*x)^m*sinh(c + d*x^2)")]
+ [InlineData("x^m*sinh(a + b/x)")]
+ [InlineData("x^m*e^(-(a + b*x^2))")]
+ public void AHyperbolicFunctionOfAPowerOfX(string integrand) => DifferentiatesBack(integrand);
+
///
/// Not where the integral is elementary, an exponential integral or an error function: those keep the form
/// they had.