diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 33abc4553..3adcb9293 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -542,6 +542,18 @@ becomes a polynomial in `u` times exponentials and trigonometric functions, and | `"(a+b*atan(c*x))^2/(1+i*c*x)^4".ToEntity().Integrate("x")` | `integral(...)` | 731 characters | | `"(a+b*atan(c*x))^3/(d+i*c*d*x)^4".ToEntity().Integrate("x")` | `integral(...)` | 1,202 characters | +### Two roots of linears written as an inverse hyperbolic cosine's radicand are asked without a condition + +**Answers where there were none.** Rubi's 7.2.5 `x/(acosh(x) sqrt(x - 1) sqrt(x + 1))` ran past the budget. The pair of roots +is written as the root of `x^2 - 1` that `acosh(x)` holds, with the factor `K` that is constant where it is defined. +The inner simplification then wrote the rewritten question with `provided not x - 1 = 0` over the whole of it, and +nothing below read that. The point it excludes is outside the integrand's domain, and the question is now asked +without it ([#233](https://github.com/asc-community/AngouriMath/issues/233)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"x/(acosh(x)*sqrt(-1+x)*sqrt(1+x))".ToEntity().Integrate("x")` | `integral(...)` | 93 characters, `K Chi(acosh(x))` | + ### An arcoth of a linear is integrated as its artanh plus a constant **Answers where there were none.** `acoth(x)^2/(1 - x^2)^2`, Rubi's 7.4.1, was declined, where diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 743b3329e..a70cb8786 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -43437,7 +43437,12 @@ Entity WithTheExponentials(Entity e, Entity? one) (rewritten, root) = (paired, pairRoot); if (rewritten == expr) return null; - var simplified = rewritten.InnerSimplified; + // Without a condition over the whole of it, which the inner simplification writes where a + // linear stands on both sides of the bar and nothing below reads: Rubi's 7.2.5 + // `x/(acosh(x) sqrt(x - 1) sqrt(x + 1))` became `x/(K sqrt(x^2 - 1) acosh(x)) provided not + // x - 1 = 0`, and ran past the budget, where without the condition it is a `Chi`. The point + // it excludes is outside the integrand's domain. + var simplified = Functions.PartialFractions.Bare(rewritten.InnerSimplified); if (simplified == expr || simplified == expr.InnerSimplified) return null; if (Integration.ComputeAsTheSameQuestion(simplified, x, integrateByParts) is not { } answer) diff --git a/Sources/Tests/UnitTests/Calculus/TheRadicandRewritingAskedWithoutItsConditionIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/TheRadicandRewritingAskedWithoutItsConditionIntegralTest.cs new file mode 100644 index 000000000..e3f5f19e8 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/TheRadicandRewritingAskedWithoutItsConditionIntegralTest.cs @@ -0,0 +1,44 @@ +// +// 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 +{ + /// + /// Two roots of linears written as the radicand an inverse hyperbolic cosine holds, asked without + /// the condition the inner simplification writes over the rewriting: Rubi's 7.2.5 + /// x/(acosh(x) sqrt(x - 1) sqrt(x + 1)) was asked as ... provided not x - 1 = 0 and ran + /// past the budget, where it is a hyperbolic cosine integral. + /// #233 + /// + [Trait("Area", "Calculus")] + public sealed class TheRadicandRewritingAskedWithoutItsConditionIntegralTest + { + [Theory] + [InlineData("x/(acosh(x)*sqrt(-1 + x)*sqrt(1 + x))")] + [InlineData("x^2/(acosh(x)*sqrt(-1 + x)*sqrt(1 + x))")] + public void IsAnswered(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + var text = integral.Stringize(); + Assert.DoesNotContain("integral(", text); + var derivative = integral.Substitute("C", 0).Differentiate("x"); + var original = integrand.ToEntity(); + foreach (var at in new[] { 1.2, 1.7, 2.3, 3.1, 4.4 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + var got = derivative.Substitute("x", at).EvalNumerical(); + 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}"); + } + } + } +}