Skip to content

Commit 081b56f

Browse files
Cancel a power of a cosine over a binomial in the sine (#1930)
* Cancel a power of a cosine over a power of one plus a sine before integrating Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the cosine powers over a binomial in the sine in 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 f77c4ad commit 081b56f

4 files changed

Lines changed: 144 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,20 @@ of a linear over a linear, which is answered in a second
486486
| `"1/(sqrt(a + i*a*tan(x))*(c + d*tan(x))^(3/2))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 2,217 characters |
487487
| `"1/((a + i*a*tan(x))^(3/2)*(c + d*tan(x))^(5/2))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 3,333 characters |
488488

489+
### A power of a cosine over `a + a sin` is cancelled to a polynomial in the sine
490+
491+
**Answers where there were none.** `cos(c + d x)^2/((e + f x) (a + a sin(c + d x)))` was left unevaluated, and so were three
492+
more of Rubi's 4.1.10. `cos^2/(1 + sin)` is `1 - sin` wherever it is defined, so the integrand becomes a sine or cosine
493+
over a power of a linear. That is answered with `Si` and `Ci`
494+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
495+
496+
| Input | Was (2.5.0) | Now |
497+
|---|---|---|
498+
| `"cos(c+d*x)^2/((pe+f*x)*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 132 characters |
499+
| `"cos(c+d*x)^2/((pe+f*x)^2*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 413 characters |
500+
| `"cos(c+d*x)^3/((pe+f*x)*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 246 characters |
501+
| `"cos(c+d*x)^3/((pe+f*x)^2*(a+a*sin(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 790 characters |
502+
489503
### Every linear beside a block is split off, a quadratic with rational roots among them
490504

491505
**Answers where there were none.** `sec(c + d x)/(a + b sin(c + d x)^3)` was declined, and so were

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26713,6 +26713,77 @@ static void Add(Dictionary<int, ERational> into, int k, ERational value)
2671326713
return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
2671426714
}
2671526715

