Skip to content

Commit 07d3df5

Browse files
Answer an odd number of secant roots where the cosine is negative too (#1967)
The half-angle rule beside a half-odd power of 1 ± sec writes each root of a constant times the secant or cosine apart in t = tan(y/2). For N roots, n of them of the cosine, with s the sign of the product of their constants: where the cosine is negative the turns multiply to (-1)^(N+n) s, the integrand is real there exactly where (-1)^N s > 0, and there the turns are (-1)^n. #1959 used this for an even N. For an odd N the root of 1 - t^2 stays in the answer in t, which is kept beyond t^2 < 1 only where its derivative holds there at sampled points of both signs. With the cosine's sign in front where n is odd, it holds on the whole real domain. Where s is not known the condition is now `provided <product> < 0 or cos(y) >= 0`. The cosecant takes the same path through the complement, with sgn(sin(y)). sqrt(-1 - sec(x)) sqrt(sec(x)) sqrt(cos(x)), real only where the cosine is negative, had no value anywhere it is real. No Rubi row has an odd root count above one, and the answers to every fair row of Rubi's 4.5 and 4.6 (2,966 rows, 1,317 of them with roots) are byte-identical. 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 820f8d9 commit 07d3df5

4 files changed

Lines changed: 110 additions & 24 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4236,8 +4236,8 @@ where `cos(y)` is positive; beyond it each turns its sign by its constant's. Two
42364236
integrand real on both sides at once, for a positive product of their constants, and then the turns
42374237
are as many as the roots of the cosine: an answer through two holds where the cosine is negative too,
42384238
with `sgn(cos(y))` in front for an odd number of roots of the cosine, and says
4239-
`provided a > 0 or cos(y) >= 0` where the product's sign is not known. Three say
4240-
`provided cos(y) >= 0` ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
4239+
`provided a > 0 or cos(y) >= 0` where the product's sign is not known. Three are in the entry
4240+
after this one ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
42414241

42424242
| Input | Was (2.5.0) | Now |
42434243
|---|---|---|
@@ -4249,6 +4249,26 @@ with `sgn(cos(y))` in front for an odd number of roots of the cosine, and says
42494249
| `"cos(x)^(3/2)/sqrt(1 + sec(x))".ToEntity().Integrate("x")` | `integral(...)` | a logarithm and a power in `tan(x/2)`, times `sgn(cos(x))` |
42504250
| `"cos(c+d*x)^(3/2)*(A+C*sec(c+d*x)^2)/sqrt(a+a*sec(c+d*x))".ToEntity().Integrate("x")` | `integral(...)` | the same, times `sgn(cos(c + d x))`, `provided a > 0 or cos(c + d x) >= 0` |
42514251

4252+
### An odd number of roots of `a ± a sec` and the secant or cosine holds where the cosine is negative
4253+
4254+
**Answers where there were none.** Three half-odd powers, as in
4255+
`sqrt(a + a sec(x)) sqrt(sec(x)) sqrt(cos(x))`, leave a root of `1 - tan(x/2)^2` in the answer, and
4256+
the answer said `provided cos(x) >= 0`. An odd number of roots makes the integrand real on one side
4257+
of the cosine's sign change at most, and on the negative one just where the product of the roots'
4258+
constants is negative, so `sqrt(-1 - sec(x)) sqrt(sec(x)) sqrt(cos(x))` had no value anywhere it is
4259+
real. There each root turns its sign by its constant's, as two do, and the turns are as many as the
4260+
roots of the cosine. The answer is kept beyond the condition where the antiderivative in the
4261+
half-angle tangent differentiates back there, and says `provided a < 0 or cos(y) >= 0` where the
4262+
product's sign is not known. The cosecant's the same way through the complement
4263+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
4264+
4265+
| Input | Was (2.5.0) | Now |
4266+
|---|---|---|
4267+
| `"sqrt(-1 - sec(x))*sqrt(sec(x))*sqrt(cos(x))".ToEntity().Integrate("x")` | `integral(...)` | an arctangent in `tan(x/2)`, times `sgn(cos(x))` |
4268+
| `"sqrt(1 - sec(x))*sqrt(sec(x))*sqrt(cos(x))".ToEntity().Integrate("x")` | `integral(...)` | a logarithm in `tan(x/2)`, times `sgn(cos(x)) sgn(tan(x/2))` |
4269+
| `"sqrt(a + a*sec(x))*sqrt(sec(x))*sqrt(cos(x))".ToEntity().Integrate("x")` | `integral(...)` | an arctangent in `tan(x/2)`, times `sgn(cos(x))`, `provided a < 0 or cos(x) >= 0` |
4270+
| `"sqrt(a + a*csc(x))*sqrt(csc(x))*sqrt(sin(x))".ToEntity().Integrate("x")` | `integral(...)` | an arctangent in `tan(pi/4 - x/2)`, times `sgn(sin(x))`, `provided a < 0 or sin(x) >= 0` |
4271+
42524272
### An even power of the secant or the cosecant under a root is written in the tangent
42534273

42544274
**Answers where there were none.** `sqrt(a + b csc(x)^2)`, `(a + b sec(x)^2)^(3/2)` and their kin,

‎Sources/AngouriMath/Functions/Algebra/Polynomials/PartialFractions.cs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,17 +2030,18 @@ internal static Entity Bare(Entity e)
20302030
/// <c>f</c> is shown real-valued, which a symbol that might be complex prevents and a
20312031
/// number does not: an answer with <c>sgn(tanh(c + d x))</c> in it, right, was
20322032
/// unevaluable differentiated with <c>c</c> and <c>d</c> in it and threw out of the
2033-
/// integrator.
2033+
/// integrator. The points may be the caller's, as <see cref="HoldsAtSampledPoints"/>
2034+
/// takes them.
20342035
/// </summary>
2035-
internal static bool DerivativeHoldsAtSampledPoints(Entity antiderivative, Entity integrand, Variable x)
2036+
internal static bool DerivativeHoldsAtSampledPoints(Entity antiderivative, Entity integrand, Variable x, string[]? points = null)
20362037
{
20372038
// Off the downcasting for the simplification of the pinned answer, which folds its
20382039
// numbers: downcast, a pinned 1.37 is 137/100 and every constant of the answer is
20392040
// folded exactly -- 28 s for the answer to u^5/((u^2 - 1)^5 (2 a u + b (u^2 + 1))),
20402041
// where in decimals it is 3 ms. The comparison itself reads no setting.
20412042
using var _ = MathS.Settings.DowncastingEnabled.Set(false);
20422043
var (pinnedAntiderivative, pinnedIntegrand) = Pinned(antiderivative, integrand, x);
2043-
return HoldsAtSampledPoints(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x);
2044+
return HoldsAtSampledPoints(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x, points);
20442045
}
20452046

20462047
/// <summary>

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

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5687,7 +5687,11 @@ bool IsAPolynomialInU(Entity part)
56875687
/// cosine is negative too, and there each written apart may have turned its sign: the answer
56885688
/// said <c>provided cos(y) >= 0</c>, half of the integrand's domain, for every one of 165
56895689
/// rows of Rubi's 4.5.4.2. Which turn is known from the constants, and the answer carries
5690-
/// the cosine's sign where they leave an odd number turned.
5690+
/// the cosine's sign where they leave an odd number turned. Three, as in
5691+
/// <c>sqrt(a + a sec(y)) sqrt(sec(y)) sqrt(cos(y))</c>, leave a root of <c>1 - t^2</c> in
5692+
/// the answer in t, which said <c>provided cos(y) >= 0</c> where for a negative a the
5693+
/// integrand is real only beyond it; the answer in t is kept beyond where it differentiates
5694+
/// back there.
56915695
/// https://github.com/asc-community/AngouriMath/issues/233
56925696
/// </para>
56935697
/// </remarks>
@@ -5841,19 +5845,24 @@ bool IsAPolynomialInU(Entity part)
58415845
// differing by e^(2 i pi p) = -1: where cos(y) < 0 a root of a ± a sec(y) or d sec(y)
58425846
// turns its sign if its constant is positive, and one of d cos(y) if its constant is
58435847
// negative. One root alone turns only where the integrand is complex, which is nothing
5844-
// to be wrong about. For an even number the turns multiply to (-1)^n s, for n roots of
5845-
// the cosine and s the sign of the product of the constants, and the integrand is real,
5846-
// on both sides at once, just where s is positive: `sqrt(sec(x)) sqrt(1 + sec(x))` holds
5847-
// as written where the cosine is negative, and `cos(x)^(3/2)/sqrt(1 + sec(x))` with the
5848-
// cosine's sign in front. The root of 1 - t^2 has cancelled then, so that the answer in
5849-
// t holds beyond t^2 < 1 too; for an odd number above one it has not, and the answer
5850-
// says where it holds, as it does where the product's sign is not known.
5848+
// to be wrong about. A root is imaginary there just where its constant is positive, so
5849+
// for N roots, n of them of the cosine, and s the sign of the product of the constants,
5850+
// the turns multiply to (-1)^(N + n) s, and the integrand is real there just where
5851+
// (-1)^N s is positive, and then the turns are (-1)^n: `sqrt(sec(x)) sqrt(1 + sec(x))`
5852+
// holds as written where the cosine is negative, and `cos(x)^(3/2)/sqrt(1 + sec(x))` with
5853+
// the cosine's sign in front. For an even N the root of 1 - t^2 has cancelled, so that
5854+
// the answer in t holds beyond t^2 < 1 too. For an odd one it has not, and the answer in
5855+
// t is kept beyond only where it differentiates back there. It said
5856+
// `provided cos(y) >= 0`, and `sqrt(a + a sec(x)) sqrt(sec(x)) sqrt(cos(x))` is real, for
5857+
// a negative a, only beyond that.
58515858
if (roots < 2)
58525859
return answer;
58535860
var withinTheHalfTurn = answer.Provided(companion >= Number.Integer.Zero);
5854-
if (!exponents[0].Numerator.Remainder(exponents[0].Denominator).IsZero)
5861+
var odd = roots % 2 == 1;
5862+
if (odd == exponents[0].Numerator.Remainder(exponents[0].Denominator).IsZero
5863+
|| odd && !Functions.PartialFractions.DerivativeHoldsAtSampledPoints(result, integrand, t, BeyondTheHalfTurn))
58555864
return withinTheHalfTurn;
5856-
var turns = rootsOfTheCompanion % 2 == 1;
5865+
var turns = (roots + rootsOfTheCompanion) % 2 == 1;
58575866
switch (constants.Evaled)
58585867
{
58595868
case Number.Real { IsFinite: true } product when !product.IsZero:
@@ -5863,11 +5872,20 @@ bool IsAPolynomialInU(Entity part)
58635872
case Number:
58645873
return withinTheHalfTurn;
58655874
default:
5866-
return (turns ? MathS.Signum(companion) * answer : answer)
5867-
.Provided(constants > Number.Integer.Zero | companion >= Number.Integer.Zero);
5875+
// Where s is not known, the answer for the s that makes the integrand real.
5876+
var real = odd ? constants < Number.Integer.Zero : constants > Number.Integer.Zero;
5877+
return (rootsOfTheCompanion % 2 == 1 ? MathS.Signum(companion) * answer : answer)
5878+
.Provided(real | companion >= Number.Integer.Zero);
58685879
}
58695880
}
58705881

