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 })
{