diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index eabde4cfc..afb191c17 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -457,6 +457,19 @@ back after. Each answer is in three cases by the sign of `4 a/c - (b/c)^2` | `"(a + b*x^3)^(1/3)/(x^2*(a*d - b*d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 773 characters | | `"1/(x^2*(1 - x^3)^(2/3)*(1 + x^3))".ToEntity().Integrate("x")` | `integral(...)` | 471 characters | +### A function of an inverse function of a linear beside powers of multiples of that linear is integrated under it + +**Answers where there were none.** `(c k + d k x)^3 (a + b asinh(c + d x))^2` ran out of time, and so did three more +of Rubi's 7.1.5 and 7.2.5. Under `u = c + d x` it is `k^3 u^3 (a + b asinh(u))^2`, which is answered, divided by `d` +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"(c*k+d*k*x)^3*(a+b*asinh(c+d*x))^2".ToEntity().Integrate("x")` | `integral(...)` | 3,574 characters | +| `"(c*k+d*k*x)^3*(a+b*asinh(c+d*x))^3".ToEntity().Integrate("x")` | `integral(...)` | 6,188 characters | +| `"(c*k+d*k*x)^3*(a+b*asinh(c+d*x))^4".ToEntity().Integrate("x")` | `integral(...)` | 9,539 characters | +| `"(c*k+d*k*x)*(a+b*acosh(c+d*x))^4".ToEntity().Integrate("x")` | `integral(...)` | 3,607 characters | + ### `a + i a tan` beside the tangent's differential is lowered over its conjugate **Answers where there were none.** `sqrt(a + i a tan(x))/(c + d tan(x))^(3/2)` ran past thirty seconds: diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 903dd96a1..6a2d91d1f 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -26647,6 +26647,72 @@ static void Add(Dictionary into, int k, ERational value) return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer; } + /// + /// A function of an inverse function of a linear L = c + d x times powers of multiples of + /// L, (c e + d e x)^3 (a + b arsinh(c + d x))^2, under u = L: each power of + /// k L is (k u)^m, the inverse function's argument is u, and the integral is + /// the one in u over d. + /// + /// + /// x^3 (a + b arsinh(x))^2 is answered in half a second, and the rules timed out on + /// Rubi's 7.1.5 (c e + d e x)^3 (a + b arsinh(c + d x))^2, which is that in u. + /// https://github.com/asc-community/AngouriMath/issues/718 + /// + internal static Entity? SolveAFunctionOfAnInverseOfALinearBesideItsPowersUnderTheLinear(Entity expr, Entity.Variable x) + { + if (!Integration.AnsweringTheQuestionAsked) + return null; + // The linear: the argument of an inverse function, with a constant term, as in `arsinh(c + d x)`, + // which arrives as `ln(c + d x + sqrt((c + d x)^2 + 1))`. + Entity? linear = null; + foreach (var node in expr.Nodes) + { + Entity? argument = node switch + { + Arcsinf(var y) => y, Arccosf(var y) => y, Arctanf(var y) => y, + _ => null + }; + // The logarithm's argument without its root: `c + d x` of `ln(c + d x + sqrt((c + d x)^2 + 1))`. + if (argument is null && node is Logf(var b0, var of) && b0 == MathS.e && of is Sumf) + { + var terms = Sumf.LinearChildren(of).ToList(); + var roots = terms.Where(term => term is Powf(_, Number.Rational { ERational.Denominator: var den }) && den.Equals(EInteger.FromInt32(2))).ToList(); + if (roots.Count == 1) + argument = terms.Where(term => term != roots[0]).Aggregate((Entity)Number.Integer.Zero, (sum, term) => sum == Number.Integer.Zero ? term : sum + term); + } + if (argument is null || !argument.ContainsNode(x)) + continue; + if (!TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope0, out var intercept0) + || Functions.PartialFractions.IsZeroAsAValue(slope0) || Functions.PartialFractions.IsZeroAsAValue(intercept0)) + continue; + linear = argument; + break; + } + if (linear is null) + return null; + if (!TreeAnalyzer.TryGetPolyLinear(linear, x, out var slope, out var intercept) || slope is null || intercept is null) + return null; + var u = Variable.CreateUnique(expr, "u_lin"); + // Every node that is a constant multiple of L becomes that multiple of u. + var inU = expr.Replace(node => + { + if (node == linear) + return u; + if (node is not (Sumf or Minusf) || !node.ContainsNode(x) + || !TreeAnalyzer.TryGetPolyLinear(node, x, out var s1, out var c1) || s1 is null || c1 is null + || Functions.PartialFractions.IsZeroAsAValue(s1)) + return node; + var k = (s1 / slope).InnerSimplified; + return Functions.PartialFractions.IsZeroAsAValue((c1 - k * intercept).InnerSimplified) ? k * u : node; + }); + if (inU.ContainsNode(x)) + return null; + if (Integration.ComputeAsAQuestionOfItsOwn(inU.InnerSimplified, u, integrateByParts: true) is not { } integrated) + return null; + var answer = (integrated.Substitute(u, linear) / slope).InnerSimplified; + return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer; + } + /// /// A rational function of a sine, or of a cosine, over a quadratic in it with a symbol /// among the coefficients: sin(x)/(a + b sin(x) + c sin(x)^2), and the same with diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index fee1b7b9e..d320fdfda 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -680,6 +680,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr) // A power of a sine, cosine or hyperbolic one over a binomial in a power of it, over the // binomial's roots, before the rules that spent the budget on `sinh(y)^4/(a + b sinh(y)^3)`. if ((answer = IndefiniteIntegralSolver.SolveAPowerOverABinomialInAPowerOfASineOverItsRoots(expr, x)) is { }) return answer; + // A function of an inverse function of c + d x beside powers of multiples of it, under + // u = c + d x, before the rules that timed out on `(c e + d e x)^3 (a + b arsinh(c + d x))^2`. + if ((answer = IndefiniteIntegralSolver.SolveAFunctionOfAnInverseOfALinearBesideItsPowersUnderTheLinear(expr, x)) is { }) return answer; // A power of x times an inverse tangent and the logarithm of its quadratic, by parts with the // two remainders that have no antiderivative apiece paired, before the rules that timed out. if ((answer = IndefiniteIntegralSolver.SolveAPowerOfXTimesAnArctangentAndTheLogarithmOfItsQuadratic(expr, x)) is { }) return answer; diff --git a/Sources/Tests/UnitTests/Calculus/AnInverseOfALinearBesideItsPowersIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnInverseOfALinearBesideItsPowersIntegralTest.cs new file mode 100644 index 000000000..a6db892e6 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AnInverseOfALinearBesideItsPowersIntegralTest.cs @@ -0,0 +1,58 @@ +// +// 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 function of an inverse function of c + d x beside powers of multiples of it, under + /// u = c + d x: Rubi's 7.1.5 (c e + d e x)^3 (a + b arsinh(c + d x))^2 and 7.2.5 + /// (c e + d e x)(a + b arcosh(c + d x))^4, which timed out. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class AnInverseOfALinearBesideItsPowersIntegralTest + { + private static void DifferentiatesBack(string integrand, params (string, double)[] pins) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + + var pinned = integral.Substitute("C", 0); + Entity original = integrand.ToEntity(); + foreach (var (name, value) in pins) + { + pinned = pinned.Substitute(name, value); + original = original.Substitute(name, value); + } + var derivative = pinned.Differentiate("x"); + foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 }) + { + var got = derivative.Substitute("x", at).EvalNumerical(); + var want = original.Substitute("x", at).EvalNumerical(); + var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart); + var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)); + Assert.True(difference / scale < 1e-8, + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + } + + // c + d x above 1 at every sampled x, where the inverse hyperbolic cosine is real. + [Theory] + [InlineData("(c*k+d*k*x)^3*(a+b*asinh(c+d*x))^2")] + [InlineData("(c*k+d*k*x)^3*(a+b*asinh(c+d*x))^4")] + [InlineData("(c*k+d*k*x)*(a+b*acosh(c+d*x))^4")] + public void IsAnsweredAndDifferentiatesBack(string integrand) + { + DifferentiatesBack(integrand, ("a", 1.3), ("b", 0.7), ("c", 3), ("d", 0.5), ("k", 1.5)); + DifferentiatesBack(integrand, ("a", -0.5), ("b", 2), ("c", 4), ("d", -0.6), ("k", -0.7)); + } + } +}