diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index a92d42e79..cc161e3d1 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -150,7 +150,9 @@ as `a - b = 0`, as a statement's own solve reads it: `"x + 1 = 3".Solve("x")` wa `x^31 e^(-x)` is, whose sum of 32 terms is `-gamma(32, x)` now. A definite integral of such an answer that is undefined at a bound is left as written. And beside a power of a sine and a cosine, which Euler's formula writes as those exponentials, the gamma functions of an imaginary argument, and beside a hyperbolic function of a power of - `x`, as `x sinh(c + d x^3)`. + `x`, as `x sinh(c + d x^3)`. A polynomial beside a symbolic power of a logarithm of one shifted linear, or an + exponential of a power of one, is written in that linear, where each term is one of these: + `x (a + b ln(c (d + e x)))^p` and `x^2 f^(c (a + b x)^3)`. ([#2075](https://github.com/asc-community/AngouriMath/issues/2075)) | Input | Was (2.6.0) | Now | diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 4b4ca4295..37f4b799c 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -18148,6 +18148,96 @@ bool Read(Entity node, bool insideASum) return back.Nodes.Any(node => node == MathS.NaN) ? null : back; } + /// + /// A polynomial beside a function of one shifted linear a + b x that parts cannot reach, written in + /// u = a + b x: a power that is not whole of something holding a logarithm of it, x (a + b ln(c (d + e x)))^p, + /// or an exponential of a function of it that is not linear, x^2 f^(c (a + b x)^3). In u the polynomial is one + /// in u, and each of its terms beside the function is answered in the gamma function: u (a + b ln(c u))^p and + /// u^2 f^(c u^3). Only where the offset is not zero, since in u it is, and this declines there. Rubi's 3.3, 3.4 + /// and 2.3. https://github.com/asc-community/AngouriMath/issues/2075 + /// + internal static Entity? SolveAPolynomialBesideATranscendentalPowerOfAShiftedLinear(Entity expr, Entity.Variable x, bool integrateByParts) + { + if (!Integration.AnsweringTheQuestionAskedOrOneBelow || expr is not (Mulf or Divf)) + return null; + // The function first, by its shape, before any factor is read as a polynomial: that expands it, and asked of + // every product an algebraic integrand is, it was five per cent of the corpus's time. + Entity? transcendental = null; + foreach (var factor in Mulf.LinearChildren(expr)) + if (factor.ContainsNode(x) && IsATranscendentalPowerOfSomethingInX(factor, x)) + { + if (transcendental is not null) + return null; + transcendental = factor; + } + if (transcendental is null) + return null; + Entity? polynomial = null; + foreach (var factor in Mulf.LinearChildren(expr)) + { + if (!factor.ContainsNode(x) || ReferenceEquals(factor, transcendental)) + continue; + if (ThePolynomialIn(factor, x) is not null) + polynomial = polynomial is null ? factor : polynomial * factor; + else + return null; + } + if (transcendental is null || polynomial is null + || TheOneLinearIn(transcendental, x) is not { } linear + || !TreeAnalyzer.TryGetPolyLinear(linear, x, out var slope, out var offset) + || slope.ContainsNode(x) || offset.ContainsNode(x) || TreeAnalyzer.IsZero(slope) || VanishesIdentically(offset)) + return null; + // x = (u - a)/b, so that the function is of u, and dx = du/b: the whole integrand, its constants with it. + var u = Variable.CreateUnique(expr, "u_linear"); + if (transcendental.Replace(node => node == linear ? u : node).ContainsNode(x)) + return null; + var inU = expr.Replace(node => node == linear ? u : node).Substitute(x, (u - offset) / slope); + if (inU.ContainsNode(x) + || Integration.ComputeAsAQuestionOfItsOwn((inU / slope).InnerSimplified, u, integrateByParts) is not { } answer) + return null; + var back = answer.Substitute(u, linear); + return back.Nodes.Any(node => node == MathS.NaN) ? null : back; + } + + /// + /// Whether is a power that is not whole of something holding a logarithm of something in + /// , or an exponential of something in that is not linear in it. + /// + private static bool IsATranscendentalPowerOfSomethingInX(Entity factor, Entity.Variable x) => factor switch + { + // An exponential whose exponent holds a sum in x, c (a + b x)^3 or c/(a + b x), read as not linear only then. + Powf(var @base, var exponent) when !@base.ContainsNode(x) && exponent.ContainsNode(x) + => exponent.Nodes.Any(node => node is Sumf or Minusf && node.ContainsNode(x)) + && !TreeAnalyzer.TryGetPolyLinear(exponent, x, out _, out _), + Powf(var @base, var exponent) when !exponent.ContainsNode(x) && exponent is not Number.Integer + => @base.Nodes.Any(node => node is Logf(_, var antilogarithm) && antilogarithm.ContainsNode(x)), + _ => false + }; + + /// + /// The one sum linear in that every in stands in, + /// outermost, or . + /// + private static Entity? TheOneLinearIn(Entity expr, Entity.Variable x) + { + Entity? found = null; + bool Read(Entity node) + { + if (!node.ContainsNode(x)) + return true; + if (node is Sumf or Minusf && TreeAnalyzer.TryGetPolyLinear(node, x, out var slope, out _) && !slope.ContainsNode(x)) + { + if (found is null) + found = node; + return node == found; + } + if (node == x) + return false; + return node.DirectChildren.All(Read); + } + return Read(expr) ? found : null; + } + /// /// A sine or a cosecant of twice an argument beside functions of the argument, written as the /// product it is: sin(2A) = 2 sin(A) cos(A), so that every trigonometric function is of diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index 5330f7a75..7a3f52a12 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -935,6 +935,8 @@ internal static bool ASumOverRootsWouldAnswer // A polynomial beside error functions of one shifted linear, written in it: by parts in // x leaves a Gaussian of the shifted linear beside a polynomial, which nothing reads. if ((answer = IndefiniteIntegralSolver.SolveAPolynomialBesideErrorFunctionsOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer; + // And beside a symbolic power of a logarithm of one, or an exponential of a power of one, the same way. + if ((answer = IndefiniteIntegralSolver.SolveAPolynomialBesideATranscendentalPowerOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer; // A half-odd power of a ± a sin(y) beside powers of other linears in the sine, by the half // angle, before the polynomial term: beneath it the substitution search ran out of time. if ((answer = IndefiniteIntegralSolver.SolveAHalfOddPowerOfOnePlusMinusASineBesidePowersOfLinearsInIt(expr, x, integrateByParts)) is { }) return answer; diff --git a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs index 34b64892e..ef6a87725 100644 --- a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs @@ -86,6 +86,7 @@ public void AHighWholePowerIsOneGammaFunction() [InlineData("integral(e^(-x^3), x, 0, 1)", 0.807511182139671)] // 3^(-2/3) on both half-lines, which is not a number but one constant. [InlineData("integral(x^(1/3)*e^(-(1 + 3*x^2)), x, 0, 1)", 0.1169389395696306439)] + [InlineData("integral(x*(1/3 + 2*ln(3/2*(1 + 2*x)))^(5/2), x, 1, 2)", 47.01873105138465896167)] public void ADefiniteIntegralFromZero(string integral, double value) { var result = integral.ToEntity().Simplify().EvalNumerical(); @@ -123,6 +124,18 @@ public void APositiveRateIsTakenOutOfThePower() [InlineData("x^m*e^(-(a + b*x^2))")] public void AHyperbolicFunctionOfAPowerOfX(string integrand) => DifferentiatesBack(integrand); + /// + /// A polynomial beside a symbolic power of a logarithm of one shifted linear, or an exponential of a power of one, + /// written in u = a + b x, where each term is a gamma function. The constant in front comes along. Rubi's 3.3, + /// 3.4 and 2.3. + /// + [Theory] + [InlineData("2*x*(a + b*ln(c*(d + f*x)))^n")] + [InlineData("x*(a + b*ln(c*(d + f*sqrt(x))))^n")] + [InlineData("x^2*f^(c*(a + b*x)^3)")] + [InlineData("(a + b*x)^2*e^(-c*(d + f*x)^3)")] + public void APolynomialBesideAFunctionOfAShiftedLinear(string integrand) => DifferentiatesBack(integrand); + /// /// Not where the integral is elementary, an exponential integral or an error function: those keep the form /// they had.