diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index a9b52ffd3..6386f8482 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -910,6 +910,9 @@ in Rubi's 1.1.3.4 come out shorter as well, and those that took two seconds take of Rubi's 4.1.4.1, 4.2.4.1 and 4.5.4.1. `f^m (alpha + beta f^2)` for `f` a sine, cosine, secant or cosecant of a linear is the derivative of `cos sin^(m + 1)`, `sin cos^(m + 1)`, `tan sec^m` or `cot csc^m` where its bracket is a multiple of that derivative's, and is now integrated so ([#718](https://github.com/asc-community/AngouriMath/issues/718)). +The power is read as a factor of the integrand, so one under the bar is `f^(-m)`: `(m + (1 - m) sec^2)/sec^m` is +the derivative of `tan sec^(-m)`. And `m` may be a number that is not whole, as in Rubi's 4.5.4.1 +`(3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x))`. | Input | Was (2.5.0) | Now | |---|---|---| @@ -917,6 +920,10 @@ that derivative's, and is now integrated so ([#718](https://github.com/asc-commu | `"sec(c+d*x)^m*(A-A*(1+m)*sec(c+d*x)^2/m)".ToEntity().Integrate("x")` | `integral(...)` | 54 characters | | `"sec(k+f*x)^m*(m-(1+m)*sec(k+f*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 54 characters | | `"sin(k+f*x)^m*(1+m-(2+m)*sin(k+f*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 71 characters | +| `"(m+(1-m)*sec(c+d*x)^2)/sec(c+d*x)^m".ToEntity().Integrate("x")` | `integral(...)` | 56 characters | +| `"(-(1-m)+(2-m)*cos(x)^2)/cos(x)^m".ToEntity().Integrate("x")` | `integral(...)` | 55 characters | +| `"(3+3*sec(c+d*x)^2)/sec(c+d*x)^(1/2)".ToEntity().Integrate("x")` | `integral(...)` | 60 characters | +| `"sqrt(sin(x))*(3/2-5/2*sin(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 27 characters | ### A power of a sine or hyperbolic sine over a binomial in a power of it is split over the binomial's roots diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index cc998dea0..5fb5c874b 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -31310,8 +31310,8 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres } /// - /// f^m (alpha + beta f^2) for a symbolic m and f a sine, cosine, secant or - /// cosecant of a linear u, where it is the derivative of a product: with u' = 1, + /// f^m (alpha + beta f^2) for a symbolic m, or a number that is not whole, and f a + /// sine, cosine, secant or cosecant of a linear u, where it is the derivative of a product: with u' = 1, /// (cos sin^(m + 1))' = sin^m ((m + 1) - (m + 2) sin^2), /// (sin cos^(m + 1))' = cos^m ((m + 2) cos^2 - (m + 1)), /// (tan sec^m)' = sec^m ((m + 1) sec^2 - m) and (cot csc^m)' = -csc^m ((m + 1) csc^2 - m). @@ -31321,23 +31321,39 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres /// square terms, alpha + beta f^2, which matches p0 + p2 f^2 above where /// beta p0 = alpha p2; the answer is (alpha/p0) G/u'. Rubi's 4.1.4.1, 4.2.4.1 and 4.5.4.1 /// sec(c + d x)^m (m - (1 + m) sec(c + d x)^2) and the like were declined. + /// The power is one of the integrand's factors, and one under the bar is f^(-m): only then is the + /// integrand f^m times the rest, and a power in one term of a sum, as in + /// -m sec^m + (1 + m) sec^2, is not a factor of it. /// https://github.com/asc-community/AngouriMath/issues/718 /// internal static Entity? SolveASymbolicPowerOfATrigonometricFunctionAsTheDerivativeOfAProduct(Entity expr, Entity.Variable x) { if (!Integration.AnsweringTheQuestionAsked) return null; - // The power with a symbol in its exponent, of a sine, cosine, secant or cosecant of a linear. - var power = expr.Nodes.OfType().FirstOrDefault(node => node.Exponent.Vars.Any() && !node.Exponent.ContainsNode(x) - && node.Base is Sinf or Cosf or Secantf or Cosecantf); - if (power is not Powf(var function, var m)) - return null; + // The power with a symbol in its exponent, of a sine, cosine, secant or cosecant of a linear, + // as a factor of the integrand. + var factors = FactorsOfTheIntegrand(expr); + var at = factors.FindIndex(pair => pair.Factor is Powf(Sinf or Cosf or Secantf or Cosecantf, var exponent) + && (exponent.Vars.Any() && !exponent.ContainsNode(x) + // Or a number that is not whole: the derivatives hold for any m on the principal branch, + // and a whole one is left to the rules for whole powers. + || exponent is Number.Rational and not Number.Integer)); + if (at < 0 || factors[at].Factor is not Powf(var function, var written)) + return null; + var m = factors[at].Underneath ? (-written).InnerSimplified : written; var argument = function switch { Sinf(var y) => y, Cosf(var y) => y, Secantf(var y) => y, Cosecantf(var y) => y, _ => null }; if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || Functions.PartialFractions.IsZeroAsAValue(slope)) return null; var s = Variable.CreateUnique(expr, "s_trig"); - // The power taken off by writing 1 for it: `s^m (...)/s^m` is not cancelled by the simplification. - var inS = expr.Substitute(power, Number.Integer.One).Substitute(function, s).InnerSimplified; + // The rest of the factors, without the power: `s^m (...)/s^m` is not cancelled by the simplification. + Entity above = Number.Integer.One, below = Number.Integer.One; + for (var i = 0; i < factors.Count; i++) + if (i != at) + { + if (factors[i].Underneath) below *= factors[i].Factor; + else above *= factors[i].Factor; + } + var inS = (above / below).Substitute(function, s).InnerSimplified; if (inS.ContainsNode(x) || !TreeAnalyzer.TryGetPolynomial(inS.Expand(), s, out var read) || read.Keys.Any(key => !key.IsZero && !key.Equals(EInteger.FromInt32(2)))) return null; diff --git a/Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfATrigonometricFunctionAsADerivativeIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfATrigonometricFunctionAsADerivativeIntegralTest.cs index 592e38a03..59933e550 100644 --- a/Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfATrigonometricFunctionAsADerivativeIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfATrigonometricFunctionAsADerivativeIntegralTest.cs @@ -24,7 +24,11 @@ private static void DifferentiatesBack(string integrand, params (string, double) { var integral = integrand.ToEntity().Integrate("x"); Assert.DoesNotContain("integral(", integral.Stringize()); + DifferentiatesBack(integrand, integral, pins); + } + private static void DifferentiatesBack(string integrand, Entity integral, params (string, double)[] pins) + { var pinned = integral.Substitute("C", 0); Entity original = integrand.ToEntity(); foreach (var (name, value) in pins) @@ -52,12 +56,46 @@ private static void DifferentiatesBack(string integrand, params (string, double) [InlineData("sin(c+d*x)^m*(1+m-(2+m)*sin(c+d*x)^2)")] [InlineData("cos(x)^m*(-(1+m)+(2+m)*cos(x)^2)")] [InlineData("csc(x)^m*(m-(1+m)*csc(x)^2)")] + // Under the bar the power is f^(-m): (tan sec^(-m))' and (sin cos^(1 - m))'. + [InlineData("(m+(1-m)*sec(c+d*x)^2)/sec(c+d*x)^m")] + [InlineData("(-(1-m)+(2-m)*cos(x)^2)/cos(x)^m")] public void IsAnsweredAndDifferentiatesBack(string integrand) { DifferentiatesBack(integrand, ("A", 0.6), ("q", 1.25), ("c", 0.3), ("d", 0.75), ("m", 2.5)); DifferentiatesBack(integrand, ("A", -0.75), ("q", 0.4), ("c", -1), ("d", -0.5), ("m", 1.0 / 3)); } + // A power that is not whole: Rubi's 4.5.4.1 (3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x)), and the + // same derivatives at m = 1/2, 1/3 and 3/2. + [Theory] + [InlineData("(3+3*sec(c+d*x)^2)/sec(c+d*x)^(1/2)")] + [InlineData("sqrt(sin(x))*(3/2-5/2*sin(x)^2)")] + [InlineData("sin(c+d*x)^(1/3)*(4/3-7/3*sin(c+d*x)^2)")] + [InlineData("cos(x)^(3/2)*(-5/2+7/2*cos(x)^2)")] + [InlineData("csc(x)^(1/2)*(1/2-3/2*csc(x)^2)")] + public void IsAnsweredForAPowerThatIsNotWhole(string integrand) + { + DifferentiatesBack(integrand, ("c", 0.3), ("d", 0.75)); + DifferentiatesBack(integrand, ("c", -1), ("d", -0.5)); + } + + // The power is not a factor of the whole integrand, or is one under the bar, where the bracket is + // not the derivative's: none of these is the derivative of a single product. + [Theory] + [InlineData("(-m+(1+m)*sec(x)^2)/sec(x)^m")] + [InlineData("-m*sec(x)^m+(1+m)*sec(x)^2")] + [InlineData("sin(x)^m*(1+m)-(2+m)*sin(x)^2")] + [InlineData("(1+m-(2+m)*sin(x)^2)/sin(x)^m")] + [InlineData("(-m+(1+m)*csc(x)^2)/csc(x)^m")] + public void IsNotAnsweredWronglyWhereThePowerIsNotThatFactor(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + if (integral.Stringize().Contains("integral(")) + return; + DifferentiatesBack(integrand, integral, ("m", 2.5)); + DifferentiatesBack(integrand, integral, ("m", 1.0 / 3)); + } + [Fact] public void IsNotClaimedWhereTheBracketIsNotTheDerivative() {