Skip to content

Commit a3bc645

Browse files
Integrate a symbolic secant power, a half-odd power of a ± a sin, and an even cosine power (#1975)
Three seams of Rubi's family 4: - (b sec(y))^m tan(y)^3 was declined only because m was not a number; in u = sec(y) it is (b u)^m (u^2 - 1)/u, and the rule for an odd power of the tangent beside a function of the secant reads a power free of x whatever its exponent now. A negative power of the tangent beside it is hypergeometric and still declined. - 1/(sqrt(a + a sin(y)) (c + d sin(y))^2) and its kin are answered by the half angle at which a + a sin(y) is a square. They never reached it: the polynomial term asked the substitution search first, which spent the budget. - cos(y)^4 sin(y)^3/(a + b sin(y))^2: an even whole power of the cosine is a power of 1 - sin(y)^2, so beside a power of the sine above the bar the integrand is a polynomial in the sine over the power. Without a sine above the bar the rules after it answer as before: cos(y)^4/(a + b sin(y))^8 is 40K characters there and was 106K here. Rubi pocket (2,061 rows of family 4): 1967 -> 1994 solved; the moves re-run alone gain 28 rows (4.1.2.1 x16, 4.1.2.2 x7, 4.1.3.1:364, 4.3.0 x4), none lost. 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 a57ddcc commit a3bc645

6 files changed

Lines changed: 331 additions & 11 deletions

‎BREAKING-CHANGES.md‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,30 @@ cache is now keyed on the downcasting setting too
350350
|---|---|---|
351351
| `MathS.FromString("1/3").Evaled` under downcasting off, after the same parse with it on | `1/3` | `0.3333…` (one hundred digits) |
352352

353+
### A symbolic power of the secant, a half-odd power of `a ± a sin` beside other linears, and an even power of the cosine over a sine's binomial are integrated
354+
355+
**Answers where there were none.** `(b sec(g + f x))^m tan(g + f x)^3` and three more of Rubi's 4.3.0 were declined only
356+
because `m` was not a number: in `u = sec(y)` it is `(b u)^m (u^2 - 1)/u`, and the rule for an odd power of the tangent
357+
beside a function of the secant now reads a power free of x whatever its exponent.
358+
`1/(sqrt(a + a sin(y)) (c + d sin(y))^2)`, `1/((a + a sin(y))^2 sqrt(c - c sin(y)))` and fourteen more of 4.1.2.1 and
359+
4.1.3.1 are answered by the half angle at which `a + a sin(y)` is a square, which they never reached: the polynomial
360+
term wrote the reciprocal as a power and asked the substitution search, which spent the budget. A half-odd power of
361+
`a ± a sin(y)` beside at least one other linear in the same sine or cosine is asked it first now. And
362+
`cos(y)^4 sin(y)^3/(a + b sin(y))^2` and six more of 4.1.2.2 are read by the rule below, an even whole power of the
363+
cosine being a power of `1 - sin(y)^2` exactly, where a power of the sine stands above the bar beside it. That file's
364+
neighbours the unreleased master answered are respelled, some shorter and some longer ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
365+
366+
| Input | Was (2.5.0) | Now |
367+
|---|---|---|
368+
| `"(b*sec(g+f*x))^m*tan(g+f*x)^3".ToEntity().Integrate("x")` | `integral(...)` | 144 characters |
369+
| `"cot(g+f*x)^5*(b*csc(g+f*x))^m".ToEntity().Integrate("x")` | `integral(...)` | 221 characters |
370+
| `"1/(sqrt(a+a*sin(g+f*x))*(c+d*sin(g+f*x))^2)".ToEntity().Integrate("x")` | `integral(...)` | 2,679 characters |
371+
| `"1/((a+a*sin(g+f*x))^2*sqrt(c-c*sin(g+f*x)))".ToEntity().Integrate("x")` | `integral(...)` | 329 characters |
372+
| `"cos(c+d*x)^4*sin(c+d*x)^3/(a+b*sin(c+d*x))^2".ToEntity().Integrate("x")` | `integral(...)` | 4,174 characters |
373+
| `"cos(c+d*x)^6*sin(c+d*x)^3/(a+b*sin(c+d*x))^3".ToEntity().Integrate("x")` | `integral(...)` | 9,029 characters |
374+
| `"cos(c+d*x)^4*sin(c+d*x)^2/(a+b*sin(c+d*x))^2".ToEntity().Integrate("x")` | `integral(...)`; 7,068 characters, in 1.3 s, on the unreleased master | 3,677 characters, in 154 ms |
375+
| `"cos(c+d*x)^2*sin(c+d*x)^2/(a+b*sin(c+d*x))^3".ToEntity().Integrate("x")` | `integral(...)`; 4,621 characters, in 431 ms, on the unreleased master | 6,584 characters, in 570 ms |
376+
353377
### A polynomial in a sine over a power of a binomial in it is written in powers of the binomial
354378

355379
**Answers where there were none.** `(A + B cos(c + d x) + C cos(c + d x)^2)/(a + b cos(c + d x))^4` ran out of time, and

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

Lines changed: 179 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5364,7 +5364,8 @@ Entity Integral(ERational power)
53645364
/// </para>
53655365
/// https://github.com/asc-community/AngouriMath/issues/718
53665366
/// </remarks>
5367-
internal static Entity? SolveByTheHalfAngleWhereOnePlusASineIsASquare(Entity expr, Entity.Variable x, bool integrateByParts)
5367+
internal static Entity? SolveByTheHalfAngleWhereOnePlusASineIsASquare(Entity expr, Entity.Variable x, bool integrateByParts,
5368+
bool collectTheLinears = false)
53685369
{
53695370
if (!Integration.AnsweringTheQuestionAsked)
53705371
return null;
@@ -5445,6 +5446,41 @@ Entity Integral(ERational power)
54455446
constant = MathS.Pow(a, exponent) * constant;
54465447
return constant * MathS.Pow(square, Number.Integer.Create(twoP));
54475448
});
5449+
// `c + d sin(y)` as `(c - d) + 2d sin(u)^2`, collected, where asked: written
5450+
// `c + d (2 sin(u)^2 - 1)`, `sin(u)^5/(c + d (2 sin(u)^2 - 1))^2` ran past the budget.
5451+
if (collectTheLinears)
5452+
{
5453+
var (function, square) = sineKind ? (sine, MathS.Sqr(sinU)) : (cosine, MathS.Sqr(cosU));
5454+
rewritten = rewritten.Replace(node =>
5455+
{
5456+
if (node is not Sumf || !node.ContainsNode(function))
5457+
return node;
5458+
Entity constant = Number.Integer.Zero, coefficient = Number.Integer.Zero;
5459+
foreach (var term in Sumf.LinearChildren(node))
5460+
{
5461+
if (!term.ContainsNode(x))
5462+
{
5463+
constant += term;
5464+
continue;
5465+
}
5466+
Entity factor = Number.Integer.One;
5467+
var seen = false;
5468+
foreach (var part in Mulf.LinearChildren(term))
5469+
if (part == function && !seen)
5470+
seen = true;
5471+
else if (part.ContainsNode(x))
5472+
return node;
5473+
else
5474+
factor *= part;
5475+
if (!seen)
5476+
return node;
5477+
coefficient += factor;
5478+
}
5479+
var shifted = (constant - coefficient).InnerSimplified;
5480+
var collected = (2 * coefficient).InnerSimplified * square;
5481+
return VanishesIdentically(shifted) ? collected : shifted + collected;
5482+
});
5483+
}
54485484
var inU = rewritten.Substitute(sine, sineInU).Substitute(cosine, cosineInU);
54495485
// What is left of the variable is the variable itself: the angle is `rate x + offset`
54505486
// and `u` is half of it (a quarter turn on for the sine), so `x` is
@@ -5468,6 +5504,82 @@ Entity Integral(ERational power)
54685504
return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
54695505
}
54705506

