Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4236,8 +4236,8 @@ where `cos(y)` is positive; beyond it each turns its sign by its constant's. Two
integrand real on both sides at once, for a positive product of their constants, and then the turns
are as many as the roots of the cosine: an answer through two holds where the cosine is negative too,
with `sgn(cos(y))` in front for an odd number of roots of the cosine, and says
`provided a > 0 or cos(y) >= 0` where the product's sign is not known. Three say
`provided cos(y) >= 0` ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
`provided a > 0 or cos(y) >= 0` where the product's sign is not known. Three are in the entry
after this one ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
Expand All @@ -4249,6 +4249,26 @@ with `sgn(cos(y))` in front for an odd number of roots of the cosine, and says
| `"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))` |
| `"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` |

### An odd number of roots of `a 卤 a sec` and the secant or cosine holds where the cosine is negative

**Answers where there were none.** Three half-odd powers, as in
`sqrt(a + a sec(x)) sqrt(sec(x)) sqrt(cos(x))`, leave a root of `1 - tan(x/2)^2` in the answer, and
the answer said `provided cos(x) >= 0`. An odd number of roots makes the integrand real on one side
of the cosine's sign change at most, and on the negative one just where the product of the roots'
constants is negative, so `sqrt(-1 - sec(x)) sqrt(sec(x)) sqrt(cos(x))` had no value anywhere it is
real. There each root turns its sign by its constant's, as two do, and the turns are as many as the
roots of the cosine. The answer is kept beyond the condition where the antiderivative in the
half-angle tangent differentiates back there, and says `provided a < 0 or cos(y) >= 0` where the
product's sign is not known. The cosecant's the same way through the complement
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sqrt(-1 - sec(x))*sqrt(sec(x))*sqrt(cos(x))".ToEntity().Integrate("x")` | `integral(...)` | an arctangent in `tan(x/2)`, times `sgn(cos(x))` |
| `"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))` |
| `"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` |
| `"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` |

### An even power of the secant or the cosecant under a root is written in the tangent