26716+
/// <summary>
26717+
/// A power of a cosine over a power of <c>k (1 +- sin(y))</c>, or of a sine over one of
26718+
/// <c>k (1 +- cos(y))</c>, cancelled where the power above is at least twice the one below:
26719+
/// <c>cos^2 = (1 - sin)(1 + sin)</c>, so <c>cos(y)^m/(k (1 + sin(y)))^n</c> is
26720+
/// <c>cos(y)^(m - 2n) (1 - sin(y))^n/k^n</c>. What else is beside it stays as it is.
26721+
/// </summary>
26722+
/// <remarks>
26723+
/// Rubi's 4.1.10 <c>cos(c + d x)^2/((e + f x)(a + a sin(c + d x)))</c> was declined, and
26724+
/// <c>(1 - sin(c + d x))/(e + f x)</c> is answered in sine and cosine integrals.
26725+
/// https://github.com/asc-community/AngouriMath/issues/718
26726+
/// </remarks>
26727+
internal static Entity? SolveACosinePowerOverABinomialInTheSineByCancelling(Entity expr, Entity.Variable x)
26728+
{
26729+
if (!Integration.AnsweringTheQuestionAsked)
26730+
return null;
26731+
static IEnumerable<(Entity Factor, bool Below)> Factors(Entity e, bool below) => e switch
26732+
{
26733+
Mulf(var l, var r) => Factors(l, below).Concat(Factors(r, below)),
26734+
Divf(var l, var r) => Factors(l, below).Concat(Factors(r, !below)),
26735+
_ => new[] { (e, below) }
26736+
};
26737+
var factors = Factors(expr, false).ToList();
26738+
foreach (var (candidate, isBelow) in factors)
26739+
{
26740+
if (!isBelow)
26741+
continue;
26742+
var (binomial, n) = candidate is Powf(var b0, Number.Integer { EInteger: var p0 }) && p0.Sign > 0 && p0.CanFitInInt32()
26743+
? (b0, p0.ToInt32Unchecked()) : (candidate, 1);
26744+
// k (1 + sigma f(y)), f a sine or a cosine.
26745+
var functions = binomial.Nodes.Where(node => node is Sinf or Cosf && node.ContainsNode(x)).Distinct().ToList();
26746+
if (functions.Count != 1)
26747+
continue;
26748+
var function = functions[0];
26749+
var s0 = Variable.CreateUnique(expr, "s_cs");
26750+
if (!TreeAnalyzer.TryGetPolyLinear(binomial.Substitute(function, s0), s0, out var slope, out var k)
26751+
|| slope is null || k is null || slope.ContainsNode(x) || k.ContainsNode(x)
26752+
|| Functions.PartialFractions.IsZeroAsAValue(k))
26753+
continue;
26754+
var sigma = (slope / k).InnerSimplified;
26755+
int sign;
26756+
if (Functions.PartialFractions.IsZeroAsAValue((sigma - 1).InnerSimplified)) sign = 1;
26757+
else if (Functions.PartialFractions.IsZeroAsAValue((sigma + 1).InnerSimplified)) sign = -1;
26758+
else continue;
26759+
var argument = function is Sinf(var ys) ? ys : ((Cosf)function).Argument;
26760+
Entity complement = function is Sinf ? MathS.Cos(argument) : MathS.Sin(argument);
26761+
// The complement's power above the bar.
26762+
var m = 0;
26763+
foreach (var (factor, below) in factors)
26764+
if (!below && (factor == complement ? 1 : factor is Powf(var b1, Number.Integer { EInteger: var p1 }) && b1 == complement && p1.CanFitInInt32() ? p1.ToInt32Unchecked() : 0) is var power and > 0)
26765+
m += power;
26766+
if (m < 2 * n)
26767+
continue;
26768+
// Everything but the binomial and the complement's powers, times what the cancelling leaves.
26769+
Entity rest = Number.Integer.One;
26770+
foreach (var (factor, below) in factors)
26771+
{
26772+
if (below && factor == candidate)
26773+
continue;
26774+
if (!below && (factor == complement || factor is Powf(var b2, Number.Integer) && b2 == complement))
26775+
continue;
26776+
rest = below ? rest / factor : rest * factor;
26777+
}
26778+
var left = m - 2 * n;
26779+
Entity cancelled = (left == 0 ? Number.Integer.One : left == 1 ? complement : MathS.Pow(complement, Number.Integer.Create(left)))
26780+
* (n == 1 ? 1 - sign * function : MathS.Pow(1 - sign * function, Number.Integer.Create(n)))
26781+
/ (n == 1 ? k : MathS.Pow(k, Number.Integer.Create(n)));
26782+
return Integration.ComputeAsAQuestionOfItsOwn((rest * cancelled).InnerSimplified, x, integrateByParts: true);
26783+
}
26784+
return null;
26785+
}
26786+
2671626787
/// <summary>
2671726788
/// A rational function of a sine, or of a cosine, over a quadratic in it with a symbol
2671826789
/// among the coefficients: <c>sin(x)/(a + b sin(x) + c sin(x)^2)</c>, and the same with

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,8 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
680680
// A power of a sine, cosine or hyperbolic one over a binomial in a power of it, over the
681681
// binomial's roots, before the rules that spent the budget on `sinh(y)^4/(a + b sinh(y)^3)`.
682682
if ((answer = IndefiniteIntegralSolver.SolveAPowerOverABinomialInAPowerOfASineOverItsRoots(expr, x)) is { }) return answer;
683+
// A power of a cosine over a power of k (1 +- sin), cancelled: `cos^2/((e + f x)(a + a sin))`.
684+
if ((answer = IndefiniteIntegralSolver.SolveACosinePowerOverABinomialInTheSineByCancelling(expr, x)) is { }) return answer;
683685
// A function of an inverse function of c + d x beside powers of multiples of it, under
684686
// u = c + d x, before the rules that timed out on `(c e + d e x)^3 (a + b arsinh(c + d x))^2`.
685687
if ((answer = IndefiniteIntegralSolver.SolveAFunctionOfAnInverseOfALinearBesideItsPowersUnderTheLinear(expr, x)) is { }) return answer;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 power of a cosine over a power of <c>k (1 +- sin)</c>, cancelled: Rubi's 4.1.10
16+
/// <c>cos(c + d x)^2/((e + f x)(a + a sin(c + d x)))</c>, which was declined.
17+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
18+
/// </summary>
19+
[Trait("Area", "Calculus")]
20+
public sealed class ACosinePowerOverABinomialInTheSineIntegralTest
21+
{
22+
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
23+
{
24+
var integral = integrand.ToEntity().Integrate("x");
25+
Assert.DoesNotContain("integral(", integral.Stringize());
26+
27+
var pinned = integral.Substitute("C", 0);
28+
Entity original = integrand.ToEntity();
29+
foreach (var (name, value) in pins)
30+
{
31+
pinned = pinned.Substitute(name, value);
32+
original = original.Substitute(name, value);
33+
}
34+
var derivative = pinned.Differentiate("x");
35+
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
36+
{
37+
var got = derivative.Substitute("x", at).EvalNumerical();
38+
var want = original.Substitute("x", at).EvalNumerical();
39+
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
40+
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
41+
Assert.True(difference / scale < 1e-8,
42+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
43+
}
44+
}
45+
46+
// The linear's root kept off the sampled points.
47+
[Theory]
48+
[InlineData("cos(c+d*x)^2/((k+f*x)*(a+a*sin(c+d*x)))")]
49+
[InlineData("cos(c+d*x)^3/((k+f*x)^2*(a+a*sin(c+d*x)))")]
50+
[InlineData("sin(c+d*x)^2/((k+f*x)*(a-a*cos(c+d*x)))")]
51+
public void IsAnsweredAndDifferentiatesBack(string integrand)
52+
{
53+
DifferentiatesBack(integrand, ("a", 1.3), ("c", 0.7), ("d", 1.07), ("k", 5), ("f", 0.3));
54+
DifferentiatesBack(integrand, ("a", -0.5), ("c", -0.25), ("d", 1.5), ("k", -6), ("f", 0.4));
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)