diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 9efef8955..2321ca0cd 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -419,6 +419,20 @@ leading coefficient once expanded, and declined: it is read expanded and bare no | `"sin(x)/(a+b*sin(x))^3".Integrate("x")`, Rubi's 4.1.2.1 row 242 | `integral(...)` | the antiderivative | | `"4*x*(1+x^2)/(a*x^2+2*b*x+a)^3".Integrate("x")` | `integral(...)` | the antiderivative | +### `x^2` over a three-quarter power of a quadratic binomial beside another is integrated where that is elementary + +**Answers where there were none.** `x^2/((A + B x^2)^(3/4) (C + D x^2))` at `B C - 2 A D = 0`, Rubi's +1.1.2.4, is the difference of the two functions whose sum answers `1/((A + B x^2)^(1/4) (C + D x^2))` +at that ratio (the entry below), with another constant, and was declined. It is answered by the +signs of `A` and `B` now, each form checked at points in its sign case +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"x^2/((a - b*x^2)^(3/4)*(2*a - b*x^2))".ToEntity().Integrate("x")` | `integral(...)` | an arctangent and an inverse hyperbolic tangent, by the signs of `a` and `b` | +| `"x^2/((-2 + 3*x^2)*(-1 + 3*x^2)^(3/4))".ToEntity().Integrate("x")` | `integral(...)` | `(arctan(u) - artanh(u))/(3 sqrt(6))`, `u = sqrt(3) x/(sqrt(2) (-1 + 3 x^2)^(1/4))` | +| `"x^2/((2 - 3*x^2)^(3/4)*(4 - 3*x^2))".ToEntity().Integrate("x")` | `integral(...)` | an arctangent and an inverse hyperbolic tangent | + ### A cube or fourth root of a quadratic binomial beside another is integrated where that is elementary **Answers where there were none.** `1/((A + B x^2)^(1/3) (C + D x^2))` is an elliptic integral diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 858c776e3..e9de4b8d8 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -4372,13 +4372,33 @@ private static bool TryReadAsABinomialIn(Entity expr, Entity.Variable x, out Ent /// it holds. /// https://github.com/asc-community/AngouriMath/issues/718 /// + /// + /// x^2/((A + B x^2)^(3/4) (C + D x^2)) at the same ratio is the same two functions' + /// difference where the quarter power is their sum, with another constant -- Rubi's + /// 1.1.2.4, x^2/((a - b x^2)^(3/4) (2 a - b x^2)), which was declined: + /// + /// A > 0, B > 0: -(atan(A^(3/4) (1 + s/sqrt(A))/(x y r)) - artanh(A^(3/4) (1 - s/sqrt(A))/(x y r)))/(A^(1/4) r^3) + /// A > 0, B < 0: (atan(A^(3/4) (1 - s/sqrt(A))/(x y r)) - artanh(A^(3/4) (1 + s/sqrt(A))/(x y r)))/(A^(1/4) r^3) + /// A < 0: (atan(u) - artanh(u))/((-A)^(1/4) sqrt(2) r^3) + /// + /// each times B/D as before, the constants solved for from the two functions' + /// derivatives and checked at points in every sign case. + /// /// internal static Entity? SolveAnEllipticLookingQuotientOfBinomials(Entity expr, Entity.Variable x) { var (numerator, denominator) = Functions.SingleQuotient.Of(expr); - if (numerator.ContainsNode(x)) - return null; + // Or c x^2 above, beside a three-quarter power below. + var squareAbove = false; Entity constant = numerator; + if (numerator.ContainsNode(x)) + { + if (!TreeAnalyzer.TryGetPolynomial(numerator, x, out var above) || above.Count != 1 + || !above.TryGetValue(EInteger.FromInt32(2), out var squared) || squared.ContainsNode(x)) + return null; + squareAbove = true; + constant = squared; + } Entity? radicand = null; var order = 0; Entity? other = null; @@ -4386,8 +4406,11 @@ private static bool TryReadAsABinomialIn(Entity expr, Entity.Variable x, out Ent { if (!factor.ContainsNode(x)) constant = constant / factor; - else if (radicand is null && factor is Powf(var @base, Number.Rational power) && power.ERational.Numerator.Equals(EInteger.One) - && (power.ERational.Denominator.Equals(EInteger.FromInt32(3)) || power.ERational.Denominator.Equals(EInteger.FromInt32(4)))) + else if (radicand is null && factor is Powf(var @base, Number.Rational power) + && (squareAbove + ? power.ERational.Equals(ERational.Create(3, 4)) + : power.ERational.Numerator.Equals(EInteger.One) + && (power.ERational.Denominator.Equals(EInteger.FromInt32(3)) || power.ERational.Denominator.Equals(EInteger.FromInt32(4))))) { radicand = @base; order = power.ERational.Denominator.ToInt32Unchecked(); @@ -4427,7 +4450,34 @@ Entity Negative(Entity q) => twelfth * q / a * (MathS.Hyperbolic.Artanh(MathS.Po var ratio = LowestOverTheSymbols(B / A); answer = BySign(ratio, Positive(MathS.Sqrt(ratio)), Negative(MathS.Sqrt(LowestOverTheSymbols(-ratio)))); } - else if (order == 4 && VanishesIdentically(B * C - 2 * A * D)) + else if (order == 4 && squareAbove && VanishesIdentically(B * C - 2 * A * D)) + { + var s = MathS.Sqrt(radicand); + var threeQuarters = Number.Rational.Create(3, 4); + var quarter = Number.Rational.Create(1, 4); + Entity BothPositive() + { + var r = MathS.Sqrt(B); + return -(MathS.Arctan(MathS.Pow(A, threeQuarters) * (1 + s / MathS.Sqrt(A)) / (x * y * r)) + - MathS.Hyperbolic.Artanh(MathS.Pow(A, threeQuarters) * (1 - s / MathS.Sqrt(A)) / (x * y * r))) / (MathS.Pow(A, quarter) * MathS.Pow(r, 3)); + } + Entity SlopeNegative() + { + var r = MathS.Sqrt(-B); + return (MathS.Arctan(MathS.Pow(A, threeQuarters) * (1 - s / MathS.Sqrt(A)) / (x * y * r)) + - MathS.Hyperbolic.Artanh(MathS.Pow(A, threeQuarters) * (1 + s / MathS.Sqrt(A)) / (x * y * r))) / (MathS.Pow(A, quarter) * MathS.Pow(r, 3)); + } + Entity ConstantNegative() + { + var r = MathS.Sqrt(B); + var u = x * r / (MathS.Pow(-A, quarter) * y * MathS.Sqrt(2)); + return (MathS.Arctan(u) - MathS.Hyperbolic.Artanh(u)) / (MathS.Pow(-A, quarter) * MathS.Sqrt(2) * MathS.Pow(r, 3)); + } + var toTheRatio = LowestOverTheSymbols(B / D); + var form = BySigns(A, B, BothPositive(), SlopeNegative(), ConstantNegative()); + answer = toTheRatio == Number.Integer.One ? form : toTheRatio * form; + } + else if (order == 4 && !squareAbove && VanishesIdentically(B * C - 2 * A * D)) { var s = MathS.Sqrt(radicand); var threeQuarters = Number.Rational.Create(3, 4); diff --git a/Sources/Tests/UnitTests/Calculus/EllipticLookingQuotientOfBinomialsTest.cs b/Sources/Tests/UnitTests/Calculus/EllipticLookingQuotientOfBinomialsTest.cs index 15283ea63..5da884f8f 100644 --- a/Sources/Tests/UnitTests/Calculus/EllipticLookingQuotientOfBinomialsTest.cs +++ b/Sources/Tests/UnitTests/Calculus/EllipticLookingQuotientOfBinomialsTest.cs @@ -76,6 +76,21 @@ public void AtTheRatioWhereItIsElementary(string integrand, double a, double b, public void WithNumbers(string integrand, double[] points) => DifferentiatesBack(integrand, points); + /// + /// x^2 over the three-quarter power at the fourth root's ratio, the same two + /// functions' difference: for each sign of b with a positive, for a + /// negative, and with numbers. Rubi's 1.1.2.4. + /// + [Theory] + [InlineData("x^2/((a + b*x^2)^(3/4)*(2*a + b*x^2))", 1.3, 0.7, new[] { 0.3, 0.9, 1.7, -0.6 })] + [InlineData("x^2/((a + b*x^2)^(3/4)*(2*a + b*x^2))", 1.3, -0.7, new[] { 0.3, 0.9, 1.1, -0.6 })] + [InlineData("x^2/((a + b*x^2)^(3/4)*(2*a + b*x^2))", -1.3, 0.7, new[] { 1.5, 2.0, 2.6, -1.8 })] + [InlineData("x^2/((a - b*x^2)^(3/4)*(2*a - b*x^2))", 1.3, 0.7, new[] { 0.3, 0.9, 1.1, -0.6 })] + [InlineData("x^2/((-2 + 3*x^2)*(-1 + 3*x^2)^(3/4))", 0, 0, new[] { 0.7, 1.1, 1.9, -1.3 })] + [InlineData("x^2/((2 - 3*x^2)^(3/4)*(4 - 3*x^2))", 0, 0, new[] { 0.2, 0.5, 0.7, -0.4 })] + public void XSquaredOverTheThreeQuarterPower(string integrand, double a, double b, double[] points) + => DifferentiatesBack(integrand, points, ("a", a), ("b", b)); + /// /// Across 0, where the cube root's answers are written over x: the value is the one /// quadrature gives.