diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 93b9519a0..03016eb5f 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -2760,6 +2760,21 @@ whatever `a` is ([#718](https://github.com/asc-community/AngouriMath/issues/718) | `"sqrt(a + i*a*sinh(g + f*x))/x".ToEntity().Integrate("x")` | `integral(...)` | `Shi` and `Chi` of half of `f x` | | `"1/sqrt(a + i*a*sinh(g + f*x))".ToEntity().Integrate("x")` | `integral(...)` | arctangents and logarithms of `e^((g + f x)/2)` | +### A quadratic with an even root of a symbol among its coefficients is integrated where the root is imaginary + +**Answers where there were none.** `1/(b (c + d x)^2 + sqrt(-a))` was answered by arms on the sign +of its discriminant `4 sqrt(-a) b d^2`, which is imaginary for every `a > 0` and has no sign there, +so the answer had a value for no positive `a`. The arm off the real line that a quadratic with `i` +among its coefficients takes, the arctangent, which uses nothing about its root but +`sqrt(q)^2 = q`, is taken where an even root of a symbol is among them too. An odd root is real +here and a root seen only squared leaves the discriminant real, so neither takes it. Rubi's 1.3.1 +([#233](https://github.com/asc-community/AngouriMath/issues/233)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"1/(b*(c + d*x)^2 + sqrt(-a))".ToEntity().Integrate("x")` | 920 characters, with no value at a real `x` for `a = 1.07` or `a = -1.3`; 570, with none for `a > 0`, on the unreleased master | 608 characters, the arctangent `provided not D in RR or D > 0` | +| `"1/(x^2 + sqrt(a))".ToEntity().Integrate("x")` | 297 characters, with no value at a real `x` for `a = 1.07` or `a = -1.3`; 261, with none for `a < 0`, on the unreleased master | 286 characters | + ### The arm for a quadratic off the real line excludes the arms on the sign of its discriminant **Shorter answers.** An antiderivative over a quadratic with `i` among its coefficients has an arm diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs index d34fc7247..d3ff34dfb 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs @@ -819,8 +819,8 @@ private static Entity IntegrateOverRootOfQuadratic( /// /// The arm that a piecewise on a sign owes a quadratic off the real line, answered by /// wherever one of is not real; - /// where none of them holds the imaginary unit, and the sign arms - /// are left to decide. + /// where none of them holds the imaginary unit or an even root of a + /// symbol, and the sign arms are left to decide. /// /// /// The sign arms choose between forms by which is real where the integrand is, and off @@ -834,18 +834,43 @@ private static Entity IntegrateOverRootOfQuadratic( /// came back as minus its integrand below -2. The arctangent and the logarithm use nothing about their root but /// sqrt(q)^2 = q, and are antiderivatives off the real line as well. /// https://github.com/asc-community/AngouriMath/issues/1598 + /// + /// An even root of a symbol leaves the real line as the imaginary unit does, on half of the + /// symbol's values: 1/(b (c + d x)^2 + sqrt(-a)) has the discriminant + /// 4 sqrt(-a) b d^2, imaginary for every a > 0, and was answered with + /// nothing there, an antiderivative of a function with one. Rubi's 1.3.1. An odd root is + /// real here, (-8)^(1/3) = -2, and is left to the sign arms, and so is a root seen + /// only squared: the discriminant of a b - b^2 x^2 - x sqrt(b^2 - 4 a b^3) holds + /// sqrt(b^2 - 4 a b^3)^2, and is real. + /// https://github.com/asc-community/AngouriMath/issues/233 + /// /// private static Entity.Providedf? ArmOffTheRealLine(Entity form, params Entity[] coefficients) { Entity? offTheRealLine = null; foreach (var coefficient in coefficients) - if (coefficient.InnerSimplified.Nodes.Any(node => node is Entity.Number.Complex number && !number.ImaginaryPart.EDecimal.IsZero)) + { + var simplified = coefficient.InnerSimplified; + if (simplified.Nodes.Any(node => node is Entity.Number.Complex number && !number.ImaginaryPart.EDecimal.IsZero) + || AnEvenRootOfASymbolNotSquared(simplified)) offTheRealLine = offTheRealLine is null ? !coefficient.In(AngouriMath.Core.Domain.Real) : offTheRealLine | !coefficient.In(AngouriMath.Core.Domain.Real); + } return offTheRealLine is null ? null : new Entity.Providedf(form, offTheRealLine); } + // An even root of something with a symbol in it, anywhere but as the base of an even power. + private static bool AnEvenRootOfASymbolNotSquared(Entity expr) => expr switch + { + Entity.Powf(var root, Entity.Number.Integer { EInteger.IsEven: true }) when IsAnEvenRootOfASymbol(root) => false, + _ when IsAnEvenRootOfASymbol(expr) => true, + _ => expr.DirectChildren.Any(AnEvenRootOfASymbolNotSquared), + }; + + private static bool IsAnEvenRootOfASymbol(Entity expr) + => expr is Entity.Powf(var radicand, Entity.Number.Rational { ERational.Denominator.IsEven: true }) && radicand.Vars.Any(); + /// /// ∫ (px + q)/(ax^2 + bx + c) dx, by writing the numerator as a multiple of the /// denominator's derivative plus a constant: diff --git a/Sources/Tests/UnitTests/Calculus/AnEvenRootOfASymbolInAQuadraticIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnEvenRootOfASymbolInAQuadraticIntegralTest.cs new file mode 100644 index 000000000..717d73d1d --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AnEvenRootOfASymbolInAQuadraticIntegralTest.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 quadratic with an even root of a symbol among its coefficients is off the real line on + /// half of that symbol's values, as one with i in it is, and takes the arm there: + /// 1/(b (c + d x)^2 + sqrt(-a)) has the discriminant 4 sqrt(-a) b d^2, which has no + /// sign for a > 0, and the answer had no value for any positive a. Rubi's 1.3.1. + /// #233 + /// + [Trait("Area", "Calculus")] + public sealed class AnEvenRootOfASymbolInAQuadraticIntegralTest + { + [Theory] + [InlineData("1/(b*(c + d*x)^2 + sqrt(-a))", "1.3")] + [InlineData("1/(b*(c + d*x)^2 + sqrt(-a))", "-1.3")] + [InlineData("1/(x^2 + sqrt(a))", "1.3")] + [InlineData("1/(x^2 + sqrt(a))", "-1.3")] + [InlineData("x/(x^2 + x + sqrt(a))", "-1.3")] + public void DifferentiatesBackOnEitherSideOfTheRoot(string integrand, string a) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + Entity Pinned(Entity e) => e.Substitute("a", a.ToEntity()).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("d", 0.3); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + var compared = 0; + foreach (var at in new[] { -1.7, -0.6, 0.3, 0.8, 1.3, 2.9 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + if (want.IsNaN) + continue; + var got = derivative.Substitute("x", at).EvalNumerical(); + compared++; + var size = Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)); + Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) < 1e-9 * size, + $"d/dx of the antiderivative of {integrand} at a = {a} is {got} at x = {at}, where the integrand is {want}"); + } + Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand} at a = {a}"); + } + + // An odd root is real here, (-8)^(1/3) = -2, and leaves the arms on the sign as they were. + [Fact] + public void AnOddRootTakesNoArmOffTheRealLine() + => Assert.DoesNotContain("in RR", "1/(x^2 + a^(1/3))".ToEntity().Integrate("x").Stringize()); + } +}