5882+
/// <summary>
5883+
/// Values of the half-angle tangent past one on either side, where the cosine is negative,
5884+
/// or for the cosecant the sine.
5885+
/// </summary>
5886+
[ConstantField]
5887+
private static readonly string[] BeyondTheHalfTurn = { "1.19", "1.43", "3.17", "7.3", "-1.61", "-2.9", "-6.1" };
5888+
58715889
/// <summary>
58725890
/// A half-odd power of <c>a ± a sec(y)</c> beside one of <c>c + d sec(y)</c>, integrated in
58735891
/// the half angle's sine, or its cosine for the minus: <c>a + a sec(y)</c> is

‎Sources/Tests/UnitTests/Calculus/OnePlusASecantHalfPowerIntegralTest.cs‎

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,64 @@ public void AProductOfConstantsOfUnknownSignSaysWhereItHolds()
178178
}
179179

180180
/// <summary>
181-
/// Three roots leave one of <c>1 - t^2</c> in the answer in t, which is not shown to hold
182-
/// beyond <c>t^2 < 1</c>, and the answer says so.
181+
/// Three roots leave one of <c>1 - t^2</c> in the answer in t, which is kept beyond
182+
/// <c>t^2 < 1</c> where it differentiates back there. An odd number of roots makes the
183+
/// integrand real on one side of the cosine's sign change at most, the negative one for a
184+
/// negative product of the constants, and there the answer holds as written or with the
185+
/// cosine's sign in front. It said <c>provided cos(x) >= 0</c>, and
186+
/// <c>sqrt(-1 - sec(x)) sqrt(sec(x)) sqrt(cos(x))</c>, real only where the cosine is
187+
/// negative, had no value anywhere it is real.
183188
/// </summary>
184-
[Fact]
185-
public void ThreeRootsSayWhereTheyHold()
189+
[Theory]
190+
[InlineData("sqrt(cos(x))*sqrt(sec(x))*sqrt(1 + sec(x))", 3)]
191+
[InlineData("(1 + sec(x))^(3/2)*sqrt(2*sec(x))*sqrt(3*cos(x))", 3)]
192+
[InlineData("sqrt(-1 - sec(x))*sqrt(sec(x))*sqrt(cos(x))", 6)]
193+
[InlineData("sqrt(1 - sec(x))*sqrt(sec(x))*sqrt(cos(x))", 6)]
194+
[InlineData("sqrt(sin(x))*sqrt(csc(x))*sqrt(1 + csc(x))", 4)]
195+
[InlineData("sqrt(-1 - csc(x))*sqrt(csc(x))*sqrt(sin(x))", 5)]
196+
public void ThreeRootsHoldWhereTheIntegrandIsReal(string integrand, int compare)
197+
{
198+
Assert.DoesNotContain("provided", integrand.ToEntity().Integrate("x").Stringize());
199+
HoldsWhereverTheIntegrandIsReal(integrand, BothSides, compare, Pins);
200+
}
201+
202+
/// <summary>
203+
/// Three roots whose product of constants is of unknown sign: the integrand is real where
204+
/// the cosine is negative for a negative product, and the answer holds there, and where it
205+
/// is positive for a positive one; the answer says <c>provided a < 0 or cos(x) >= 0</c>, or
206+
/// <c>-a < 0</c> for <c>a - a sec(x)</c>, the integrand complex beyond it. Each at both
207+
/// signs of the constant, and the cosecant's through the complement.
208+
/// </summary>
209+
[Theory]
210+
[InlineData("sqrt(a + a*sec(x))*sqrt(sec(x))*sqrt(cos(x))", 1.7, 3)]
211+
[InlineData("sqrt(a + a*sec(x))*sqrt(sec(x))*sqrt(cos(x))", -1.7, 6)]
212+
[InlineData("sqrt(a + a*sec(x))*sec(x)^(3/2)*sqrt(cos(x))*(A + B*sec(x))", 1.7, 3)]
213+
[InlineData("sqrt(a + a*sec(x))*sec(x)^(3/2)*sqrt(cos(x))*(A + B*sec(x))", -1.7, 6)]
214+
[InlineData("sqrt(a - a*sec(x))*sqrt(sec(x))*sqrt(cos(x))*cos(x)", 1.7, 6)]
215+
[InlineData("sqrt(a - a*sec(x))*sqrt(sec(x))*sqrt(cos(x))*cos(x)", -1.7, 3)]
216+
[InlineData("sqrt(a + a*csc(x))*sqrt(csc(x))*sqrt(sin(x))", 1.7, 4)]
217+
[InlineData("sqrt(a + a*csc(x))*sqrt(csc(x))*sqrt(sin(x))", -1.7, 5)]
218+
public void ThreeRootsOfAConstantOfUnknownSignHoldWhereTheIntegrandIsReal(string integrand, double a, int compare)
186219
{
187-
const string integrand = "sqrt(cos(x))*sqrt(sec(x))*sqrt(1 + sec(x))";
188-
Assert.Contains("provided cos(x) >= 0", integrand.ToEntity().Integrate("x").Stringize());
189-
DifferentiatesBackPinned(integrand, new[] { 0.3, 0.7, 1.1, 1.4 });
220+
Assert.Contains(" < 0 or ", integrand.ToEntity().Integrate("x").Stringize());
221+
HoldsWhereverTheIntegrandIsReal(integrand, BothSides, compare, new[] { ("a", a), ("A", 0.6), ("B", 1.3) });
190222
}
191223

