Skip to content

Commit 1e79b0e

Browse files
Integrate a symbolic power of a trigonometric function where it is the derivative of a product (#1912)
* Integrate a symbolic power of a trigonometric function where it is the derivative of a product f^m (alpha + beta f^2) for f a sine, cosine, secant or cosecant of a linear is the derivative of cos sin^(m + 1), sin cos^(m + 1), tan sec^m or cot csc^m where beta p0 = alpha p2, p0 + p2 f^2 being that derivative's bracket. Rubi's 4.1.4.1 and 4.5.4.1 sec(c + d x)^m (m - (1 + m) sec(c + d x)^2), sin(e + f x)^m (1 + m - (2 + m) sin(e + f x)^2) and the like were declined. Part of #718 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the trigonometric derivative of a product in the breaking changes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent bc175c2 commit 1e79b0e

4 files changed

Lines changed: 133 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,20 @@ in Rubi's 1.1.3.4 come out shorter as well, and those that took two seconds take
396396
| `"1/(x^8*(a + b*x^3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,217 characters |
397397
| `"1/(x^6*(a + b*x^4)*(c + d*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 795 characters |
398398

399+
### A symbolic power of a sine, cosine, secant or cosecant is integrated where it is the derivative of a product
400+
401+
**Answers where there were none.** `sec(c + d x)^m (m - (1 + m) sec(c + d x)^2)` was declined, and so were five more
402+
of Rubi's 4.1.4.1, 4.2.4.1 and 4.5.4.1. `f^m (alpha + beta f^2)` for `f` a sine, cosine, secant or cosecant of a linear
403+
is the derivative of `cos sin^(m + 1)`, `sin cos^(m + 1)`, `tan sec^m` or `cot csc^m` where its bracket is a multiple of
404+
that derivative's, and is now integrated so ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
405+
406+
| Input | Was (2.5.0) | Now |
407+
|---|---|---|
408+
| `"sec(c+d*x)^m*(-q*m/(1+m)+q*sec(c+d*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 75 characters |
409+
| `"sec(c+d*x)^m*(A-A*(1+m)*sec(c+d*x)^2/m)".ToEntity().Integrate("x")` | `integral(...)` | 54 characters |
410+
| `"sec(k+f*x)^m*(m-(1+m)*sec(k+f*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 54 characters |
411+
| `"sin(k+f*x)^m*(1+m-(2+m)*sin(k+f*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 71 characters |
412+
399413
### A power of a sine or hyperbolic sine over a binomial in a power of it is split over the binomial's roots
400414

401415
**Answers where there were none.** `sinh(c + d x)^4/(a + b sinh(c + d x)^3)` was declined, and so were eight more of

‎Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29317,6 +29317,53 @@ bool HoldsItsValue(Entity factor)
2931729317
return (s * logarithm - remainder).InnerSimplified;
2931829318
}
2931929319

29320+
/// <summary>
29321+
/// <c>f^m (alpha + beta f^2)</c> for a symbolic <c>m</c> and <c>f</c> a sine, cosine, secant or
29322+
/// cosecant of a linear <c>u</c>, where it is the derivative of a product: with <c>u' = 1</c>,
29323+
/// <c>(cos sin^(m + 1))' = sin^m ((m + 1) - (m + 2) sin^2)</c>,
29324+
/// <c>(sin cos^(m + 1))' = cos^m ((m + 2) cos^2 - (m + 1))</c>,
29325+
/// <c>(tan sec^m)' = sec^m ((m + 1) sec^2 - m)</c> and <c>(cot csc^m)' = -csc^m ((m + 1) csc^2 - m)</c>.
29326+
/// </summary>
29327+
/// <remarks>
29328+
/// The integrand is read as <c>f^m</c> times a polynomial in <c>f</c> with only the constant and
29329+
/// square terms, <c>alpha + beta f^2</c>, which matches <c>p0 + p2 f^2</c> above where
29330+
/// <c>beta p0 = alpha p2</c>; the answer is <c>(alpha/p0) G/u'</c>. Rubi's 4.1.4.1, 4.2.4.1 and 4.5.4.1
29331+
/// <c>sec(c + d x)^m (m - (1 + m) sec(c + d x)^2)</c> and the like were declined.
29332+
/// https://github.com/asc-community/AngouriMath/issues/718
29333+
/// </remarks>
29334+
internal static Entity? SolveASymbolicPowerOfATrigonometricFunctionAsTheDerivativeOfAProduct(Entity expr, Entity.Variable x)
29335+
{
29336+
if (!Integration.AnsweringTheQuestionAsked)
29337+
return null;
29338+
// The power with a symbol in its exponent, of a sine, cosine, secant or cosecant of a linear.
29339+
var power = expr.Nodes.OfType<Powf>().FirstOrDefault(node => node.Exponent.Vars.Any() && !node.Exponent.ContainsNode(x)
29340+
&& node.Base is Sinf or Cosf or Secantf or Cosecantf);
29341+
if (power is not Powf(var function, var m))
29342+
return null;
29343+
var argument = function switch { Sinf(var y) => y, Cosf(var y) => y, Secantf(var y) => y, Cosecantf(var y) => y, _ => null };
29344+
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || Functions.PartialFractions.IsZeroAsAValue(slope))
29345+
return null;
29346+
var s = Variable.CreateUnique(expr, "s_trig");
29347+
// The power taken off by writing 1 for it: `s^m (...)/s^m` is not cancelled by the simplification.
29348+
var inS = expr.Substitute(power, Number.Integer.One).Substitute(function, s).InnerSimplified;
29349+
if (inS.ContainsNode(x) || !TreeAnalyzer.TryGetPolynomial(inS.Expand(), s, out var read)
29350+
|| read.Keys.Any(key => !key.IsZero && !key.Equals(EInteger.FromInt32(2))))
29351+
return null;
29352+
var alpha = read.TryGetValue(EInteger.Zero, out var a0) ? a0 : Number.Integer.Zero;
29353+
var beta = read.TryGetValue(EInteger.FromInt32(2), out var b2) ? b2 : Number.Integer.Zero;
29354+
var (p0, p2, product) = function switch
29355+
{
29356+
Sinf => (m + 1, -(m + 2), MathS.Cos(argument) * MathS.Pow(function, m + 1)),
29357+
Cosf => (-(m + 1), m + 2, MathS.Sin(argument) * MathS.Pow(function, m + 1)),
29358+
Secantf => (-m, m + 1, MathS.Tan(argument) * MathS.Pow(function, m)),
29359+
_ => (m, -(m + 1), MathS.Cotan(argument) * MathS.Pow(function, m)),
29360+
};
29361+
if (Functions.PartialFractions.IsZeroAsAValue(p0.InnerSimplified)
29362+
|| !Functions.PartialFractions.IsZeroAsAValue((beta * p0 - alpha * p2).Expand().InnerSimplified))
29363+
return null;
29364+
return (alpha / (p0 * slope) * product).InnerSimplified;
29365+
}
29366+
2932029367
/// <summary>
2932129368
/// A power of a constant multiple of a radicand that a logarithm holds under a root,
2932229369
/// written over that radicand: <c>acosh(c x)</c> is <c>ln(c x + sqrt(c^2 x^2 - 1))</c>,

‎Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
664664
// A sum free of the variable that comes to a number multiplied out, as that number,
665665
// before anything reads a coefficient or an exponent in it: `2 + 2 a - 2 (1 + a)`.
666666
if ((answer = IndefiniteIntegralSolver.SolveByFoldingConstantSumsToTheirNumbers(expr, x, integrateByParts)) is { }) return answer;
667+
// A symbolic power of a sine, cosine, secant or cosecant times `alpha + beta f^2`, where it is
668+
// the derivative of `cos sin^(m + 1)`, `sin cos^(m + 1)`, `tan sec^m` or `cot csc^m`.
669+
if ((answer = IndefiniteIntegralSolver.SolveASymbolicPowerOfATrigonometricFunctionAsTheDerivativeOfAProduct(expr, x)) is { }) return answer;
667670
// A power of a sine, cosine or hyperbolic one over a binomial in a power of it, over the
668671
// binomial's roots, before the rules that spent the budget on `sinh(y)^4/(a + b sinh(y)^3)`.
669672
if ((answer = IndefiniteIntegralSolver.SolveAPowerOverABinomialInAPowerOfASineOverItsRoots(expr, x)) is { }) return answer;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
// Copyright (c) 2019-2026 Angouri.
3+
// AngouriMath is licensed under MIT.
4+
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
5+
// Website: https://am.angouri.org.
6+
//
7+
8+
using System;
9+
using AngouriMath.Extensions;
10+
using Xunit;
11+
12+
namespace AngouriMath.Tests.Calculus
13+
{
14+
/// <summary>
15+
/// A symbolic power of a sine, cosine, secant or cosecant times <c>alpha + beta f^2</c>, where it is
16+
/// the derivative of <c>cos sin^(m + 1)</c>, <c>sin cos^(m + 1)</c>, <c>tan sec^m</c> or <c>cot csc^m</c>:
17+
/// Rubi's 4.1.4.1 and 4.5.4.1.
18+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
19+
/// </summary>
20+
[Trait("Area", "Calculus")]
21+
public sealed class ASymbolicPowerOfATrigonometricFunctionAsADerivativeIntegralTest
22+
{
23+
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
24+
{
25+
var integral = integrand.ToEntity().Integrate("x");
26+
Assert.DoesNotContain("integral(", integral.Stringize());
27+
28+
var pinned = integral.Substitute("C", 0);
29+
Entity original = integrand.ToEntity();
30+
foreach (var (name, value) in pins)
31+
{
32+
pinned = pinned.Substitute(name, value);
33+
original = original.Substitute(name, value);
34+
}
35+
var derivative = pinned.Differentiate("x");
36+
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
37+
{
38+
var got = derivative.Substitute("x", at).EvalNumerical();
39+
var want = original.Substitute("x", at).EvalNumerical();
40+
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
41+
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
42+
Assert.True(difference / scale < 1e-8,
43+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
44+
}
45+
}
46+
47+
// d with the sign that keeps the argument real at every sampled x, and |d| below 2/2.3^2
48+
// so that the inverse sine and cosine stay inside [-1, 1]; off it the exact checks hold too.
49+
[Theory]
50+
[InlineData("sec(c+d*x)^m*(-q*m/(1+m)+q*sec(c+d*x)^2)")]
51+
[InlineData("sec(c+d*x)^m*(A-A*(1+m)*sec(c+d*x)^2/m)")]
52+
[InlineData("sin(c+d*x)^m*(1+m-(2+m)*sin(c+d*x)^2)")]
53+
[InlineData("cos(x)^m*(-(1+m)+(2+m)*cos(x)^2)")]
54+
[InlineData("csc(x)^m*(m-(1+m)*csc(x)^2)")]
55+
public void IsAnsweredAndDifferentiatesBack(string integrand)
56+
{
57+
DifferentiatesBack(integrand, ("A", 0.6), ("q", 1.25), ("c", 0.3), ("d", 0.75), ("m", 2.5));
58+
DifferentiatesBack(integrand, ("A", -0.75), ("q", 0.4), ("c", -1), ("d", -0.5), ("m", 1.0 / 3));
59+
}
60+
61+
[Fact]
62+
public void IsNotClaimedWhereTheBracketIsNotTheDerivative()
63+
{
64+
// beta p0 != alpha p2: (1 + m) sec^2 - (m + 1) is not the derivative of tan sec^m.
65+
var integral = "sec(x)^m*(1+m-(1+m)*sec(x)^2)".ToEntity().Integrate("x").Stringize();
66+
Assert.DoesNotContain("tan(x) * sec(x) ^ m", integral);
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)