diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 21b3a5303..490257fd1 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -465,6 +465,24 @@ interval between the poles of the tangent, as the substitution's are | `"(A + B*tan(x))/(a + i*a*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)` | the same | | `"1/(a + i*a*tan(c + d*x))^3".ToEntity().Integrate("x")` | `integral(...)` | the same in `tan(c + d x)` | +### An integrand with the imaginary unit in it is not simplified by the rule that scales the variable + +**Answers where there were none.** The rule that scales the variable by the integrand's one symbol +simplified the scaled integrand three times, to separate the whole power of the scale it is +homogeneous of. With the imaginary unit beside the symbol that search ran past any budget: on the +unreleased master `((1 + i a x)/(1 - i a x))^(-5/4)/x^2` took more than two minutes to be declined, +and the rules after this one never had their turn on integrands that it held up. Such an integrand is +inner-simplified now, and declined in seconds where the parts do not separate +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"sec(c + d*x)^3/(a + i*a*tan(c + d*x))".ToEntity().Integrate("x")` | `integral(...)` | logarithms of `tan((c + d x)/2) ± 1` and a quotient | +| `"tan(c + d*x)^(8/3)/(a + i*a*tan(c + d*x))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `tan(c + d x)^(1/3)` | +| `"1/(a + b*csc(c + d*x)^2)^4".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative, by the signs of `a` and `b` | +| `"x^4*e^(2*i*atan(a + b*x))".ToEntity().Integrate("x")` | `integral(...)` | a polynomial, logarithms and arctangents of `a + b x` | +| `"sec(c + d*x)^2/(a + i*a*tan(c + d*x))".ToEntity().Integrate("x")` | `ln(i a d tan(c + d x) + a d)/(i a d)` | `ln(i tan(c + d x) + 1)/(i a d)`, the same up to a constant | + ### `NaN` was returned as the antiderivative of something that has one **A wrong answer, not a missing one.** `1/(a*x^2)` came back as `NaN + C`, and `NaN` is this diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 73456d7c7..fcc8620a3 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -18538,7 +18538,13 @@ bool IsAConstantTimesAPowerOfOneMinusUSquared(Entity polynomial, out Entity cons // dx = c dt. var t = Variable.CreateUnique(expr, "u_scale"); - var scaled = (expr.Substitute(x, scale * t) * scale).Simplify(); + // With the imaginary unit in the integrand the simplifier's search beside the symbol + // does not end within any budget, and inner simplification separates what a whole + // power of the scale does: `((1 + i a x)/(1 - i a x))^(-5/4)/x^2` under its radical's + // substitution was minutes here before it was declined. Where the parts do not + // separate so, the check below declines it. + Entity Simplified(Entity e) => HoldsTheImaginaryUnit(expr) ? Functions.PartialFractions.Bare(e.InnerSimplified) : e.Simplify(); + var scaled = Simplified(expr.Substitute(x, scale * t) * scale); if (scaled.ContainsNode(x)) return null; @@ -18547,11 +18553,11 @@ bool IsAConstantTimesAPowerOfOneMinusUSquared(Entity polynomial, out Entity cons // The integrand with the scale set to one is the candidate h(t), and what is left // over when the scaled integrand is divided by it is the candidate factor. Read off // rather than searched for: if the two do separate, the quotient *is* the factor. - var withoutScale = scaled.Substitute(scale, Number.Integer.One).Simplify(); + var withoutScale = Simplified(scaled.Substitute(scale, Number.Integer.One)); if (withoutScale.ContainsNode(scale) || withoutScale == Number.Integer.Zero) return null; - var factor = (scaled / withoutScale).Simplify(); + var factor = Simplified(scaled / withoutScale); // Collapsing the quotient attaches the condition that the denominator it cleared is // non-zero. That denominator is the integrand's own, so the condition says where the // integrand is defined and nothing about this rewrite; it is dropped for the same diff --git a/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs index 53e961c15..37f56139a 100644 --- a/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/ComplexCoefficientRationalIntegralTest.cs @@ -79,5 +79,20 @@ private static void DifferentiatesBack(string integrand) [InlineData("(A + B*tan(x))/(a + i*a*tan(x))^2")] [InlineData("1/(a + i*a*tan(c + d*x))^3")] public void APowerOfAPlusIATangent(string integrand) => DifferentiatesBack(integrand); + + /// + /// A power of a quotient of linears with the imaginary unit in it, beside a power of x the + /// substitution for it does not take down to something answered: declined, and within a + /// minute. The scaling of the variable simplified it with the symbol beside the imaginary + /// unit, and that ran past two minutes before the decline. + /// + [Theory] + [InlineData("((1 + i*a*x)/(1 - i*a*x))^(-5/4)/x^2")] + [InlineData("((1 + i*a*x)/(1 - i*a*x))^(-5/4)/x^3")] + public void AnImaginaryMobiusPowerIsDeclinedWithinAMinute(string integrand) + { + var integrating = System.Threading.Tasks.Task.Run(() => integrand.ToEntity().Integrate("x")); + Assert.True(integrating.Wait(TimeSpan.FromSeconds(60)), $"{integrand} was not settled within a minute"); + } } }