diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index eb29c904c..009dc96c6 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -4595,6 +4595,76 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a return expr * inTermsOfU.Substitute(u, other.Radicand) / inX; } + /// + /// An odd whole power of tan(y) beside a function of sec(y), integrated in + /// u = sec(y): du = sec(y) tan(y) dy and the even power of the tangent left over + /// is a power of u^2 - 1, so tan(y)^n with the rest is + /// (u^2 - 1)^((n - 1)/2)/u du beside it, exactly. The cotangent beside a function of + /// the cosecant, in u = csc(y), the same. + /// + /// + /// Rubi's cot(c + d x)/(a + b sec(c + d x))^(3/2) was declined and + /// cot(c + d x)^3 sqrt(a + b sec(c + d x)) ran past thirty seconds, where in the + /// secant each is a rational function beside the root of a linear. + /// https://github.com/asc-community/AngouriMath/issues/718 + /// + internal static Entity? SolveAnOddPowerOfTheTangentBesideAFunctionOfTheSecant(Entity expr, Entity.Variable x, bool integrateByParts) + { + if (!Integration.AnsweringTheQuestionAskedOrOneBelow) + return null; + Entity? argument = null; + foreach (var node in expr.Nodes) + { + if (TrigonometricArgument(node) is not { } thisArgument || !thisArgument.ContainsNode(x)) + continue; + if (argument is null) + argument = thisArgument; + else if (argument != thisArgument) + return null; + } + if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out _) + || rate.ContainsNode(x) || TreeAnalyzer.IsZero(rate)) + return null; + if (!expr.Nodes.All(node => !node.ContainsNode(x) + || node is Variable or Sumf or Minusf or Mulf or Divf or Sinf or Cosf or Secantf or Cosecantf or Tanf or Cotanf + || node is Powf(_, Number.Rational))) + return null; + var t = Variable.CreateUnique(expr, "t_odd_tangent"); + var u = Variable.CreateUnique(expr, "u_secant"); + foreach (var ofTheSecant in new[] { true, false }) + { + // The tangent and its reciprocal as one base; for the cosecant, the cotangent. + var inT = expr.Replace(node => node switch + { + Tanf(var a) when a == argument => ofTheSecant ? t : 1 / t, + Cotanf(var a) when a == argument => ofTheSecant ? 1 / t : t, + _ => node, + }); + var (rest, exponents) = GatheredOverTheBases(inT, new Entity[] { t }); + var n = exponents[0]; + if (rest.ContainsNode(t) || !n.IsInteger() || n.ToEInteger().IsEven || !n.ToEInteger().CanFitInInt32()) + continue; + var inU = rest.Replace(node => node switch + { + Secantf(var a) when ofTheSecant && a == argument => u, + Cosf(var a) when ofTheSecant && a == argument => 1 / u, + Cosecantf(var a) when !ofTheSecant && a == argument => u, + Sinf(var a) when !ofTheSecant && a == argument => 1 / u, + _ => node, + }); + if (inU.ContainsNode(x)) + continue; + // dy = du/(u t) for the secant, -du/(u t) for the cosecant, and t^(n - 1) = (u^2 - 1)^((n - 1)/2). + var half = n.Subtract(ERational.One).Divide(ERational.FromInt32(2)).ToEInteger(); + var integrand = (inU * MathS.Pow(MathS.Sqr(u) - 1, Number.Integer.Create(half)) / ((ofTheSecant ? rate : -rate) * u)).InnerSimplified; + if (Integration.ComputeAsAQuestionOfItsOwn(integrand, u, integrateByParts) is not { } inTermsOfU + || inTermsOfU.Nodes.Any(node => node == MathS.NaN)) + return null; + return inTermsOfU.Substitute(u, ofTheSecant ? MathS.Sec(argument) : new Cosecantf(argument)); + } + return null; + } + /// /// tan(y) tan(2y) written sec(2y) - 1, and the integrand asked again in the /// one argument 2y. diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index 105c4a753..8e98ddd36 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -911,6 +911,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) => // And the secant beside powers of a + a sec(y) and c - c sec(y), whose product is a // square of the tangent: in u = a + a sec(y), a symbolic power of it too. if ((answer = IndefiniteIntegralSolver.SolveASecantBesidePowersOfItsConjugateSums(expr, x, integrateByParts)) is { }) return answer; + // And an odd power of the tangent beside a function of the secant, in u = sec(y). + if ((answer = IndefiniteIntegralSolver.SolveAnOddPowerOfTheTangentBesideAFunctionOfTheSecant(expr, x, integrateByParts)) is { }) return answer; // And `a ± a cosh(y)` under a fractional power: `2a cosh(y/2)^2`, `-2a sinh(y/2)^2`. if ((answer = IndefiniteIntegralSolver.SolveByTheHalfAngleWhereOnePlusAHyperbolicCosineIsASquare(expr, x, integrateByParts)) is { }) return answer; // The hyperbolic sine's, off the real line: 1 + i sinh(y) is (cosh(y/2) + i sinh(y/2))^2. diff --git a/Sources/Tests/UnitTests/Calculus/AnOddPowerOfTheTangentBesideTheSecantIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/AnOddPowerOfTheTangentBesideTheSecantIntegralTest.cs new file mode 100644 index 000000000..33efff53b --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/AnOddPowerOfTheTangentBesideTheSecantIntegralTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) 2019-2026 Angouri. +// AngouriMath is licensed under MIT. +// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md. +// Website: https://am.angouri.org. +// + +using System; +using AngouriMath.Extensions; +using Xunit; + +namespace AngouriMath.Tests.Calculus +{ + /// + /// An odd power of the tangent beside a function of the secant, in u = sec(y), and the + /// cotangent beside one of the cosecant. Rubi's 4.5.1.4. + /// #718 + /// + [Trait("Area", "Calculus")] + public sealed class AnOddPowerOfTheTangentBesideTheSecantIntegralTest + { + [Theory] + [InlineData("cot(g + f*x)/(a + b*sec(g + f*x))^(3/2)")] + [InlineData("cot(x)^3/(a + b*sec(x))^(3/2)")] + [InlineData("tan(x)^3/(a + b*csc(x))")] + public void InTheSecant(string integrand) + { + var integral = integrand.ToEntity().Integrate("x"); + var text = integral.Stringize(); + Assert.DoesNotContain("integral(", text); + Assert.True(text.Length < 40000, $"{text.Length} characters of answer for {integrand}"); + Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.4).Substitute("g", 0.2).Substitute("f", 0.7); + var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x"); + var original = Pinned(integrand.ToEntity()); + var compared = 0; + foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.9 }) + { + var want = original.Substitute("x", at).EvalNumerical(); + var got = derivative.Substitute("x", at).EvalNumerical(); + if (want.IsNaN) + continue; + compared++; + Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) + < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)), + $"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}"); + } + Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}"); + } + } +}