diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index b0414ac96..942ebafc6 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -18148,6 +18148,66 @@ bool Read(Entity node, bool insideASum) return back.Nodes.Any(node => node == MathS.NaN) ? null : back; } + /// + /// An exponential of a polynomial of degree 3 to 12 written expanded where it is a multiple of a power of one shifted + /// linear and a constant, written so: a^3 + 3 a^2 b x + 3 a b^2 x^2 + b^3 x^3 is b^3 (x + a/b)^3. With + /// p the leading coefficient and s = p_(n - 1)/(n p), the polynomial less p (x + s)^n is a constant + /// exactly where each lower coefficient is p C(n, j) s^(n - j), and the integrand with the exponent written so + /// is the rule's below for a polynomial beside a function of a shifted linear. Rubi's 2.3. + /// https://github.com/asc-community/AngouriMath/issues/2075 + /// + internal static Entity? SolveByWritingAnExponentAsAPowerOfAShiftedLinear(Entity expr, Entity.Variable x, bool integrateByParts) + { + if (!Integration.AnsweringTheQuestionAskedOrOneBelow) + return null; + foreach (var node in expr.Nodes) + { + // Written expanded: a power of a sum in x is already written as one, and its expansion would be read again. + if (node is not Powf(var @base, var exponent) || @base.ContainsNode(x) + || !exponent.Nodes.Any(inner => inner is Sumf or Minusf && inner.ContainsNode(x)) + || exponent.Nodes.Any(inner => inner is Powf(Sumf or Minusf, _) && inner.ContainsNode(x)) + || !TreeAnalyzer.TryGetPolynomial(exponent, x, out var monomials) || monomials is null || monomials.Keys.Any(degree => degree.Sign < 0)) + continue; + var n = monomials.Keys.Max()!; + if (n.CompareTo(EInteger.FromInt32(3)) < 0 || n.CompareTo(EInteger.FromInt32(12)) > 0 + || !monomials.TryGetValue(n.Subtract(EInteger.One), out var next) || VanishesIdentically(next)) + continue; + var degree = n.ToInt32Checked(); + // Both small, and simplified so that the answer reads them: 3 a b^2/(3 b^3) is a/b. The condition that comes + // with it, that the leading coefficient is not 0, is the reading's own: the polynomial is of degree n. + var leading = monomials[n].InnerSimplified; + var shift = (next / (degree * leading)).Simplify(); + while (shift is Providedf(var unconditional, _)) + shift = unconditional; + var isAShiftedPower = true; + for (var j = 1; j <= degree - 2 && isAShiftedPower; j++) + { + var coefficient = monomials.TryGetValue(EInteger.FromInt32(j), out var written) ? written : Number.Integer.Zero; + isAShiftedPower = VanishesIdentically(coefficient - leading * Number.Integer.Create(Choose(degree, j)) * MathS.Pow(shift, degree - j)); + } + if (!isAShiftedPower) + continue; + var constant = monomials.TryGetValue(EInteger.Zero, out var free) ? free : Number.Integer.Zero; + var left = (constant - leading * MathS.Pow(shift, degree)).Simplify(); + while (left is Providedf(var unconditional, _)) + left = unconditional; + var asAPower = VanishesIdentically(left) ? leading * MathS.Pow(x + shift, degree) : leading * MathS.Pow(x + shift, degree) + left; + // The same question, respelled: asked at this depth, so that the rules scoped to the question asked or one + // below still read it. + var rewritten = expr.Replace(inner => inner == node ? MathS.Pow(@base, asAPower) : inner); + return Integration.ComputeAsTheSameQuestion(rewritten, x, integrateByParts); + } + return null; + + static EInteger Choose(int whole, int chosen) + { + var result = EInteger.One; + for (var i = 1; i <= chosen; i++) + result = result.Multiply(EInteger.FromInt32(whole - chosen + i)).Divide(EInteger.FromInt32(i)); + return result; + } + } + /// /// 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, diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index 2c66fcd5e..137b9d2b4 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -936,6 +936,7 @@ internal static bool ASumOverRootsWouldAnswer // 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.SolveByWritingAnExponentAsAPowerOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer; if ((answer = IndefiniteIntegralSolver.SolveAPolynomialBesideATranscendentalPowerOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer; if ((answer = IndefiniteIntegralSolver.SolveAPowerOfXBesideATranscendentalPowerOfABinomial(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 diff --git a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs index 3205bfdfe..fb101a74d 100644 --- a/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/APowerBesideAnExponentialOfAPowerIntegralTest.cs @@ -193,6 +193,16 @@ public void APositiveRateIsTakenOutOfThePower() [InlineData("Chi(a + b*x)^2")] public void AnExponentWrittenOtherwise(string integrand) => DifferentiatesBack(integrand); + /// + /// An exponent written expanded that is a power of one shifted linear and a constant, written so first: + /// a^3 + 3 a^2 b x + 3 a b^2 x^2 + b^3 x^3 is (a + b x)^3. Rubi's 2.3. + /// + [Theory] + [InlineData("e^(a^3 + 3*a^2*b*x + 3*a*b^2*x^2 + b^3*x^3)*x")] + [InlineData("f^(c + d^3 + 3*d^2*x + 3*d*x^2 + x^3)")] + [InlineData("x^2*e^(b*(1 + 4*x + 6*x^2 + 4*x^3 + x^4))")] + public void AnExponentThatIsAPowerOfAShiftedLinearWrittenExpanded(string integrand) => DifferentiatesBack(integrand); + /// /// Not where the integral is elementary, an exponential integral or an error function: those keep the form /// they had.