Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// A hyperbolic cosecant beside a power of a binomial in an even power of the sine, where
/// <c>csch(y)</c> arrives as the difference of the exponentials with its half gathered
/// elsewhere: Rubi's 6.1.7 <c>csch(c + d x) (a + b sinh(c + d x)^4)^n</c>, which was left
/// unevaluated.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[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));
}
}
}
Loading