Skip to content

Commit 8270086

Browse files
Integrate secant powers of a logarithm two apart as the derivative of a product (#1977)
With u = a + b ln(c x^n), B = b n and k = m + 1, the derivative of x^k sec(u)^(p - 2) (B (p - 2) tan(u) - k) is x^m (D sec(u)^p - E sec(u)^(p - 2)), where D = B^2 (p - 1)(p - 2) and E = B^2 (p - 2)^2 + k^2. So x^m (alpha sec(u)^p + beta sec(u)^(p - 2)) is a multiple of that product wherever beta D + alpha E = 0, and a lone power is one where E is zero. The cosecant's twin is the same, and k = 0 is left to the substitution. Rubi pocket (1,525 rows of family 4): 1480 -> 1487 solved, none lost. The moves re-run alone answer nine rows of 4.7.5: seven verified, and 4.7.5:337 and 395, whose integrands are complex along the whole real line, agree with Simpson's rule to 1e-13. Part of #233. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 56caf2c commit 8270086

4 files changed

Lines changed: 234 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

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

964+
### A secant or cosecant power of a logarithm beside the power two below it is integrated as the derivative of a product
965+
966+
**Answers where there were none.** `-(1 + b^2 n^2) sec(a + b ln(c x^n)) + 2 b^2 n^2 sec(a + b ln(c x^n))^3` was
967+
declined, and so were eight more of Rubi's 4.7.5, `sec(a + i ln(c x^n)/(n (p - 2)))^p` and the cosecant's twins among
968+
them. With `u = a + b ln(c x^n)`, `B = b n` and `k = m + 1`, the derivative of `x^k sec(u)^(p - 2) (B (p - 2) tan(u) - k)`
969+
is `x^m (D sec(u)^p - E sec(u)^(p - 2))`, where `D = B^2 (p - 1)(p - 2)` and `E = B^2 (p - 2)^2 + k^2`. So
970+
`x^m (alpha sec(u)^p + beta sec(u)^(p - 2))` is a multiple of that product wherever `beta D + alpha E = 0`, and a lone
971+
power is where `E` is zero; the cosecant's product is `-x^k csc(u)^(p - 2) (B (p - 2) cot(u) + k)`. Not for `k = 0`,
972+
where the logarithm's derivative is beside it and the substitution answers
973+
([#233](https://github.com/asc-community/AngouriMath/issues/233)).
974+
975+
| Input | Was (2.5.0) | Now |
976+
|---|---|---|
977+
| `"-(1+b^2*n^2)*sec(a+b*ln(c*x^n))+2*b^2*n^2*sec(a+b*ln(c*x^n))^3".ToEntity().Integrate("x")` | `integral(...)` | 77 characters |
978+
| `"x^m*sec(a+2*ln(c*x^(1/2*sqrt(-(1+m)^2))))^3".ToEntity().Integrate("x")` | `integral(...)` | 189 characters |
979+
| `"sec(a+i*ln(c*x^n)/(n*(-2+p)))^p".ToEntity().Integrate("x")` | `integral(...)` | 138 characters |
980+
| `"csc(a+2*ln(c/x^(1/2*i)))^3".ToEntity().Integrate("x")` | `integral(...)` | 102 characters |
981+
964982
### A symbolic power of a sine, cosine, secant or cosecant is integrated where it is the derivative of a product
965983

966984
**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

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

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32210,6 +32210,145 @@ private static bool IsAnImaginaryNumber(Entity expr)
3221032210
return answer.InnerSimplified;
3221132211
}
3221232212

32213+
/// <summary>
32214+
/// A power of the variable times a secant or cosecant power of a logarithm, beside the
32215+
/// power two below it, where the pair is the derivative of one product. With
32216+
/// <c>u = a + b ln(c x^n)</c>, so that <c>u' = B/x</c> for <c>B = b n</c>, and <c>k = m + 1</c>,
32217+
/// <c>d/dx [x^k sec(u)^(p - 2) (B (p - 2) tan(u) - k)] = x^m (D sec(u)^p - E sec(u)^(p - 2))</c>
32218+
/// with <c>D = B^2 (p - 1)(p - 2)</c> and <c>E = B^2 (p - 2)^2 + k^2</c>, so
32219+
/// <c>x^m (alpha sec(u)^p + beta sec(u)^(p - 2))</c> integrates to <c>alpha/D</c> times the
32220+
/// product wherever <c>beta D + alpha E = 0</c>. The cosecant's product is
32221+
/// <c>-x^k csc(u)^(p - 2) (B (p - 2) cot(u) + k)</c>, with the same <c>D</c> and <c>E</c>.
32222+
/// </summary>
32223+
/// <remarks>
32224+
/// Rubi's reduction for <c>x^m sec(a + b ln(c x^n))^p</c> lowers <c>p</c> by two and leaves
32225+
/// <c>E/D</c> times the lower power, which alone is a hypergeometric function; where
32226+
/// <c>E</c> is zero -- <c>b^2 n^2 (p - 2)^2 + (m + 1)^2 = 0</c> -- or the lower power is the
32227+
/// integrand's other term, the product is the whole answer. Declined before: the
32228+
/// exponentials are written for a numerical imaginary <c>b</c> only, and
32229+
/// <c>sec(a + i ln(c x^n)/(n (p - 2)))^p</c>, <c>x^m sec(a + 2 ln(c x^(sqrt(-(1 + m)^2)/2)))^3</c>
32230+
/// and <c>-(1 + b^2 n^2) sec(u) + 2 b^2 n^2 sec(u)^3</c> were unanswered or ran out of time.
32231+
/// The derivative uses only <c>z^(p - 2) z^2 = z^p</c>, <c>x^m x = x^k</c> and
32232+
/// <c>ln(c x^n)' = n/x</c>, which hold for principal powers wherever the base is not zero,
32233+
/// so the answer holds wherever the integrand is defined. Not for <c>k = 0</c>, where the
32234+
/// logarithm's own derivative is beside it and the substitution answers. Rubi's 4.7.5.
32235+
/// https://github.com/asc-community/AngouriMath/issues/233
32236+
/// </remarks>
32237+
internal static Entity? SolveSecantPowersOfALogarithmTwoApartAsTheDerivativeOfAProduct(Entity expr, Entity.Variable x)
32238+
{
32239+
static Entity? ArgumentOf(Entity node) => node switch { Secantf(var y) => y, Cosecantf(var y) => y, _ => null };
32240+
if (!expr.Nodes.Any(node => ArgumentOf(node) is { } y && y.Nodes.Any(inner => inner is Logf && inner.ContainsNode(x))))
32241+
return null;
32242+
Entity? argument = null;
32243+
var isSecant = false;
32244+
var monomials = Read(expr);
32245+
if (monomials is null || monomials.Count == 0 || argument is null)
32246+
return null;
32247+
// Gathered by their powers, and those whose coefficients cancel dropped.
32248+
var gathered = new List<(Entity Coefficient, Entity OfX, Entity OfF)>();
32249+
foreach (var (coefficient, ofX, ofF) in monomials)
32250+
{
32251+
var index = gathered.FindIndex(g => VanishesIdentically(g.OfX - ofX) && VanishesIdentically(g.OfF - ofF));
32252+
if (index < 0)
32253+
gathered.Add((coefficient, ofX.InnerSimplified, ofF.InnerSimplified));
32254+
else
32255+
gathered[index] = (gathered[index].Coefficient + coefficient, gathered[index].OfX, gathered[index].OfF);
32256+
}
32257+
gathered.RemoveAll(g => VanishesIdentically(g.Coefficient));
32258+
if (gathered.Count is 0 or > 2 || gathered.Count == 2 && !VanishesIdentically(gathered[0].OfX - gathered[1].OfX))
32259+
return null;
32260+
var (alpha, m, p) = gathered[0];
32261+
Entity beta = Number.Integer.Zero;
32262+
if (gathered.Count == 2)
32263+
{
32264+
var (otherCoefficient, _, otherPower) = gathered[1];
32265+
if (VanishesIdentically(otherPower - p + 2))
32266+
beta = otherCoefficient;
32267+
else if (VanishesIdentically(p - otherPower + 2))
32268+
(alpha, beta, p) = (otherCoefficient, alpha, otherPower);
32269+
else
32270+
return null;
32271+
}
32272+
// `x u'`, the constant `B`.
32273+
var rate = Functions.PartialFractions.Bare((argument.Differentiate(x) * x).InnerSimplified.Simplify());
32274+
var k = (m + 1).InnerSimplified;
32275+
if (rate.ContainsNode(x) || VanishesIdentically(rate) || VanishesIdentically(k))
32276+
return null;
32277+
var d = (rate * rate * (p - 1) * (p - 2)).InnerSimplified;
32278+
var e = (rate * rate * (p - 2) * (p - 2) + k * k).InnerSimplified;
32279+
if (VanishesIdentically(d) || !VanishesIdentically(beta * d + alpha * e))
32280+
return null;
32281+
var lowered = (p - 2).InnerSimplified;
32282+
// Folded, as `2 b^2 n^2/((b n)^2 2)` and `i/(p - 2) (p - 2)` are not by the simplification
32283+
// the answer gets.
32284+
var scale = Functions.PartialFractions.Bare((alpha / d).Simplify());
32285+
var slope = Functions.PartialFractions.Bare((rate * lowered).Simplify());
32286+
var product = isSecant
32287+
? MathS.Pow(x, k) * MathS.Pow(MathS.Sec(argument), lowered) * (slope * MathS.Tan(argument) - k)
32288+
: -MathS.Pow(x, k) * MathS.Pow(MathS.Cosec(argument), lowered) * (slope * MathS.Cotan(argument) + k);
32289+
return (scale * product).InnerSimplified;
32290+
32291+
// The integrand as monomials `coefficient x^m f(u)^p`, one `f` of one `u` throughout.
32292+
List<(Entity Coefficient, Entity OfX, Entity OfF)>? Read(Entity node)
32293+
{
32294+
if (!node.ContainsNode(x))
32295+
return new() { (node, Number.Integer.Zero, Number.Integer.Zero) };
32296+
if (node == x)
32297+
return new() { (Number.Integer.One, Number.Integer.One, Number.Integer.Zero) };
32298+
switch (node)
32299+
{
32300+
case Powf(var @base, var exponent) when @base == x && !exponent.ContainsNode(x):
32301+
return new() { (Number.Integer.One, exponent, Number.Integer.Zero) };
32302+
case Secantf or Cosecantf:
32303+
return Takes(node) ? new() { (Number.Integer.One, Number.Integer.Zero, Number.Integer.One) } : null;
32304+
case Powf(var function, var exponent) when function is Secantf or Cosecantf && !exponent.ContainsNode(x):
32305+
return Takes(function) ? new() { (Number.Integer.One, Number.Integer.Zero, exponent) } : null;
32306+
case Mulf(var left, var right):
32307+
return Product(Read(left), Read(right));
32308+
case Divf(var numerator, var denominator):
32309+
if (Read(denominator) is not { Count: 1 } below)
32310+
return null;
32311+
var (c, ofX, ofF) = below[0];
32312+
return Product(Read(numerator), new() { (Number.Integer.One / c, -ofX, -ofF) });
32313+
case Sumf(var augend, var addend):
32314+
return Sum(Read(augend), Read(addend), Number.Integer.One);
32315+
case Minusf(var minuend, var subtrahend):
32316+
return Sum(Read(minuend), Read(subtrahend), Number.Integer.MinusOne);
32317+
default:
32318+
return null;
32319+
}
32320+
}
32321+
32322+
bool Takes(Entity function)
32323+
{
32324+
var (inner, secant) = (ArgumentOf(function)!, function is Secantf);
32325+
if (argument is null)
32326+
(argument, isSecant) = (inner, secant);
32327+
return argument == inner && isSecant == secant;
32328+
}
32329+
32330+
static List<(Entity, Entity, Entity)>? Product(List<(Entity Coefficient, Entity OfX, Entity OfF)>? left, List<(Entity Coefficient, Entity OfX, Entity OfF)>? right)
32331+
{
32332+
if (left is null || right is null || left.Count * right.Count > 8)
32333+
return null;
32334+
var product = new List<(Entity, Entity, Entity)>();
32335+
foreach (var l in left)
32336+
foreach (var r in right)
32337+
product.Add((l.Coefficient * r.Coefficient, l.OfX + r.OfX, l.OfF + r.OfF));
32338+
return product;
32339+
}
32340+
32341+
static List<(Entity, Entity, Entity)>? Sum(List<(Entity Coefficient, Entity OfX, Entity OfF)>? left, List<(Entity Coefficient, Entity OfX, Entity OfF)>? right, Entity sign)
32342+
{
32343+
if (left is null || right is null || left.Count + right.Count > 8)
32344+
return null;
32345+
var sum = new List<(Entity, Entity, Entity)>(left);
32346+
foreach (var r in right)
32347+
sum.Add((sign * r.Coefficient, r.OfX, r.OfF));
32348+
return sum;
32349+
}
32350+
}
32351+
3221332352
/// <summary>
3221432353
/// A sum as the constant written in every one of its terms times the sum without it:
3221532354
/// <c>a - a sin(x)^2</c> is <c>a (1 - sin(x)^2)</c>, and <c>a + a sin(x)</c> is

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
927927
// The same with an imaginary coefficient on the logarithm, where the closed form divides
928928
// by zero: in exponentials, powers of the variable.
929929
if ((answer = IndefiniteIntegralSolver.SolveByWritingATrigonometricFunctionOfAnImaginaryLogarithmInExponentials(expr, x, integrateByParts)) is { }) return answer;
930+
// A secant or cosecant power of a logarithm beside the power two below it, where the
931+
// reduction closes: `sec(a + i ln(c x^n)/(n (p - 2)))^p` is the derivative of a product.
932+
if ((answer = IndefiniteIntegralSolver.SolveSecantPowersOfALogarithmTwoApartAsTheDerivativeOfAProduct(expr, x)) is { }) return answer;
930933
// A logarithm of a quotient that cancels with the functions in it as
931934
// indeterminates, which is how an inverse hyperbolic function of a hyperbolic
932935
// one arrives: atanh(tanh(u)) is 1/2 ln(e^(2u)) once its quotient is cancelled.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
/// <c>x^m (alpha sec(u)^p + beta sec(u)^(p - 2))</c> for <c>u = a + b ln(c x^n)</c>, and the same
16+
/// with cosecants, where the pair is the derivative of <c>x^(m + 1) sec(u)^(p - 2) (b n (p - 2) tan(u) - (m + 1))</c>:
17+
/// the reduction's remainder vanishing, <c>b^2 n^2 (p - 2)^2 + (m + 1)^2 = 0</c>, or being the
18+
/// integrand's other term. Rubi's 4.7.5.
19+
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
20+
/// </summary>
21+
[Trait("Area", "Calculus")]
22+
public sealed class SecantPowersOfALogarithmTwoApartAsTheDerivativeOfAProductIntegralTest
23+
{
24+
private static Entity Pinned(Entity e) => e
25+
.Substitute("a", 0.6).Substitute("b", 0.5).Substitute("c", 1.2)
26+
.Substitute("n", 1.0).Substitute("m", 0.6).Substitute("p", 3.5);
27+
28+
// Where the integral is answered, its derivative is the integrand at every point compared.
29+
private static bool DifferentiatesBack(string integrand)
30+
{
31+
var integral = integrand.ToEntity().Integrate("x");
32+
var text = integral.Stringize();
33+
if (text.Contains("integral("))
34+
return false;
35+
Assert.True(text.Length < 2000, $"{text.Length} characters of answer for {integrand}");
36+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
37+
var original = Pinned(integrand.ToEntity());
38+
var compared = 0;
39+
foreach (var at in new[] { 0.4, 0.9, 1.3, 1.7, 2.2, 2.9 })
40+
{
41+
var want = original.Substitute("x", at).EvalNumerical();
42+
var got = derivative.Substitute("x", at).EvalNumerical();
43+
if (want.IsNaN)
44+
continue;
45+
compared++;
46+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
47+
< 1e-8 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
48+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
49+
}
50+
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
51+
return true;
52+
}
53+
54+
[Theory]
55+
[InlineData("-(1+b^2*n^2)*sec(a+b*ln(c*x^n))+2*b^2*n^2*sec(a+b*ln(c*x^n))^3")]
56+
[InlineData("-(1+b^2*n^2)*csc(a+b*ln(c*x^n))+2*b^2*n^2*csc(a+b*ln(c*x^n))^3")]
57+
[InlineData("x^m*sec(a+2*ln(c*x^(1/2*sqrt(-(1+m)^2))))^3")]
58+
[InlineData("x^m*csc(a+2*ln(c*x^(1/2*sqrt(-(1+m)^2))))^3")]
59+
[InlineData("sec(a+i*ln(c*x^n)/(n*(-2+p)))^p")]
60+
[InlineData("sec(a-i*ln(c*x^n)/(n*(-2+p)))^p")]
61+
[InlineData("csc(a+i*ln(c*x^n)/(n*(-2+p)))^p")]
62+
[InlineData("csc(a-i*ln(c*x^n)/(n*(-2+p)))^p")]
63+
[InlineData("csc(a+2*ln(c/x^(1/2*i)))^3")]
64+
public void IsAnsweredAndDifferentiatesBack(string integrand)
65+
=> Assert.True(DifferentiatesBack(integrand), $"{integrand} was left unevaluated");
66+
67+
// The remainder does not vanish: alone, each power is a hypergeometric function of x.
68+
[Theory]
69+
[InlineData("sec(a+b*ln(c*x^n))^p")]
70+
[InlineData("x*sec(a+b*ln(c*x^n))^2")]
71+
public void IsNotClaimedWhereTheReductionDoesNotClose(string integrand)
72+
=> DifferentiatesBack(integrand);
73+
}
74+
}

0 commit comments

Comments
 (0)