diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 0f1494e17..6dc4bb0d0 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -1109,6 +1109,28 @@ with `int F` the next power over `p^2`. Rubi's 6.1.1, 6.2.1, 6.5.1 and 6.6.1 | `"x/sech(x)^(7/2)-5/21*x*sqrt(sech(x))".Integrate("x")` | left unevaluated | the antiderivative, over two reduction steps | | `"x/csch(x)^(3/2)+1/3*x*sqrt(csch(x))".Integrate("x")` | left unevaluated | the antiderivative | +### A hyperbolic function of a logarithm is integrated, the exponent folded structurally + +`tanh(ln(x))` was left as written. The library spells `tanh(y)` with `e^(2y)`, so a hyperbolic +function of a logarithm is an exponential whose exponent is a *sum* holding one logarithm -- +`e^(a + b ln(q))` -- and the rule that folds an exponential of a logarithm read only a product, +`e^(k ln(q))`. It reads the exponent structurally now: `e^(u + v)` is `e^u e^v`, `e^(k u)` is +`(e^u)^k` for an `x`-free `k`, `e^(u/d)` likewise, and `e^(ln q)` is `q`, so +`a + b ln(c x^n)` folds to `e^a (c x^n)^b` and its negation -- which the same function writes +below the bar -- to the reciprocal. Composed exponents are flattened as they fold: `n (ln(q)/2)` +is `q^(n/2)` and not `(sqrt(q))^n`, whose nesting made +`e^(n acoth(a x))/(c - a^2 c x^2)^4` a search of fifty seconds where the flat form is declined in +three. One logarithm in the exponent, since a difference of two folds to a power of a quotient of +quotients that nothing below reads. Rubi's 6.3.2 and 6.5.3 +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"tanh(ln(x))".Integrate("x")` | left unevaluated | `x - 2 arctan(x)` up to the form | +| `"sinh(2+3*ln(x))".Integrate("x")` | left unevaluated | `e^2 x^4/8 - x^(-2)/(4 e^2)` up to the form | +| `"sech(a+2*ln(c/x^(1/2)))^3".Integrate("x")` | left unevaluated | the antiderivative | +| `"x*tanh(a+2*ln(x))^2".Integrate("x")` | left unevaluated | the antiderivative | + ### `binomial(n, k)` is a function **Addition, not silent.** The binomial coefficient is a node, `Entity.Binomialf`, spelled diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 0499ba134..7c8cf4439 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -9877,27 +9877,78 @@ node is Powf(Powf(var @base, var inner), var outer) { if (node is not Powf(var @base, var exponent) || @base != MathS.e || !exponent.ContainsNode(x)) return node; - // The exponent as a product with one natural logarithm of x among its factors - // and nothing else of x: `3 * (1/2 * ln(q))` is how `e^(3 acoth(a x))` arrives. - Entity? argument = null; - Entity k = Number.Integer.One; - foreach (var factor in Mulf.LinearChildren(exponent)) - { - if (factor is Logf(var logBase, var inner) && logBase == MathS.e && argument is null) - argument = inner; - else if (factor.ContainsNode(x)) - return node; - else - k = k * factor; - } - if (argument is null) + // The exponent read structurally, since `e^(u + v)` is `e^u e^v`, `e^(k u)` is + // `(e^u)^k` and `e^(ln q)` is `q`: `a + b ln(c x^n)` -- a hyperbolic function of + // a logarithm -- folds to `e^a (c x^n)^b`, and its negation, which the same + // function writes below the bar, to the reciprocal of that. + // One logarithm in the exponent: `a + b ln(c x^n)` is a hyperbolic function of + // a logarithm, and `n acoth(a x)` written as a difference of two -- which folds + // to a power of a quotient of quotients -- was a search of fifty seconds where + // the unfolded form is declined in three. + if (exponent.Nodes.Count(inner => inner is Logf(var logBase, _) && logBase == MathS.e) != 1) return node; - var power = k.InnerSimplified; - return power == Number.Integer.One ? argument : MathS.Pow(argument, power); + var folded = FoldTheExponent(exponent, x); + return folded ?? node; }); return folded == expr ? null : Integration.ComputeAsAQuestionOfItsOwn(folded, x, integrateByParts); } + /// + /// e^(exponent) written without the exponential wherever the exponent is built + /// from logarithms and constants: e^(u + v) is e^u e^v, e^(k u) is + /// (e^u)^k for an -free k, and e^(ln q) is + /// q. where a part mentioning is + /// none of those, and where no logarithm was folded at all. + /// + private static Entity? FoldTheExponent(Entity exponent, Entity.Variable x) + { + var folded = Fold(exponent, out var found); + return found ? folded : null; + + Entity? Fold(Entity exponent, out bool found) + { + found = false; + switch (exponent) + { + case Logf(var logBase, var inner) when logBase == MathS.e: + found = true; + return inner; + case Sumf(var left, var right): + { + var foldedLeft = Fold(left, out var leftFound); + var foldedRight = Fold(right, out var rightFound); + found = leftFound || rightFound; + return foldedLeft is null || foldedRight is null ? null : foldedLeft * foldedRight; + } + case Minusf(var left, var right): + { + var foldedLeft = Fold(left, out var leftFound); + var foldedRight = Fold(right, out var rightFound); + found = leftFound || rightFound; + return foldedLeft is null || foldedRight is null ? null : foldedLeft / foldedRight; + } + case Mulf(var left, var right) when !left.ContainsNode(x): + return Raised(Fold(right, out found), left); + case Mulf(var left, var right) when !right.ContainsNode(x): + return Raised(Fold(left, out found), right); + case Divf(var above, var below) when !below.ContainsNode(x): + return Raised(Fold(above, out found), (Number.Integer.One / below).InnerSimplified); + default: + // A part free of the variable stays an exponential of itself. + return exponent.ContainsNode(x) ? null : MathS.Pow(MathS.e, exponent); + } + } + + // The power of a power as one power: `n (1/2 ln q)` is `q^(n/2)` and not + // `(sqrt(q))^n`, whose nesting the rules below read as a different question -- + // `e^(n acoth(a x))/(c - a^2 c x^2)^4` was a search of fifty seconds written that + // way and is declined in three written flat. + static Entity? Raised(Entity? folded, Entity power) + => folded is null ? null + : folded is Powf(var @base, var inner) ? MathS.Pow(@base, (inner * power).InnerSimplified) + : MathS.Pow(folded, power); + } + /// /// A logarithm whose argument is a quotient in that cancels with /// the functions of in it taken for indeterminates, with the diff --git a/Sources/Tests/UnitTests/Calculus/ExponentialOfALogarithmIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ExponentialOfALogarithmIntegralTest.cs index 0adf3706e..f2a3f0490 100644 --- a/Sources/Tests/UnitTests/Calculus/ExponentialOfALogarithmIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/ExponentialOfALogarithmIntegralTest.cs @@ -76,5 +76,19 @@ private static void DifferentiatesBack(string integrand) [InlineData("e^(ln(x^2 + 1)/2) * x")] [InlineData("e^(2*(1/2*ln(x + 2)))")] public void AnExponentialOfAMultipleOfALogarithm(string integrand) => DifferentiatesBack(integrand); + + /// + /// The exponent read structurally, since e^(u + v) is e^u e^v and + /// e^(k u) is (e^u)^k: a hyperbolic function of a logarithm is written + /// with e^(a + b ln(q)) above the bar and e^(-(a + b ln(q))) below it, + /// and both are powers of q times a constant. Rubi's 6.5.3 and 6.6.3. + /// + [Theory] + [InlineData("sinh(2 + 3*ln(x))")] + [InlineData("cosh(1 + ln(x^2 + 1))")] + [InlineData("sech(3 + 2*ln(2/x^(1/2)))^3")] + [InlineData("e^(1 + ln(x + 2)/2)")] + [InlineData("tanh(ln(x))")] + public void AHyperbolicFunctionOfALogarithm(string integrand) => DifferentiatesBack(integrand); } }