**Answers where there were none.** `sqrt(a + b csc(x)^2)`, `(a + b sec(x)^2)^(3/2)` and their kin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2030,17 +2030,18 @@ internal static Entity Bare(Entity e)
/// <c>f</c> is shown real-valued, which a symbol that might be complex prevents and a
/// number does not: an answer with <c>sgn(tanh(c + d x))</c> in it, right, was
/// unevaluable differentiated with <c>c</c> and <c>d</c> in it and threw out of the
/// integrator.
/// integrator. The points may be the caller's, as <see cref="HoldsAtSampledPoints"/>
/// takes them.
/// </summary>
internal static bool DerivativeHoldsAtSampledPoints(Entity antiderivative, Entity integrand, Variable x)
internal static bool DerivativeHoldsAtSampledPoints(Entity antiderivative, Entity integrand, Variable x, string[]? points = null)
{
// Off the downcasting for the simplification of the pinned answer, which folds its
// numbers: downcast, a pinned 1.37 is 137/100 and every constant of the answer is
// folded exactly -- 28 s for the answer to u^5/((u^2 - 1)^5 (2 a u + b (u^2 + 1))),
// where in decimals it is 3 ms. The comparison itself reads no setting.
using var _ = MathS.Settings.DowncastingEnabled.Set(false);
var (pinnedAntiderivative, pinnedIntegrand) = Pinned(antiderivative, integrand, x);
return HoldsAtSampledPoints(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x);
return HoldsAtSampledPoints(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x, points);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5687,7 +5687,11 @@ bool IsAPolynomialInU(Entity part)
/// cosine is negative too, and there each written apart may have turned its sign: the answer
/// said <c>provided cos(y) >= 0</c>, half of the integrand's domain, for every one of 165
/// rows of Rubi's 4.5.4.2. Which turn is known from the constants, and the answer carries
/// the cosine's sign where they leave an odd number turned.
/// the cosine's sign where they leave an odd number turned. Three, as in
/// <c>sqrt(a + a sec(y)) sqrt(sec(y)) sqrt(cos(y))</c>, leave a root of <c>1 - t^2</c> in
/// the answer in t, which said <c>provided cos(y) >= 0</c> where for a negative a the
/// integrand is real only beyond it; the answer in t is kept beyond where it differentiates
/// back there.
/// https://github.com/asc-community/AngouriMath/issues/233
/// </para>
/// </remarks>
Expand Down Expand Up @@ -5841,19 +5845,24 @@ bool IsAPolynomialInU(Entity part)
// differing by e^(2 i pi p) = -1: where cos(y) < 0 a root of a 卤 a sec(y) or d sec(y)
// turns its sign if its constant is positive, and one of d cos(y) if its constant is
// negative. One root alone turns only where the integrand is complex, which is nothing
// to be wrong about. For an even number the turns multiply to (-1)^n s, for n roots of
// the cosine and s the sign of the product of the constants, and the integrand is real,
// on both sides at once, just where s is positive: `sqrt(sec(x)) sqrt(1 + sec(x))` holds
// as written where the cosine is negative, and `cos(x)^(3/2)/sqrt(1 + sec(x))` with the
// cosine's sign in front. The root of 1 - t^2 has cancelled then, so that the answer in
// t holds beyond t^2 < 1 too; for an odd number above one it has not, and the answer
// says where it holds, as it does where the product's sign is not known.
// to be wrong about. A root is imaginary there just where its constant is positive, so
// for N roots, n of them of the cosine, and s the sign of the product of the constants,
// the turns multiply to (-1)^(N + n) s, and the integrand is real there just where
// (-1)^N s is positive, and then the turns are (-1)^n: `sqrt(sec(x)) sqrt(1 + sec(x))`
// holds as written where the cosine is negative, and `cos(x)^(3/2)/sqrt(1 + sec(x))` with
// the cosine's sign in front. For an even N the root of 1 - t^2 has cancelled, so that
// the answer in t holds beyond t^2 < 1 too. For an odd one it has not, and the answer in
// t is kept beyond only where it differentiates back there. It said
// `provided cos(y) >= 0`, and `sqrt(a + a sec(x)) sqrt(sec(x)) sqrt(cos(x))` is real, for
// a negative a, only beyond that.
if (roots < 2)
return answer;
var withinTheHalfTurn = answer.Provided(companion >= Number.Integer.Zero);
if (!exponents[0].Numerator.Remainder(exponents[0].Denominator).IsZero)
var odd = roots % 2 == 1;
if (odd == exponents[0].Numerator.Remainder(exponents[0].Denominator).IsZero
|| odd && !Functions.PartialFractions.DerivativeHoldsAtSampledPoints(result, integrand, t, BeyondTheHalfTurn))
return withinTheHalfTurn;
var turns = rootsOfTheCompanion % 2 == 1;
var turns = (roots + rootsOfTheCompanion) % 2 == 1;
switch (constants.Evaled)
{
case Number.Real { IsFinite: true } product when !product.IsZero:
Expand All @@ -5863,11 +5872,20 @@ bool IsAPolynomialInU(Entity part)
case Number:
return withinTheHalfTurn;
default:
return (turns ? MathS.Signum(companion) * answer : answer)
.Provided(constants > Number.Integer.Zero | companion >= Number.Integer.Zero);
// Where s is not known, the answer for the s that makes the integrand real.
var real = odd ? constants < Number.Integer.Zero : constants > Number.Integer.Zero;
return (rootsOfTheCompanion % 2 == 1 ? MathS.Signum(companion) * answer : answer)
.Provided(real | companion >= Number.Integer.Zero);
}
}

/// <summary>
/// Values of the half-angle tangent past one on either side, where the cosine is negative,
/// or for the cosecant the sine.
/// </summary>
[ConstantField]
private static readonly string[] BeyondTheHalfTurn = { "1.19", "1.43", "3.17", "7.3", "-1.61", "-2.9", "-6.1" };

/// <summary>
/// A half-odd power of <c>a 卤 a sec(y)</c> beside one of <c>c + d sec(y)</c>, integrated in
/// the half angle's sine, or its cosine for the minus: <c>a + a sec(y)</c> is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,64 @@ public void AProductOfConstantsOfUnknownSignSaysWhereItHolds()
}

/// <summary>
/// Three roots leave one of <c>1 - t^2</c> in the answer in t, which is not shown to hold
/// beyond <c>t^2 < 1</c>, and the answer says so.
/// Three roots leave one of <c>1 - t^2</c> in the answer in t, which is kept beyond
/// <c>t^2 < 1</c> where it differentiates back there. An odd number of roots makes the
/// integrand real on one side of the cosine's sign change at most, the negative one for a
/// negative product of the constants, and there the answer holds as written or with the
/// cosine's sign in front. It said <c>provided cos(x) >= 0</c>, and
/// <c>sqrt(-1 - sec(x)) sqrt(sec(x)) sqrt(cos(x))</c>, real only where the cosine is
/// negative, had no value anywhere it is real.
/// </summary>
[Fact]
public void ThreeRootsSayWhereTheyHold()
[Theory]
[InlineData("sqrt(cos(x))*sqrt(sec(x))*sqrt(1 + sec(x))", 3)]
[InlineData("(1 + sec(x))^(3/2)*sqrt(2*sec(x))*sqrt(3*cos(x))", 3)]
[InlineData("sqrt(-1 - sec(x))*sqrt(sec(x))*sqrt(cos(x))", 6)]
[InlineData("sqrt(1 - sec(x))*sqrt(sec(x))*sqrt(cos(x))", 6)]
[InlineData("sqrt(sin(x))*sqrt(csc(x))*sqrt(1 + csc(x))", 4)]
[InlineData("sqrt(-1 - csc(x))*sqrt(csc(x))*sqrt(sin(x))", 5)]
public void ThreeRootsHoldWhereTheIntegrandIsReal(string integrand, int compare)
{
Assert.DoesNotContain("provided", integrand.ToEntity().Integrate("x").Stringize());
HoldsWhereverTheIntegrandIsReal(integrand, BothSides, compare, Pins);
}

/// <summary>
/// Three roots whose product of constants is of unknown sign: the integrand is real where
/// the cosine is negative for a negative product, and the answer holds there, and where it
/// is positive for a positive one; the answer says <c>provided a < 0 or cos(x) >= 0</c>, or
/// <c>-a < 0</c> for <c>a - a sec(x)</c>, the integrand complex beyond it. Each at both
/// signs of the constant, and the cosecant's through the complement.
/// </summary>
[Theory]
[InlineData("sqrt(a + a*sec(x))*sqrt(sec(x))*sqrt(cos(x))", 1.7, 3)]
[InlineData("sqrt(a + a*sec(x))*sqrt(sec(x))*sqrt(cos(x))", -1.7, 6)]
[InlineData("sqrt(a + a*sec(x))*sec(x)^(3/2)*sqrt(cos(x))*(A + B*sec(x))", 1.7, 3)]
[InlineData("sqrt(a + a*sec(x))*sec(x)^(3/2)*sqrt(cos(x))*(A + B*sec(x))", -1.7, 6)]
[InlineData("sqrt(a - a*sec(x))*sqrt(sec(x))*sqrt(cos(x))*cos(x)", 1.7, 6)]
[InlineData("sqrt(a - a*sec(x))*sqrt(sec(x))*sqrt(cos(x))*cos(x)", -1.7, 3)]
[InlineData("sqrt(a + a*csc(x))*sqrt(csc(x))*sqrt(sin(x))", 1.7, 4)]
[InlineData("sqrt(a + a*csc(x))*sqrt(csc(x))*sqrt(sin(x))", -1.7, 5)]
public void ThreeRootsOfAConstantOfUnknownSignHoldWhereTheIntegrandIsReal(string integrand, double a, int compare)
{
const string integrand = "sqrt(cos(x))*sqrt(sec(x))*sqrt(1 + sec(x))";
Assert.Contains("provided cos(x) >= 0", integrand.ToEntity().Integrate("x").Stringize());
DifferentiatesBackPinned(integrand, new[] { 0.3, 0.7, 1.1, 1.4 });
Assert.Contains(" < 0 or ", integrand.ToEntity().Integrate("x").Stringize());
HoldsWhereverTheIntegrandIsReal(integrand, BothSides, compare, new[] { ("a", a), ("A", 0.6), ("B", 1.3) });
}

/// <summary>
/// The same at <c>y = 0.9 + 1.3 x</c>, where the points fall four where the cosine is
/// positive and five where it is negative, and with three binomials, of which the
/// integrand is real where the cosine is negative for <c>a c k > 0</c> and where it is
/// positive for <c>a c k < 0</c>.
/// </summary>
[Theory]
[InlineData("sqrt(a + a*sec(c + d*x))*sqrt(sec(c + d*x))*sqrt(cos(c + d*x))", 1.7, 0.9, 4)]
[InlineData("sqrt(a + a*sec(c + d*x))*sqrt(sec(c + d*x))*sqrt(cos(c + d*x))", -1.7, 0.9, 5)]
[InlineData("sqrt(a + a*sec(x))*sqrt(c + c*sec(x))*sqrt(k - k*sec(x))", 1.7, 0.9, 6)]
[InlineData("sqrt(a + a*sec(x))*sqrt(c + c*sec(x))*sqrt(k - k*sec(x))", -1.7, 0.9, 3)]
public void ThreeRootsOfSymbolsHoldWhereTheIntegrandIsReal(string integrand, double a, double c, int compare)
=> HoldsWhereverTheIntegrandIsReal(integrand, BothSides, compare,
new[] { ("a", a), ("c", c), ("d", 1.3), ("k", 1.3) });

/// <summary>The cosecant's, through the complement: <c>csc(y) = sec(pi/2 - y)</c>.</summary>
[Theory]
[InlineData("sqrt(1 + csc(x))")]
Expand Down
Loading