diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index e9ae2c6a1..21b3a5303 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -896,6 +896,26 @@ its sign on its own, so an answer through two says `provided cos(y) >= 0` | `"sqrt(a - a*sec(x))".ToEntity().Integrate("x")` | `integral(...)` | a logarithm in `tan(x/2)`, times `sgn(tan(x/2))` | | `"sqrt(1 + csc(x))".ToEntity().Integrate("x")` | `integral(...)` | an arctangent in `tan(pi/4 - x/2)` | +### An even power of the secant or the cosecant under a root is written in the tangent + +**Answers where there were none.** `sqrt(a + b csc(x)^2)`, `(a + b sec(x)^2)^(3/2)` and their kin, +Rubi's 4.5.7 and 4.6.7, were declined, while `sqrt(a + b tan(x)^2)` and `sqrt(a + b cot(x)^2)` were +answered through `u = tan(x)`. That substitution writes an even power of the secant or the +cosecant in the tangent, `sec^2 = 1 + tan^2` and `csc^2 = (1 + tan^2)/tan^2`, but went on to do so +only where the tangent, or a sine or a cosine under a root, was already in the integrand. An even +power of the secant or the cosecant under a root goes on now too. The cosecant's answers carry +`sgn(tan(x))`, from the root of `1/tan(x)^2` +([#718](https://github.com/asc-community/AngouriMath/issues/718)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"sqrt(a + b*csc(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | arctangents in `sqrt((a + b) tan(x)^2 + b)`, times `sgn(tan(x))` | +| `"(a + b*csc(c + d*x)^2)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | the same in `tan(c + d x)` and an algebraic part, by the signs of `a` and `b` | +| `"sqrt(a + b*sec(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | arctangents in `tan(x)/sqrt(a + b (1 + tan(x)^2))` | +| `"1/sqrt(a + b*sec(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | an arctangent or a logarithm in the same, by the sign of `a` | +| `"sqrt(1 + csc(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | logarithms and an arctangent in `sqrt(2 tan(x)^2 + 1)`, times `sgn(tan(x))` | +| `"1/sqrt(-1 + csc(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | `sgn(tan(x)) ln(1 + tan(x)^2)/2` | + ### A partial-fraction coefficient with symbols in it is in lowest terms, its rational content included **Improvement, not silent.** The decomposition over written factors with symbols among their diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 9965d1608..73456d7c7 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -8464,9 +8464,13 @@ node is Powf(var product, Number.Rational fraction) && fraction is not Number.In : node); var tangent = MathS.Tan(x); - // Or a sine or a cosine under a root, for the writing by the sign below; a rational - // function of those is the half-angle substitution's. - if (!expr.ContainsNode(tangent) && !(HasARadicalOf(expr, x) && expr.Nodes.Any(node => node is Sinf or Cosf && node.ContainsNode(x)))) + // Or a sine or a cosine under a root, for the writing by the sign below, or an even + // power of the secant or the cosecant under one, which the next step writes in the + // tangent: `sqrt(a + b csc(x)^2)` is `sqrt(a + b + b/tan(x)^2)`. A rational function + // of those is the half-angle substitution's. + if (!expr.ContainsNode(tangent) && !(HasARadicalOf(expr, x) && expr.Nodes.Any(node => + node is Sinf or Cosf && node.ContainsNode(x) + || node is Powf(Secantf or Cosecantf, Number.Integer { EInteger.IsEven: true }) && node.ContainsNode(x)))) return null; // An even power of the secant, cosine, sine or cosecant of x is a rational function diff --git a/Sources/Tests/UnitTests/Calculus/TangentSubstitutionIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/TangentSubstitutionIntegralTest.cs index 3f9c84a4f..f11a00f71 100644 --- a/Sources/Tests/UnitTests/Calculus/TangentSubstitutionIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/TangentSubstitutionIntegralTest.cs @@ -97,6 +97,51 @@ private static void DifferentiatesBack(string integrand) [InlineData("(sec(x)^2 - 3*sqrt(4*sec(x)^2 + 5*tan(x)^2)*tan(x))/(sin(x)^2*(4*sec(x)^2 + 5*tan(x)^2)^(3/2))")] public void AnEvenPowerOfTheOthersIsWrittenInTheTangent(string integrand) => DifferentiatesBack(integrand); + /// + /// The same under a root with no tangent beside it, Rubi's 4.5.7 and 4.6.7: + /// sqrt(a + b csc(x)^2) is sqrt(a + b (1 + u^2)/u^2) under the tangent. On both + /// signs of the tangent, which the cosecant's answer reads, with the symbols pinned before + /// the derivative is taken: sgn(tan(c + d x)) is flat only where c and + /// d are real. + /// + [Theory] + [InlineData("sqrt(a + b*csc(x)^2)")] + [InlineData("(a + b*csc(x)^2)^(3/2)")] + [InlineData("1/(a + b*csc(x)^2)^(3/2)")] + [InlineData("sqrt(a + b*sec(x)^2)")] + [InlineData("1/sqrt(a + b*sec(x)^2)")] + [InlineData("sqrt(a + b*csc(c + d*x)^2)")] + [InlineData("sqrt(1 + csc(x)^2)")] + [InlineData("1/sqrt(-1 + csc(x)^2)")] + public void AnEvenPowerOfTheSecantOrTheCosecantUnderARoot(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + var pins = new (string, double)[] { ("a", 1.3), ("b", 0.7), ("c", 0.4), ("d", 1.1) }; + var answer = integral.Substitute("C", 0); + var original = integrand.ToEntity(); + foreach (var (name, value) in pins) + { + answer = answer.Substitute(name, value); + original = original.Substitute(name, value); + } + var derivative = answer.Differentiate("x"); + var compared = 0; + foreach (var at in new[] { -2.6, -0.9, 0.4, 1.1, 1.9, 2.7 }) + { + var got = derivative.Substitute("x", at).EvalNumerical(); + var want = original.Substitute("x", at).EvalNumerical(); + if (got.IsNaN || want.IsNaN) + continue; + compared++; + var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart); + var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)); + Assert.True(difference / scale < 1e-9, + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + Assert.True(compared >= 4, $"only {compared} points could be compared for {integrand}"); + } + /// /// An odd power of the sine or the cosine is its sign times a function of the tangent, /// cos(x) = sgn(cos(x))/sqrt(1 + tan^2) and sin(x) = tan(x) cos(x), the sign a