diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 205ec5f0b..10acfc723 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -2319,6 +2319,21 @@ one reads a numeric slope, and writes an offset out by the addition formula, whi | `"csc(1+x)*csc(2+2*x)^2".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `cos(1 + x)` | | `"sin(a+b*x)^3/sin(2*a+2*b*x)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `tan(a + b x)` | +### Multiples of one linear argument are written in it with `x` beside them in a linear + +`csc(a + b x) sin(3a + 3b x)/(c + d x)` was left unevaluated, and so was each power of `c + d x` +below the bar. Writing the arguments in `u = a + b x` declined wherever `x` stood outside them as +well. Where it stands there only in sums, products and powers, it is written in `u` too, as +`(u - a)/b`, and the linears in the answer are put back in lowest terms. Rubi's 4.7.3 +([#718](https://github.com/asc-community/AngouriMath/issues/718), PR +[#1863](https://github.com/asc-community/AngouriMath/pull/1863)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"csc(a+b*x)*sin(3*a+3*b*x)/(c+d*x)".ToEntity().Integrate("x")` | `integral(...)` | 190 characters, in `ln`, `Si` and `Ci` of a multiple of `c + d x` | +| `"csc(a+b*x)*sin(3*a+3*b*x)/(c+d*x)^4".ToEntity().Integrate("x")` | `integral(...)` | 736 characters | +| `"csc(2+3*x)*sin(6+9*x)/(c+d*x)".ToEntity().Integrate("x")` | `integral(...)` | 194 characters | + ### A function of one linear under a power inside a sum is written in that linear `x^3/(a + b (c + d x)^3)` was left unevaluated, and `(c + d x)^4/(a + b (c + d x)^3)` ran for more diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 50b8e7d7f..39383a7c2 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -13297,8 +13297,10 @@ Entity Named(Entity value) /// csc(1 + x) csc(2 + 2x)^2 became a rational function of the sine and cosine with /// sin(2) and cos(2) in its coefficients, and ran twenty seconds; with the slope /// a symbol it declined at once. Only where some argument has an offset or a symbolic - /// slope, the rest being that rule's already, and where x stands nowhere else. At the - /// question asked or one below it, since it lands on the chain in u. Rubi's 4.7.1. + /// slope, the rest being that rule's already, and where x stands elsewhere only + /// algebraically: csc(a + b x) sin(3a + 3b x)/(c + d x) is + /// csc(u) sin(3u)/(b c + d (u - a)) over b, Rubi's 4.7.3. At the question + /// asked or one below it, since it lands on the chain in u. Rubi's 4.7.1. /// https://github.com/asc-community/AngouriMath/issues/718 /// internal static Entity? SolveByWritingMultiplesOfOneLinearArgument(Entity expr, Entity.Variable x, bool integrateByParts) @@ -13348,12 +13350,26 @@ Entity Named(Entity value) Cosecantf(var a) when Of(a) is { } v => new Cosecantf(v), _ => node, }); - if (inU.ContainsNode(x)) - return null; + // x beside the functions as well, algebraically -- `csc(a + b x) sin(3a + 3b x)/(c + d x)` + // -- is written in u too, x = (u - p)/q. + var beside = inU.ContainsNode(x); + if (beside) + { + if (inU.Nodes.Any(node => node.ContainsNode(x) && node is not (Variable or Sumf or Minusf or Mulf or Divf) + && !(node is Powf(_, var exponent) && !exponent.ContainsNode(x)))) + return null; + inU = inU.Substitute(x, (u - firstOffset) / firstSlope).InnerSimplified; + } // dx = du/q. if (Integration.ComputeAsAQuestionOfItsOwn((inU / firstSlope).InnerSimplified, u, integrateByParts) is not { } answer) return null; var back = answer.Substitute(u, first); + // And each linear the substitution wrote, `b c + d (a + b x - a)`, back in lowest terms. + if (beside) + back = back.Replace(node => node is Sumf or Minusf && node.ContainsNode(x) + && TreeAnalyzer.TryGetPolyLinear(node, x, out var slope, out var offset) && !slope.ContainsNode(x) && !offset.ContainsNode(x) + ? LowestOverTheSymbols(slope) * x + LowestOverTheSymbols(offset) + : node).InnerSimplified; return back.Nodes.Any(node => node == MathS.NaN) ? null : back; } diff --git a/Sources/Tests/UnitTests/Calculus/MixedTrigonometricArgumentsTest.cs b/Sources/Tests/UnitTests/Calculus/MixedTrigonometricArgumentsTest.cs index 9aa5c0c68..8c874fd7e 100644 --- a/Sources/Tests/UnitTests/Calculus/MixedTrigonometricArgumentsTest.cs +++ b/Sources/Tests/UnitTests/Calculus/MixedTrigonometricArgumentsTest.cs @@ -77,22 +77,29 @@ private static void DifferentiatesBack(string integrand, string variable = "x") /// Multiples of one linear with an offset or a symbolic slope, written in that linear: /// csc(1 + x) csc(2 + 2x)^2 ran twenty seconds with the offsets written out by the /// addition formula, and csc(a + b x) csc(2a + 2b x)^2 was declined for its slope. - /// Rubi's 4.7.1. + /// Rubi's 4.7.1; and with x beside them in a linear, csc(a + b x) sin(3a + 3b x)/(c + d x), + /// which was declined whatever the slope, Rubi's 4.7.3. /// [Theory] [InlineData("csc(1 + x)*csc(2 + 2*x)^2")] [InlineData("sin(3 + 3*x)/sin(1 + x)")] + [InlineData("csc(2 + 3*x)*sin(6 + 9*x)/(1 + x)")] + [InlineData("csc(2 + 3*x)*sin(6 + 9*x)/(1 + x)^2")] public void MultiplesOfOneLinearArgument(string integrand) => DifferentiatesBack(integrand); [Theory] [InlineData("csc(a + b*x)*csc(2*a + 2*b*x)^2")] [InlineData("cos(a + b*x)^2/sin(2*a + 2*b*x)^5")] + [InlineData("csc(a + b*x)*sin(3*a + 3*b*x)/(c + d*x)")] + [InlineData("csc(a + b*x)*sin(3*a + 3*b*x)/(c + d*x)^2")] + [InlineData("csc(a + b*x)*sin(3*a + 3*b*x)/(c + d*x)^4")] public void MultiplesOfOneLinearArgumentWithASymbolicSlope(string integrand) { var integral = integrand.ToEntity().Integrate("x"); Assert.DoesNotContain("integral(", integral.Stringize()); - var derivative = integral.Substitute("C", 0).Differentiate("x").Substitute("a", 0.3).Substitute("b", 1.2); - var original = integrand.ToEntity().Substitute("a", 0.3).Substitute("b", 1.2); + var derivative = integral.Substitute("C", 0).Differentiate("x") + .Substitute("a", 0.3).Substitute("b", 1.2).Substitute("c", 0.7).Substitute("d", 1.3); + var original = integrand.ToEntity().Substitute("a", 0.3).Substitute("b", 1.2).Substitute("c", 0.7).Substitute("d", 1.3); var compared = 0; foreach (var at in new[] { 0.3, 0.5, 0.9, 1.1 }) {