diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 0909ba42e..6534d2e88 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -218,6 +218,21 @@ compared everywhere with its value at one positive point | `"asin(sqrt(1+b*x^2))^n/sqrt(1+b*x^2)".ToEntity().Integrate("x")` | `integral(...)` | 136 characters | | `"acos(sqrt(1+b*x^2))^n/sqrt(1+b*x^2)".ToEntity().Integrate("x")` | `integral(...)` | 137 characters | +### An exponential of a polynomial beside its derivative over a power of a multiple of it is integrated in the polynomial + +**Answers where there were none.** `e^(a + b x + c x^2) (b + 2 c x)/(a + b x + c x^2)^3` ran out of time, where with +`u = a + b x + c x^2` it is `e^u/u^3`. Taking the leading coefficient's power out of a power below the bar hands it on +as `e^P P'/(P/c)^3`, and the exponent read as the variable was read only as written, so `P/c` was another polynomial +and the question went on to the ansatz. A whole power of a constant multiple of the exponent is now read as that power +of the multiple times the power of `u` (Rubi's 2.3, [#233](https://github.com/asc-community/AngouriMath/issues/233)). +The square was answered by the unreleased master in fourteen seconds, as 90 characters; it is answered in a third of +a second now, as 114, which carry a factor `(1/c)^(-2)/c^2` that is 1. + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"e^(a+b*x+c*x^2)*(b+2*c*x)/(a+b*x+c*x^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 197 characters | +| `"e^(a+b*x+c*x^2)*(b+2*c*x)/(a+b*x+c*x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 114 characters | + ### A power of a sum beside its derivative is read with the logarithms in it as factors **Answers where there were none.** `(a m x^m + b n q ln(c x^n)^(q - 1))/(x (a x^m + b ln(c x^n)^q))` was declined, and so diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 31dc3156e..0c7f4a53e 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -41189,10 +41189,37 @@ static bool SameMonomial((Entity Coefficient, Entity Exponent, List<(Entity Base if (scale is null) return null; var u = Variable.CreateUnique(expr, "u"); - var inU = (scale * rest.Replace(node => node == polynomial ? u : node)).InnerSimplified; + // And a constant multiple of P, as that multiple of u: taking the leading coefficient's + // power out of a power below the bar hands on `e^P P'/(P/c)^2`, and with P read only as + // written, `e^(a + b x + c x^2) (b + 2 c x)/(a + b x + c x^2)^2` went on to the ansatz, + // which ran out of time, where `e^u/u^2` is answered at once. + // A whole power of one as that power of the multiple times the power of u, so that the + // multiple stands apart from u and the rules in u read a power of u alone. + TreeAnalyzer.TryGetPolynomial(polynomial, x, out var ofP); + var inU = (scale * rest + .Replace(node => node is Powf(var @base, Number.Integer whole) && @base != polynomial && AMultipleOfThePolynomial(@base) is { } multiple + ? MathS.Pow(multiple, whole) * MathS.Pow(polynomial, whole) : node) + .Replace(node => node == polynomial ? u : AMultipleOfThePolynomial(node) is { } multiple ? multiple * u : node)).InnerSimplified; if (inU.ContainsNode(x)) return null; return Integration.ComputeAsAQuestionOfItsOwn(inU, u, integrateByParts)?.Substitute(u, polynomial); + + // The constant k where the node is a polynomial in x equal to k P coefficient by coefficient. + Entity? AMultipleOfThePolynomial(Entity node) + { + if (node is not (Sumf or Minusf) || !node.ContainsNode(x) || ofP is null + || !TreeAnalyzer.TryGetPolynomial(node, x, out var read) || read.Count != ofP.Count + || read.Keys.Any(key => !ofP.ContainsKey(key)) || read.Values.Any(value => value.ContainsNode(x))) + return null; + var top = ofP.Keys.Max()!; + var multiple = Functions.PartialFractions.InLowestTermsOverTheSymbols(read[top] / ofP[top]); + if (multiple.Evaled is Number.Complex { IsZero: true } || multiple.Nodes.Any(inner => inner == MathS.NaN)) + return null; + foreach (var pair in read) + if (!VanishesIdentically(pair.Value - multiple * ofP[pair.Key])) + return null; + return multiple; + } } /// diff --git a/Sources/Tests/UnitTests/Calculus/TheExponentAsTheVariableBesideAMultipleOfItIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/TheExponentAsTheVariableBesideAMultipleOfItIntegralTest.cs new file mode 100644 index 000000000..62ea22cdf --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/TheExponentAsTheVariableBesideAMultipleOfItIntegralTest.cs @@ -0,0 +1,53 @@ +// +// 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 +{ + /// + /// e^P P' over a power of the quadratic P, under u = P, with a constant multiple + /// of P read as that multiple of u: taking the leading coefficient's power out from + /// below the bar hands on e^P P'/(P/c)^2, and e^(a + b x + c x^2) (b + 2 c x)/(a + b x + c x^2)^2 + /// went on to the ansatz and ran out of time. Rubi's 2.3. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class TheExponentAsTheVariableBesideAMultipleOfItIntegralTest + { + [Theory] + [InlineData("e^(a + b*x + c*x^2)*(b + 2*c*x)/(a + b*x + c*x^2)^2")] + [InlineData("e^(a + b*x + c*x^2)*(b + 2*c*x)/(a + b*x + c*x^2)^3")] + [InlineData("e^(a + b*x + c*x^2)*(b + 2*c*x)/(a + b*x + c*x^2)^4")] + [InlineData("e^(1 + x + 3*x^2)*(1 + 6*x)/(1 + x + 3*x^2)^2")] + public void APowerOfTheExponentBelowTheBar(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + var text = integral.Stringize(); + Assert.DoesNotContain("integral(", text); + Assert.True(text.Length < 20000, $"{text.Length} characters of answer for {integrand}"); + Entity Pinned(Entity e) => e.Substitute("a", -1.3).Substitute("b", 0.7).Substitute("c", 0.4); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + var compared = 0; + foreach (var at in new[] { -2.7, -1.2, -0.5, 0.3, 0.8, 1.7, 2.9 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + var got = derivative.Substitute("x", at).EvalNumerical(); + if (want.IsNaN) + continue; + compared++; + Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) + < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)), + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}"); + } + } +}