From 60fd0c74dc8b40c0971c7340532314a360855af7 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Thu, 22 Oct 2026 02:55:01 +0000 Subject: [PATCH] Ask a hyperbolic quotient over a linear in its cosine as the cosine's, before the tangent Rubi's 6.2.5 sech(x)^3/(a + b cosh(x)) and 6.5.3 sech(x)^4/(a + b sech(x)) ran past the corpus's budget. SolveARationalFunctionOfTheHyperbolicTangent answered each under the tangent of the half argument, a cube of 1 + t^2 beside a symbolic quadratic, in about 118,000 characters. By Osborn's rule each is a rational function of a cosine alone over a sum linear in it, which partial fractions in the cosine answer: 1/(cos(s)^3 (a + b cos(s))) is 2,121 characters. A third mode of SolveAHyperbolicIntegrandAsATrigonometricOne asks that question beside #2042's, ahead of the tangent, only where s stands in cos(s) alone and the sums below the bar are linear in it, to the first power: over the square of one, written out, 6.7.1's (a + b cosh(x))/(b + a cosh(x))^2 took four times the memory in s. The answer in s goes back as the rule writes every one, and is checked by its derivative. Part of #233. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- BREAKING-CHANGES.md | 16 ++++++ .../Integration/IndefiniteIntegralSolver.cs | 28 +++++++++- .../Integration/Integration.Definition.cs | 3 ++ ...tientOverALinearInItsCosineIntegralTest.cs | 51 +++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 Sources/Tests/UnitTests/Calculus/AHyperbolicQuotientOverALinearInItsCosineIntegralTest.cs diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index cdac56cc4..d67fd2311 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -561,6 +561,22 @@ power, which by parts answers as before ([#233](https://github.com/asc-community | `"x*(a+b*atanh(c*x))^2/(d+c*d*x)^3".ToEntity().Integrate("x")` | `integral(...)` | 731 characters | | `"x*(a+b*acoth(c*x))^2/(d+c*d*x)^3".ToEntity().Integrate("x")` | `integral(...)` | 756 characters | +### A hyperbolic quotient over a linear in its cosine is asked as the cosine's + +**Answers where there were none, and shorter than master's.** Rubi's 6.2.5 `sech(x)^3/(a + b cosh(x))` and 6.5.3 +`sech(x)^4/(a + b sech(x))` ran past the corpus's budget. Under the tangent of the half argument each is a cube of +`1 + t^2` beside a symbolic quadratic, and was answered in about 118,000 characters. By Osborn's rule each is a rational +function of a cosine alone, over a sum linear in it, and that question is now asked first. In the cosine, +`1/(cos(s)^3 (a + b cos(s)))` is a partial fraction in it +([#233](https://github.com/asc-community/AngouriMath/issues/233)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"sech(x)^3/(a+b*cosh(x))".ToEntity().Integrate("x")` | `integral(...)` | 2,396 characters, where before this change master gave 118,366 | +| `"sech(x)^4/(a+b*sech(x))".ToEntity().Integrate("x")` | `integral(...)` | 2,412 characters, where before this change master gave 118,032 | +| `"cosh(x)^3/(a+b*cosh(x))".ToEntity().Integrate("x")` | `integral(...)` | 1,108 characters, where before this change master gave 2,858 | +| `"cosh(x)^2/(a+b*sech(x))".ToEntity().Integrate("x")` | `integral(...)` | 802 characters, where before this change master gave 2,820 | + ### An arctangent over a power of `d (1 + i c x)` is integrated under the tangent **Answers where there were none.** `(a + b atan(c x))^2/(d + i c d x)^2` ran out of time, and so did seven more of diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 0ebd2e20e..b00ba9a9a 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -1430,7 +1430,7 @@ static bool APowerOfAQuotientOfLinearsVanishingAtPlusOrMinusOne(Entity inU, Vari /// s took seconds to decline, five gigabytes for 1/(1 + sinh(x)^3) against 0.2. /// internal static Entity? SolveAHyperbolicIntegrandAsATrigonometricOne(Entity expr, Entity.Variable x, bool integrateByParts, bool rationalToo, - bool overAQuadraticInTheSine = false) + bool overAQuadraticInTheSine = false, bool overALinearInTheCosine = false) { // Only the question asked: a question in s is not cheap to decline, and below the top every // sub-question a route gives up on would ask it. @@ -1498,6 +1498,12 @@ static bool APowerOfAQuotientOfLinearsVanishingAtPlusOrMinusOne(Entity inU, Vari && AFactorAboveAQuadraticInS(inS, s)) return null; var question = MathS.i / slopeOfL * inS; + // Or, asked as early, only a rational function of the cosine alone over a sum linear in it, or in + // the secant: under the tangent of the half argument Rubi's `sech(x)^3/(a + b cosh(x))` is a + // cube of `1 + t^2` beside a symbolic quadratic, answered in 118,366 characters, where in the + // cosine `1/(cos(s)^3 (a + b cos(s)))` is a partial fraction in it. + if (overALinearInTheCosine && !IsARationalFunctionOfTheCosineOverALinearInIt(inS, s)) + return null; // Or the one rule for a rational function of a sine or a cosine over a symbolic quadratic in // it, asked ahead of the exponential substitution and cheap to decline. if ((overAQuadraticInTheSine @@ -1520,6 +1526,26 @@ static bool APowerOfAQuotientOfLinearsVanishingAtPlusOrMinusOne(Entity inU, Vari }).Substitute(s, -MathS.i * linear).InnerSimplified; return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null; + // Whether inS holds s only in cos(s), and below the bar a sum linear in it, to the first power: + // over `b^2 + 2 a b cos(s) + a^2 cos(s)^2`, the square of one, the question in s took four times + // the memory the tangent did, Rubi's 6.7.1 `(a + b cosh(x))/(b + a cosh(x))^2`. + static bool IsARationalFunctionOfTheCosineOverALinearInIt(Entity inS, Variable s) + { + var cosine = MathS.Cos(s); + foreach (var node in inS.Nodes) + if (node.ContainsNode(s) && node != cosine && node != s + && node is not (Sumf or Minusf or Mulf or Divf or Powf(_, Number.Integer))) + return false; + var (_, below) = Functions.SingleQuotient.Of(inS); + var y = Variable.CreateUnique(inS, "y_osb"); + bool ASum(Entity node) => node is Sumf or Minusf && node.ContainsNode(cosine); + bool Linear(Entity sum) => TreeAnalyzer.TryGetPolynomial(sum.Replace(node => node == cosine ? y : node), y, out var read) + && read.Keys.Max()!.Equals(EInteger.One); + var sums = Mulf.LinearChildren(below).Where(ASum).ToList(); + return sums.Count > 0 && sums.All(Linear) + && !Mulf.LinearChildren(below).Any(factor => factor is Powf(var @base, _) && ASum(@base)); + } + // Whether inS, rational in cos(s) alone or sin(s) alone, has a denominator of degree three or // more that t = tan(s/2) or t = tan(s) leaves with an irreducible factor of degree above two, // which the rational rules write only as a sum over roots. The cosine, and the square of the diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index 2341084f9..83739326f 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -1242,6 +1242,9 @@ internal static bool ASumOverRootsWouldAnswer // `sinh(x)^2/(a + b sinh(x) + c sinh(x)^2)` is a quartic with symbols, and its partial // fractions ran past the budget, where the sine's rule answers the question in s at once. if ((answer = IndefiniteIntegralSolver.SolveAHyperbolicIntegrandAsATrigonometricOne(expr, x, integrateByParts, rationalToo: true, overAQuadraticInTheSine: true)) is { }) return answer; + // And one of the cosine alone over a sum linear in it, which the tangent made a cube of + // `1 + t^2` beside a symbolic quadratic: `sech(x)^3/(a + b cosh(x))` in 118,366 characters. + if ((answer = IndefiniteIntegralSolver.SolveAHyperbolicIntegrandAsATrigonometricOne(expr, x, integrateByParts, rationalToo: true, overALinearInTheCosine: true)) is { }) return answer; if ((answer = IndefiniteIntegralSolver.SolveARationalFunctionOfTheHyperbolicTangent(expr, x, integrateByParts)) is { }) return answer; // A rational function of a sine or a cosine over a quadratic in it with a symbol // among the coefficients, split over the quadratic's two roots: under the diff --git a/Sources/Tests/UnitTests/Calculus/AHyperbolicQuotientOverALinearInItsCosineIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AHyperbolicQuotientOverALinearInItsCosineIntegralTest.cs new file mode 100644 index 000000000..d3b365087 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AHyperbolicQuotientOverALinearInItsCosineIntegralTest.cs @@ -0,0 +1,51 @@ +// +// 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 rational function of the hyperbolic cosine alone over a sum linear in it, or in the secant, + /// asked by Osborn's rule as one of the cosine before the tangent of the half argument: under that + /// tangent Rubi's 6.2.5 sech(x)^3/(a + b cosh(x)) and 6.5.3 sech(x)^4/(a + b sech(x)) + /// are a cube of 1 + t^2 beside a symbolic quadratic, and were answered in 118,366 and + /// 118,032 characters, past the corpus's budget. + /// #233 + /// + [Trait("Area", "Calculus")] + public sealed class AHyperbolicQuotientOverALinearInItsCosineIntegralTest + { + [Theory] + // Rubi 6.2.5:89. + [InlineData("sech(x)^3/(a + b*cosh(x))")] + // Rubi 6.5.3:139. + [InlineData("sech(x)^4/(a + b*sech(x))")] + [InlineData("cosh(x)^2/(a + b*sech(x))")] + public void IsAnsweredInTheCosine(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + var text = integral.Stringize(); + Assert.DoesNotContain("integral(", text); + Assert.True(text.Length < 5000, $"{integrand} came back in {text.Length} characters"); + Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + foreach (var at in new[] { -2.2, -0.9, 0.3, 1.4, 2.1 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + var got = derivative.Substitute("x", at).EvalNumerical(); + Assert.False(got.IsNaN, $"d/dx of the antiderivative of {integrand} has no value at x = {at}, where the integrand is {want}"); + Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) + < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)), + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + } + } +}