|
| 1 | +// |
| 2 | +// Copyright (c) 2019-2026 Angouri. |
| 3 | +// AngouriMath is licensed under MIT. |
| 4 | +// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md. |
| 5 | +// Website: https://am.angouri.org. |
| 6 | +// |
| 7 | + |
| 8 | +using System; |
| 9 | +using AngouriMath.Extensions; |
| 10 | +using Xunit; |
| 11 | + |
| 12 | +namespace AngouriMath.Tests.Calculus |
| 13 | +{ |
| 14 | + /// <summary> |
| 15 | + /// A half-odd power of <c>a + b cosh(y) + c sinh(y)</c> with <c>a^2 = b^2 - c^2</c>, a square of |
| 16 | + /// the half angle turned by a phase, in closed form through <c>T = b sinh(y) + c cosh(y)</c>. |
| 17 | + /// Rubi's 6.7.1. |
| 18 | + /// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a> |
| 19 | + /// </summary> |
| 20 | + /// <remarks> |
| 21 | + /// Compared as complex numbers: with <c>b < |c|</c> the constant is imaginary and so is the |
| 22 | + /// base everywhere. Away from the zero of the base where <c>a</c> is negative. |
| 23 | + /// </remarks> |
| 24 | + [Trait("Area", "Calculus")] |
| 25 | + public sealed class APowerOfAHyperbolicCosineAndSinePlusTheirAmplitudeIntegralTest |
| 26 | + { |
| 27 | + [Theory] |
| 28 | + [InlineData("(b*cosh(x) + c*sinh(x) + sqrt(b^2 - c^2))^(5/2)", 2.0, 1.0)] |
| 29 | + [InlineData("sqrt(b*cosh(x) + c*sinh(x) - sqrt(b^2 - c^2))", 2.0, 1.0)] |
| 30 | + [InlineData("1/sqrt(b*cosh(x) + c*sinh(x) + sqrt(b^2 - c^2))", 2.0, 1.0)] |
| 31 | + [InlineData("1/(b*cosh(x) + c*sinh(x) - sqrt(b^2 - c^2))^(5/2)", 2.0, -1.0)] |
| 32 | + [InlineData("1/(b*cosh(g + f*x) + c*sinh(g + f*x) + sqrt(b^2 - c^2))^(3/2)", 0.7, 1.1)] |
| 33 | + [InlineData("(b*cosh(g + f*x) + c*sinh(g + f*x) - sqrt(b^2 - c^2))^(3/2)", 0.7, 1.1)] |
| 34 | + [InlineData("1/sqrt(5*cosh(2*x + 1) + 3*sinh(2*x + 1) + 4)", 0.0, 0.0)] |
| 35 | + [InlineData("3*(2*cosh(x) - sinh(x) - sqrt(3))^(-3/2)", 0.0, 0.0)] |
| 36 | + public void IsWrittenThroughTheDerivativeOfItsBase(string integrand, double b, double c) |
| 37 | + => DifferentiatesBack(integrand, b, c); |
| 38 | + |
| 39 | + // A whole power is left to the exponential substitution, which writes `1/S^2`, `1/S^3` and |
| 40 | + // `1/S^4` shorter than the same recurrence stepped up to `T/(a S)`: answered all the same. |
| 41 | + [Theory] |
| 42 | + [InlineData("1/(b*cosh(x) + c*sinh(x) + sqrt(b^2 - c^2))^2", 2.0, 1.0)] |
| 43 | + [InlineData("1/(b*cosh(x) + c*sinh(x) + sqrt(b^2 - c^2))^3", 2.0, -1.0)] |
| 44 | + [InlineData("1/(b*cosh(x) + c*sinh(x) + sqrt(b^2 - c^2))^4", 0.7, 1.1)] |
| 45 | + [InlineData("1/(b*cosh(g + f*x) + c*sinh(g + f*x) - sqrt(b^2 - c^2))^3", 2.0, 1.0)] |
| 46 | + [InlineData("3*(2*cosh(x) - sinh(x) - sqrt(3))^(-2)", 0.0, 0.0)] |
| 47 | + [InlineData("1/(5*cosh(2*x + 1) + 3*sinh(2*x + 1) + 4)^3", 0.0, 0.0)] |
| 48 | + public void AWholePowerIsAnsweredToo(string integrand, double b, double c) |
| 49 | + => DifferentiatesBack(integrand, b, c); |
| 50 | + |
| 51 | + private static void DifferentiatesBack(string integrand, double b, double c) |
| 52 | + { |
| 53 | + var integral = integrand.ToEntity().Integrate("x"); |
| 54 | + Assert.DoesNotContain("integral(", integral.Stringize()); |
| 55 | + Entity Pinned(Entity e) => e.Substitute("b", b).Substitute("c", c).Substitute("g", 0.4).Substitute("f", 1.3); |
| 56 | + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); |
| 57 | + var original = Pinned(integrand.ToEntity()); |
| 58 | + foreach (var at in new[] { -1.9, -1.1, -0.2, 0.4, 1.1, 1.9 }) |
| 59 | + { |
| 60 | + var want = original.Substitute("x", at).EvalNumerical(); |
| 61 | + var got = derivative.Substitute("x", at).EvalNumerical(); |
| 62 | + Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) |
| 63 | + < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)), |
| 64 | + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + /// <summary> |
| 69 | + /// Real and continuous where the integrand is: across the point where <c>T</c> vanishes and |
| 70 | + /// the base does not, and on either side of the base's zero where <c>a</c> is negative. |
| 71 | + /// The difference of the answer against Simpson's rule. |
| 72 | + /// </summary> |
| 73 | + [Theory] |
| 74 | + [InlineData(1.0, -2.0, 1.0)] |
| 75 | + [InlineData(-1.0, 0.1, 2.0)] |
| 76 | + [InlineData(-1.0, -3.0, -1.0)] |
| 77 | + public void IsAContinuousAntiderivativeOnTheRealLine(double sign, double from, double to) |
| 78 | + { |
| 79 | + var root = sign > 0 ? "sqrt(3)" : "-sqrt(3)"; |
| 80 | + var integral = $"1/sqrt(2*cosh(x) + sinh(x) + {root})".ToEntity().Integrate("x").Substitute("C", 0); |
| 81 | + Assert.DoesNotContain("integral(", integral.Stringize()); |
| 82 | + double Integrand(double t) => 1 / Math.Sqrt(2 * Math.Cosh(t) + Math.Sinh(t) + sign * Math.Sqrt(3)); |
| 83 | + const int steps = 400; |
| 84 | + var h = (to - from) / steps; |
| 85 | + var simpson = Integrand(from) + Integrand(to); |
| 86 | + for (var k = 1; k < steps; k++) |
| 87 | + simpson += (k % 2 == 1 ? 4 : 2) * Integrand(from + k * h); |
| 88 | + simpson *= h / 3; |
| 89 | + var difference = integral.Substitute("x", to).EvalNumerical() - integral.Substitute("x", from).EvalNumerical(); |
| 90 | + Assert.True(Math.Abs((double)difference.ImaginaryPart) < 1e-9, $"F({to}) - F({from}) is {difference}"); |
| 91 | + Assert.True(Math.Abs((double)difference.RealPart - simpson) < 1e-7, $"F({to}) - F({from}) is {difference}, Simpson's rule {simpson}"); |
| 92 | + } |
| 93 | + } |
| 94 | +} |
0 commit comments