224+
/// <summary>
225+
/// The same at <c>y = 0.9 + 1.3 x</c>, where the points fall four where the cosine is
226+
/// positive and five where it is negative, and with three binomials, of which the
227+
/// integrand is real where the cosine is negative for <c>a c k > 0</c> and where it is
228+
/// positive for <c>a c k < 0</c>.
229+
/// </summary>
230+
[Theory]
231+
[InlineData("sqrt(a + a*sec(c + d*x))*sqrt(sec(c + d*x))*sqrt(cos(c + d*x))", 1.7, 0.9, 4)]
232+
[InlineData("sqrt(a + a*sec(c + d*x))*sqrt(sec(c + d*x))*sqrt(cos(c + d*x))", -1.7, 0.9, 5)]
233+
[InlineData("sqrt(a + a*sec(x))*sqrt(c + c*sec(x))*sqrt(k - k*sec(x))", 1.7, 0.9, 6)]
234+
[InlineData("sqrt(a + a*sec(x))*sqrt(c + c*sec(x))*sqrt(k - k*sec(x))", -1.7, 0.9, 3)]
235+
public void ThreeRootsOfSymbolsHoldWhereTheIntegrandIsReal(string integrand, double a, double c, int compare)
236+
=> HoldsWhereverTheIntegrandIsReal(integrand, BothSides, compare,
237+
new[] { ("a", a), ("c", c), ("d", 1.3), ("k", 1.3) });
238+
192239
/// <summary>The cosecant's, through the complement: <c>csc(y) = sec(pi/2 - y)</c>.</summary>
193240
[Theory]
194241
[InlineData("sqrt(1 + csc(x))")]

0 commit comments

Comments
 (0)