Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4595,6 +4595,76 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
return expr * inTermsOfU.Substitute(u, other.Radicand) / inX;
}

/// <summary>
/// An odd whole power of <c>tan(y)</c> beside a function of <c>sec(y)</c>, integrated in
/// <c>u = sec(y)</c>: <c>du = sec(y) tan(y) dy</c> and the even power of the tangent left over
/// is a power of <c>u^2 - 1</c>, so <c>tan(y)^n</c> with the rest is
/// <c>(u^2 - 1)^((n - 1)/2)/u du</c> beside it, exactly. The cotangent beside a function of
/// the cosecant, in <c>u = csc(y)</c>, the same.
/// </summary>
/// <remarks>
/// Rubi's <c>cot(c + d x)/(a + b sec(c + d x))^(3/2)</c> was declined and
/// <c>cot(c + d x)^3 sqrt(a + b sec(c + d x))</c> 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
/// </remarks>
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;
}

/// <summary>
/// <c>tan(y) tan(2y)</c> written <c>sec(2y) - 1</c>, and the integrand asked again in the
/// one argument <c>2y</c>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// An odd power of the tangent beside a function of the secant, in <c>u = sec(y)</c>, and the
/// cotangent beside one of the cosecant. Rubi's 4.5.1.4.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[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}");
}
}
}
Loading