diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 789ecb4db..062394597 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -945,6 +945,19 @@ wherever the cosine is negative; they are answered with the rest now | `"sqrt(c*sec(x))*sqrt(a + i*a*tan(x))".ToEntity().Integrate("x")` | `integral(...)` | the integrand times an antiderivative in `e^(i x/2)` over its derivative | | `"(c*sec(x))^(5/2)/(a + i*a*tan(x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | the integrand times a power of `e^(i x)` | +### A symbolic power of `a + i a tan` beside a power of the secant is integrated as an exponential + +**Answers where there were none.** `(k sec(c + d x))^(-4 - n) (a + i a tan(c + d x))^n` and the rest +of Rubi's 4.3.1.2 whose two powers are symbols adding up to a whole number were declined: the powers +were read as numbers only. In `w = e^(i z)` the integral is a power of `w` beside a whole power of +`w^2 + 1`, a sum of powers of `w` +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"(k*sec(c + d*x))^(-4 - n)*(a + i*a*tan(c + d*x))^n".ToEntity().Integrate("x")` | `integral(...)` | the integrand times powers of `e^(i (c + d x))` from `n - 4` to `n + 4`, each over its exponent | +| `"(a + i*a*tan(c + d*x))^n/(k*sec(c + d*x))^n".ToEntity().Integrate("x")` | `integral(...)` | the integrand over `i d n` | + ### A rational function of the tangent beside a power of `a + i a tan` is integrated in that sum **Answers where there were none.** `(A + B tan(c + d x))/sqrt(a + i a tan(c + d x))` and the rest of diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 3d986f2f0..f9acaedcb 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -26574,7 +26574,9 @@ bool ARootOfTheTangent(Entity node) /// cosine is positive, and was declined, with every other power of the pair whose exponents /// add up to a whole number: a root of a sum with the imaginary unit in it beside a root of the /// secant is read by no rule, and e^(i x/2)/cos(x), what the positive sums leave, is - /// declined as well. Rubi's 4.3.1.2. + /// declined as well. Rubi's 4.3.1.2. A symbol for the powers is read too: + /// (pe sec(z))^(-4 - n) (a + i a tan(z))^n adds up to -4, and the integral in w + /// is w^(n - 5) (w^2 + 1)^4, a sum of powers. /// /// /// The constant is not written: the answer is the integrand times the antiderivative in @@ -26591,7 +26593,7 @@ bool ARootOfTheTangent(Entity node) return null; Entity? argument = null; var plus = false; - Number.Rational? tangentPower = null, secantPower = null; + Entity? tangentPower = null, secantPower = null; Entity constant = Number.Integer.One; Entity varying = Number.Integer.One; foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr)) @@ -26602,11 +26604,13 @@ bool ARootOfTheTangent(Entity node) continue; } varying = underneath ? varying / factor : varying * factor; - var (@base, power) = factor is Powf(var b, var p) && p.Evaled is Number.Rational r - ? (b, r) - : (factor, (Number.Rational)Number.Integer.One); + // A number or a symbol for the power: `(pe sec(z))^(-4 - n) (a + i a tan(z))^n` + // adds up to a whole number all the same. + var (@base, power) = factor is Powf(var b, var p) && !p.ContainsNode(x) + ? (b, p.Evaled is Number.Rational r ? r : p) + : (factor, (Entity)Number.Integer.One); if (underneath) - power = (Number.Rational)(-power); + power = power is Number.Rational numeric ? -numeric : (-power).InnerSimplified; if (TryReadAnImaginaryTangent(@base, x, out var tangentOf, out var isPlus)) { if (tangentPower is not null || argument is not null && argument != tangentOf) @@ -26627,22 +26631,34 @@ bool ARootOfTheTangent(Entity node) } if (tangentPower is null || secantPower is null || argument is null || tangentPower is Number.Integer && secantPower is Number.Integer - || (tangentPower + secantPower) is not Number.Integer { EInteger: var whole } || !whole.CanFitInInt32() + || SumOfThePowers(tangentPower, secantPower) is not Number.Integer { EInteger: var whole } || !whole.CanFitInInt32() || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || TreeAnalyzer.IsZero(slope)) return null; var k = whole.ToInt32Checked(); - var phase = plus ? tangentPower : (Number.Rational)(-tangentPower); + var phase = plus ? tangentPower : (-tangentPower).InnerSimplified; // In w = e^(i z): sec(z)^k e^(i n z) dz is (2 w)^k (w^2 + 1)^(-k) w^(n - 1) dw / i. var w = Variable.CreateUnique(expr, "w_exp"); - var inW = k >= 0 + // At k = 0 the power of w alone: `(w^2 + 1)^0` is 1 only where `w^2 + 1` is not 0, and + // the integral of `w^(n - 1)` with that condition beside it was declined. + var inW = k == 0 + ? MathS.Pow(w, (phase - 1).InnerSimplified) + : k > 0 ? MathS.Pow(2, k) * MathS.Pow(w, (phase + k - 1).InnerSimplified) / MathS.Pow(MathS.Sqr(w) + 1, k) : MathS.Pow(2, k) * MathS.Pow(MathS.Sqr(w) + 1, -k) * MathS.Pow(w, (phase + k - 1).InnerSimplified); if (Integration.ComputeAsAQuestionOfItsOwn(inW.InnerSimplified, w, integrateByParts) is not { } inWAnswer || inWAnswer.Nodes.Any(node => node == MathS.NaN)) return null; + // A short sum of symbols simplified, which the power rule leaves as it wrote it: + // `w^(n + -4 - 1 + 1)/(n + -4 - 1 + 1)` is `w^(n - 4)/(n - 4)`. + inWAnswer = inWAnswer.Replace(node => node is Sumf or Minusf && !node.ContainsNode(w) && node.Vars.Any() && node.Complexity <= 20 + ? node.Simplify() : node); var exponential = MathS.Pow(MathS.e, MathS.i * argument); var differentiatesBackTo = MathS.Pow(MathS.Sec(argument), k) * MathS.Pow(exponential, phase); return constant * varying * inWAnswer.Substitute(w, exponential) / (MathS.i * slope * differentiatesBackTo); + + // `n + (-4 - n)` is -4, which the inner simplification leaves as written. + static Entity SumOfThePowers(Entity first, Entity second) + => (first + second).InnerSimplified is var sum && (sum is Number || sum.Complexity > 40) ? sum : sum.Simplify(); } /// diff --git a/Sources/Tests/UnitTests/Calculus/ImaginaryTangentBesideTheSecantIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ImaginaryTangentBesideTheSecantIntegralTest.cs index f29f5865a..24fbbf784 100644 --- a/Sources/Tests/UnitTests/Calculus/ImaginaryTangentBesideTheSecantIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/ImaginaryTangentBesideTheSecantIntegralTest.cs @@ -17,7 +17,8 @@ namespace AngouriMath.Tests.Calculus /// the real line, so the integrand is a constant on every interval where it is continuous times /// a power of the secant and an exponential. Rubi's 4.3.1.2. The integrands are complex along /// the real line and are compared as complex numbers, where the cosine is negative as well, - /// which is where the fourth row's answer on master was off by a constant factor. + /// which is where the fourth row's answer on master was off by a constant factor. A symbol + /// for the powers, adding up to a whole number, as well. /// #718 /// [Trait("Area", "Calculus")] @@ -29,12 +30,16 @@ public sealed class ImaginaryTangentBesideTheSecantIntegralTest [InlineData("(a + i*a*tan(x))^(3/2)/(k*sec(x))^(7/2)")] [InlineData("(k*sec(x))^(5/2)/(a + i*a*tan(x))^(5/2)")] [InlineData("(k*sec(c + d*x))^(3/2)*(a - i*a*tan(c + d*x))^(3/2)")] + // A symbol for the powers, adding up to -4, -1 and 0. + [InlineData("(k*sec(c + d*x))^(-4 - n)*(a + i*a*tan(c + d*x))^n")] + [InlineData("(k*sec(c + d*x))^(-1 - n)*(a + i*a*tan(c + d*x))^n")] + [InlineData("(a + i*a*tan(c + d*x))^n/(k*sec(c + d*x))^n")] public void AsTheExponentialItIs(string integrand) { var integral = integrand.ToEntity().Integrate("x"); Assert.DoesNotContain("integral(", integral.Stringize()); Assert.DoesNotContain("NaN", integral.Stringize()); - Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("k", 0.8).Substitute("c", 0.4).Substitute("d", 1.1); + Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("k", 0.8).Substitute("c", 0.4).Substitute("d", 1.1).Substitute("n", 0.6); var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); var original = Pinned(integrand.ToEntity()); var compared = 0;