diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 534d41cf7..936316186 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -521,6 +521,19 @@ roots, `1 - x^2`, is taken as its two ([#718](https://github.com/asc-community/A | `"1/((1 - x^2)*(a + b*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,600 characters | | `"1/(x*(1 - x^2)*(a + b*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,637 characters | +### A hyperbolic cosecant beside a power of a binomial in the sine is read through its exponentials + +**Answers where there were none.** `csch(c + d x) (a + b sinh(c + d x)^4)^2` ran past fifteen minutes. `csch(y)` arrives +as `(e^y - e^-y)^(-1)` beside a 2 the product gathered, and the reader of the hyperbolic functions recognised the +difference only with its half; it now reads `e^y - e^-y` as `2 sinh(y)` and `e^y + e^-y` as `2 cosh(y)` under a whole +power, and the integrand is answered in a fraction of a second +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"csch(c+d*x)*(a+b*sinh(c+d*x)^4)^2".ToEntity().Integrate("x")` | past fifteen minutes | 426 characters | +| `"csch(c+d*x)*(a+b*sinh(c+d*x)^4)^3".ToEntity().Integrate("x")` | past fifteen minutes | 646 characters | + ### An exponential of a whole multiple of an inverse hyperbolic tangent is written algebraically first **Answers where there were none.** `e^(n artanh(u))` with u linear is written as powers of `1 + u` and `1 - u`, and then diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index e54a297c5..b4e4d7be8 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -29165,9 +29165,22 @@ private static (Entity OverTheTwo, Entity Argument)? ReadTheHyperbolicFunctions( return -c / s; case Divf(Number.Integer { EInteger: var m }, var inner) when m.Equals(EInteger.One) && ReadTheTwo(inner) is { } read: return 1 / read; + // The difference and the sum with their halves taken elsewhere, as a whole + // power only: `csch(y)` arrives as `(e^y - e^-y)^(-1)` beside a 2 the + // product gathered. Inside a fractional power, or below a constant other + // than 1, the reading hands the caller a root it then declines. + case Powf(var b, Number.Integer { EInteger: var n }) when ReadTheBare(b) is { } bare: + return MathS.Pow(bare, n); } return null; } + Entity? ReadTheBare(Entity node) => node switch + { + Minusf(var a, var b) when Is(a, one) && Is(b, one?.Negate()) => 2 * s, + Minusf(var a, var b) when Is(a, one?.Negate()) && Is(b, one) => -2 * s, + Sumf(var a, var b) when Is(a, one) && Is(b, one?.Negate()) || Is(a, one?.Negate()) && Is(b, one) => 2 * c, + _ => null + }; bool Is(Entity node, EInteger? k) => k is { } wanted && exponentials.TryGetValue(node, out var found) && found.Equals(wanted); var read = expr.Replace(node => node.ContainsNode(x) && ReadTheTwo(node) is { } inTheTwo ? inTheTwo : node); if (read.ContainsNode(s) || read.ContainsNode(c)) diff --git a/Sources/Tests/UnitTests/Calculus/AnOddHyperbolicIntegrandWithAPowerOfABinomialIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnOddHyperbolicIntegrandWithAPowerOfABinomialIntegralTest.cs new file mode 100644 index 000000000..d0da35d0d --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AnOddHyperbolicIntegrandWithAPowerOfABinomialIntegralTest.cs @@ -0,0 +1,57 @@ +// +// 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 +{ + /// + /// A hyperbolic cosecant beside a power of a binomial in an even power of the sine, where + /// csch(y) arrives as the difference of the exponentials with its half gathered + /// elsewhere: Rubi's 6.1.7 csch(c + d x) (a + b sinh(c + d x)^4)^n, which was left + /// unevaluated. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class AnOddHyperbolicIntegrandWithAPowerOfABinomialIntegralTest + { + private static void DifferentiatesBack(string integrand, params (string, double)[] pins) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + + var pinned = integral.Substitute("C", 0); + Entity original = integrand.ToEntity(); + foreach (var (name, value) in pins) + { + pinned = pinned.Substitute(name, value); + original = original.Substitute(name, value); + } + var derivative = pinned.Differentiate("x"); + foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 }) + { + var got = derivative.Substitute("x", at).EvalNumerical(); + var want = original.Substitute("x", at).EvalNumerical(); + var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart); + var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)); + Assert.True(difference / scale < 1e-8, + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + } + + [Theory] + [InlineData("csch(c+d*x)*(a+b*sinh(c+d*x)^4)^2")] + [InlineData("csch(c+d*x)*(a+b*sinh(c+d*x)^4)^3")] + public void IsAnsweredAndDifferentiatesBack(string integrand) + { + DifferentiatesBack(integrand, ("a", 1.3), ("b", 0.7), ("c", 0.3), ("d", 1.1)); + DifferentiatesBack(integrand, ("a", -0.5), ("b", 1.5), ("c", -0.4), ("d", 0.8)); + } + } +}