5507+
/// <summary>
5508+
/// A half-odd power of <c>a ± a sin(y)</c> times whole or half-odd powers of other linears
5509+
/// in the same sine, <c>c + d sin(y)</c>, and nothing else of <c>x</c>: by
5510+
/// <see cref="SolveByTheHalfAngleWhereOnePlusASineIsASquare"/>, asked first. The cosine's
5511+
/// the same.
5512+
/// </summary>
5513+
/// <remarks>
5514+
/// <para>
5515+
/// Rubi's <c>1/(sqrt(a + a sin(e + f x)) (c + d sin(e + f x))^2)</c>,
5516+
/// <c>1/((a + a sin(e + f x))^2 sqrt(c - c sin(e + f x)))</c> and their kin in 4.1.2.1
5517+
/// ran past the budget. The half angle answers each, its question in <c>u</c> taking
5518+
/// tens of milliseconds, but it was not reached: the polynomial term above it wrote the
5519+
/// reciprocal as a power, took the constant out of the root, and asked the substitution
5520+
/// search below that, whose simplification of what it found took the whole budget.
5521+
/// </para>
5522+
/// <para>
5523+
/// The answer is that rule's, so exact on the same terms. Read narrowly: a product of
5524+
/// powers of linears in one sine or one cosine, one of them half-odd and of
5525+
/// <c>a ± a sin(y)</c>, and at least two of them, since one alone is
5526+
/// <see cref="SolveAHalfPowerOfOnePlusASine"/>'s.
5527+
/// https://github.com/asc-community/AngouriMath/issues/233
5528+
/// </para>
5529+
/// </remarks>
5530+
internal static Entity? SolveAHalfOddPowerOfOnePlusMinusASineBesidePowersOfLinearsInIt(Entity expr, Entity.Variable x, bool integrateByParts)
5531+
{
5532+
if (!Integration.AnsweringTheQuestionAsked)
5533+
return null;
5534+
if (!TryReadAsQuotient(expr, out var above, out var below))
5535+
(above, below) = (expr, Number.Integer.One);
5536+
Entity? function = null;
5537+
var (factors, halfOdd) = (0, false);
5538+
foreach (var side in new[] { above, below })
5539+
foreach (var factor in Mulf.LinearChildren(side))
5540+
{
5541+
if (!factor.ContainsNode(x))
5542+
continue;
5543+
var (@base, exponent) = factor is Powf(var inner, Number.Rational power) ? (inner, power.ERational) : (factor, ERational.One);
5544+
var isHalfOdd = exponent.Denominator.Equals(EInteger.FromInt32(2));
5545+
if (!isHalfOdd && !exponent.Denominator.Equals(EInteger.One))
5546+
return null;
5547+
// c + d f(y), the one f throughout, and c there: a power of f itself is not this shape.
5548+
Entity? found = null;
5549+
var constant = false;
5550+
foreach (var term in Sumf.LinearChildren(@base))
5551+
{
5552+
if (!term.ContainsNode(x))
5553+
{
5554+
constant = true;
5555+
continue;
5556+
}
5557+
foreach (var part in Mulf.LinearChildren(term))
5558+
if (part.ContainsNode(x))
5559+
{
5560+
if (found is not null || part is not (Sinf or Cosf))
5561+
return null;
5562+
found = part;
5563+
}
5564+
}
5565+
if (found is null || !constant || function is not null && found != function)
5566+
return null;
5567+
function = found;
5568+
if (isHalfOdd)
5569+
{
5570+
if (ReadAsOnePlusMinusAFunction(@base, function is Sinf ? function : MathS.Sin(function.DirectChildren.First()),
5571+
function is Cosf ? function : MathS.Cos(function.DirectChildren.First())) is null)
5572+
return null;
5573+
halfOdd = true;
5574+
}
5575+
factors++;
5576+
}
5577+
if (!halfOdd || factors < 2 || function is null
5578+
|| !TreeAnalyzer.TryGetPolyLinear(function.DirectChildren.First(), x, out var rate, out _) || rate.ContainsNode(x))
5579+
return null;
5580+
return SolveByTheHalfAngleWhereOnePlusASineIsASquare(expr, x, integrateByParts, collectTheLinears: true);
5581+
}
5582+
54715583
/// <summary>
54725584
/// Powers of <c>a ± a sin(y)</c> with a symbolic exponent, or beside a fractional power of
54735585
/// <c>g cos(y)</c>, and anything else in the sine alone, under <c>u = sin(y)</c>:
@@ -6154,8 +6266,12 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
61546266
/// <remarks>
61556267
/// Rubi's <c>cot(c + d x)/(a + b sec(c + d x))^(3/2)</c> was declined and
61566268
/// <c>cot(c + d x)^3 sqrt(a + b sec(c + d x))</c> ran past thirty seconds, where in the
6157-
/// secant each is a rational function beside the root of a linear.
6269+
/// secant each is a rational function beside the root of a linear. A power free of
6270+
/// <c>x</c> is read whatever its exponent: Rubi's <c>(b sec(e + f x))^m tan(e + f x)^3</c>,
6271+
/// 4.3.0, was declined only because <c>m</c> was not a number, where in <c>u</c> it is
6272+
/// <c>(b u)^m (u^2 - 1)/u</c>.
61586273
/// https://github.com/asc-community/AngouriMath/issues/718
6274+
/// https://github.com/asc-community/AngouriMath/issues/233
61596275
/// </remarks>
61606276
internal static Entity? SolveAnOddPowerOfTheTangentBesideAFunctionOfTheSecant(Entity expr, Entity.Variable x, bool integrateByParts)
61616277
{
@@ -6174,10 +6290,24 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
61746290
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out _)
61756291
|| rate.ContainsNode(x) || TreeAnalyzer.IsZero(rate))
61766292
return null;
6177-
if (!expr.Nodes.All(node => !node.ContainsNode(x)
6178-
|| node is Variable or Sumf or Minusf or Mulf or Divf or Sinf or Cosf or Secantf or Cosecantf or Tanf or Cotanf
6179-
|| node is Powf(_, Number.Rational)))
6180-
return null;
6293+
// A symbolic power too, of a product of constants and secants or cosecants:
6294+
// `(b sec(y))^m` is `(b u)^m`. Of a sum it brings `(a + b u)^m/u`, not elementary.
6295+
var z = Variable.CreateUnique(expr, "z_secant");
6296+
bool IsAProductOfSecants(Entity @base)
6297+
{
6298+
var inZ = @base.Replace(node => node is Secantf(var a) && a == argument || node is Cosecantf(var b) && b == argument ? z : node);
6299+
return !inZ.ContainsNode(x) && inZ.Nodes.All(node => !node.ContainsNode(z) || node == z || node is Mulf or Divf or Powf(_, Number.Rational));
6300+
}
6301+
var symbolic = false;
6302+
foreach (var node in expr.Nodes)
6303+
{
6304+
if (!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
6305+
|| node is Powf(_, Number.Rational))
6306+
continue;
6307+
if (node is not Powf(var @base, var exponent) || exponent.ContainsNode(x) || !IsAProductOfSecants(@base))
6308+
return null;
6309+
symbolic = true;
6310+
}
61816311
var t = Variable.CreateUnique(expr, "t_odd_tangent");
61826312
var u = Variable.CreateUnique(expr, "u_secant");
61836313
foreach (var ofTheSecant in new[] { true, false })
@@ -6193,6 +6323,9 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
61936323
var n = exponents[0];
61946324
if (rest.ContainsNode(t) || !n.IsInteger() || n.ToEInteger().IsEven || !n.ToEInteger().CanFitInInt32())
61956325
continue;
6326+
// Beside a symbolic power a negative one leaves `u^m/(u (u^2 - 1)^k)`, hypergeometric.
6327+
if (symbolic && n.Sign < 0)
6328+
continue;
61966329
var inU = rest.Replace(node => node switch
61976330
{
61986331
Secantf(var a) when ofTheSecant && a == argument => u,
@@ -27949,22 +28082,57 @@ when two.Equals(EInteger.FromInt32(2)) && e1 == MathS.e && e2 == MathS.e
2794928082
/// The rules after it took the whole quotient through the half-angle and wrote 30 million
2795028083
/// characters for <c>(A + B cos(x) + C cos(x)^2)/(a + b cos(x))^4</c> in eighteen seconds, where
2795128084
/// <c>1/(a + b cos(x))^4</c> alone is 44 thousand. Rubi's 4.1.4 and 4.2.4
27952-
/// <c>(a + b cos)^m (A + B cos + C cos^2)</c> at <c>m = -4</c> timed out.
28085+
/// <c>(a + b cos)^m (A + B cos + C cos^2)</c> at <c>m = -4</c> timed out. An even whole power
28086+
/// of the other function above the bar is a power of <c>1 - f^2</c>, exactly, and read as
28087+
/// one: Rubi's 4.1.2.2 <c>cos(c + d x)^6 sin(c + d x)^3/(a + b sin(c + d x))^2</c> and its
28088+
/// kin timed out too, where in the sine each is the polynomial over the power.
2795328089
/// https://github.com/asc-community/AngouriMath/issues/718
28090+
/// https://github.com/asc-community/AngouriMath/issues/233
2795428091
/// </remarks>
2795528092
internal static Entity? SolveAPolynomialInASineOverAPowerOfABinomialInItInPowersOfTheBinomial(Entity expr, Entity.Variable x)
2795628093
{
2795728094
if (!Integration.AnsweringTheQuestionAsked)
2795828095
return null;
2795928096
var functions = expr.Nodes.Where(node => node is Sinf or Cosf && node.ContainsNode(x)).Distinct().ToList();
27960-
if (functions.Count != 1)
28097+
if (functions.Count is not (1 or 2))
28098+
return null;
28099+
var s = Variable.CreateUnique(expr, "s_pow");
28100+
Entity? function = null, inS = null;
28101+
foreach (var candidate in functions)
28102+
{
28103+
// The other of the two only to even whole powers above the bar, each a power of
28104+
// 1 - f^2: `cos(y)^4 sin(y)^3/(a + b sin(y))^2` is a polynomial in the sine over the power.
28105+
var written = candidate;
28106+
if (functions.Count == 2)
28107+
{
28108+
var other = functions.Single(f => f != candidate);
28109+
if (candidate is Sinf && other is not Cosf || candidate is Cosf && other is not Sinf
28110+
|| other.DirectChildren.First() != candidate.DirectChildren.First())
28111+
return null;
28112+
// And only beside the function itself above the bar: `cos(y)^4/(a + b sin(y))^8`, with
28113+
// no sine above it, is answered by the rules after it in 40 thousand characters, and
28114+
// was 106 thousand here, in 60% more time.
28115+
var (top, _) = Functions.SingleQuotient.Of(expr);
28116+
if (!top.ContainsNode(candidate))
28117+
continue;
28118+
written = expr.Replace(node => node is Powf(var @base, Number.Integer { EInteger: var power }) && @base == other
28119+
&& power.Sign > 0 && power.IsEven && power.CompareTo(EInteger.FromInt32(12)) <= 0
28120+
? MathS.Pow(1 - MathS.Sqr(candidate), Number.Integer.Create(power / 2))
28121+
: node);
28122+
if (written.ContainsNode(other))
28123+
continue;
28124+
}
28125+
else
28126+
written = expr;
28127+
function = candidate;
28128+
inS = written.Substitute(candidate, s);
28129+
break;
28130+
}
28131+
if (function is null || inS is null)
2796128132
return null;
27962-
var function = functions[0];
2796328133
var argument = function switch { Sinf(var y) => y, Cosf(var y) => y, _ => null! };
2796428134
if (!TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || Functions.PartialFractions.IsZeroAsAValue(slope))
2796528135
return null;
27966-
var s = Variable.CreateUnique(expr, "s_pow");
27967-
var inS = expr.Substitute(function, s);
2796828136
if (inS.ContainsNode(x))
2796928137
return null;
2797028138
var (above, below) = Functions.SingleQuotient.Of(inS);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
776776
// A polynomial beside error functions of one shifted linear, written in it: by parts in
777777
// x leaves a Gaussian of the shifted linear beside a polynomial, which nothing reads.
778778
if ((answer = IndefiniteIntegralSolver.SolveAPolynomialBesideErrorFunctionsOfAShiftedLinear(expr, x, integrateByParts)) is { }) return answer;
779+
// A half-odd power of a ± a sin(y) beside powers of other linears in the sine, by the half
780+
// angle, before the polynomial term: beneath it the substitution search ran out of time.
781+
if ((answer = IndefiniteIntegralSolver.SolveAHalfOddPowerOfOnePlusMinusASineBesidePowersOfLinearsInIt(expr, x, integrateByParts)) is { }) return answer;
779782
if ((answer = IndefiniteIntegralSolver.SolveAsPolynomialTerm(expr, x, integrateByParts)) is { }) return answer;
780783
// A product of powers of the variable with a power of a constant multiple of it
781784
// among them, `(c x)^m x^n`, by the power rule with the written power kept as

0 commit comments

Comments
 (0)