diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 430aa694f..5e39bfc78 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -2315,6 +2315,26 @@ no integrand without one changes: Rubi's independent suites and a sample of its | `"Ei(b*x)^2".Integrate("x")` | `Ei * (b * x) ^ 3 / 3 / b + C`, with `Ei` a variable | an antiderivative in `Ei(b x)` and `Ei(2 b x)` | | `"x*erf(b*x)^2".Integrate("x")` | `UnrecognizedFunctionParseException`: there is no function `erf` | an antiderivative in `erf(b x)` | +### A constant of integration is matched to a linear divisor with a symbol in it + +By parts against a polynomial chooses the polynomial's antiderivative so that what the other +factor's derivative divides by divides it too, and that was done only where the division left a +number over. `a + b x` leaves a symbol: `x^2/2` over `a + b x` leaves `a^2/(2 b^2)`, so the +remainder of `x Shi(a + b x)^2` kept `x^2/(a + b x)` and nothing read it. The antiderivative less +its value at `-a/b` is taken now, written as `a + b x` times the quotient, so that the linear +cancels in the remainder ([#1501](https://github.com/asc-community/AngouriMath/issues/1501)). +`x Shi(a + b x)^2`, `x Chi(a + b x)^2` and `x Ei(a + b x)^2` are answered. An integrand holding one of +these functions had no reading in 2.5.0, which the entries for the functions themselves record, and +no integrand without one changes: Rubi's independent suites and a sample of its families 1 to 7 +are answered alone as they were. Where by parts already answered a special function of `a + b x` +beside a power of `x`, the answer is written with the same constant: `x Ei(a + b x)` now reads +`Ei(a + b x) (a + b x)(x/(2b) - a/(2b^2)) - ...`, the same function as before. + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"x*Shi(a+b*x)^2".Integrate("x")` | `a ^ 2 * Shi * x ^ 2 / 2 + a * b * 2 * Shi * x ^ 3 / 3 + b ^ 2 * Shi * x ^ 4 / 4 + C`, with `Shi` a variable | an antiderivative in `Shi(a + b x)` and `Ei(2 (a + b x))`, `Ei(-2 (a + b x))` | +| `"x*Ei(a+b*x)^2".Integrate("x")` | `a ^ 2 * Ei * x ^ 2 / 2 + a * b * 2 * Ei * x ^ 3 / 3 + b ^ 2 * Ei * x ^ 4 / 4 + C`, with `Ei` a variable | an antiderivative in `Ei(a + b x)` and `Ei(2 (a + b x))` | + ### An exponential or a hyperbolic function over several linears is split into partial fractions over them `e^x/(x (x + 1))` was left unevaluated, where `e^x/x` and `e^x/(x + 1)` were each answered with diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 90ed4521c..8694cc5d6 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -1691,6 +1691,19 @@ static Entity WithTheConstantMatchedTo(Entity antiderivative, Entity derivativeO && Functions.SingleQuotient.Of(leftOver) is var (leftOverTop, _) && !leftOverTop.ContainsNode(x) && leftOverTop.Evaled is Number.Complex { IsZero: false } constant) return (antiderivative + (-constant).Evaled).InnerSimplified; + // A linear divisor with a symbol in it, `a + b x`, leaves a symbol over, which the + // case above does not read: the antiderivative less its value at the root, -a/b, is + // divisible by the linear, and it is written as the linear times the quotient, so + // that the linear the derivative divides by cancels as written. `x Shi(a + b x)^2` + // is two rounds of parts that way, where with `x^2/2` the remainder kept + // `x^2/(a + b x)` and nothing read it. + // https://github.com/asc-community/AngouriMath/issues/1501 + if (TreeAnalyzer.TryGetPolyLinear(divisors[0], x, out var slope, out var offset) && !TreeAnalyzer.IsZero(slope) + && antiderivative.Substitute(x, (-offset / slope).InnerSimplified).InnerSimplified is var atTheRoot + && !atTheRoot.ContainsNode(x) && !TreeAnalyzer.IsZero(atTheRoot) + && TreeAnalyzer.PolynomialLongDivision((antiderivative - atTheRoot).Expand().InnerSimplified, divisors[0], genericCase: true, inTermsOf: x) is var (quotient, _) + && !quotient.ContainsNode(MathS.NaN)) + return divisors[0] * Functions.PartialFractions.Bare(quotient); return antiderivative; } diff --git a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs index 0b774a01e..6df826679 100644 --- a/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs +++ b/Sources/Tests/UnitTests/Calculus/SpecialFunctionsByPartsTest.cs @@ -133,5 +133,16 @@ public void BesideAPowerAndTheElementaryFactorOfItsDerivative(string integrand) [InlineData("Chi(b*x)^2")] public void ASquareIsTwoRoundsOfParts(string integrand) => DifferentiatesBack(integrand, Parameters); + + /// + /// Of a + b x, the first round against x takes x^2/2 less its value at + /// -a/b, written as a + b x times the quotient, so the linear the derivative + /// divides by cancels and what is left is the case above. + /// + [Theory] + [InlineData("x*Shi(a + b*x)^2")] + [InlineData("x*Ei(a + b*x)^2")] + public void ASquareOfAShiftedArgumentIsTwoRoundsOfParts(string integrand) + => DifferentiatesBack(integrand, Parameters); } }