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
17 changes: 17 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,23 @@ antiderivative on each side of it, as Rubi's is
| `"1/((2 + 3*x^2)^(1/4)*(4 + 3*x^2))".ToEntity().Integrate("x")` | `integral(...)` | the same in `(2 + 3 x^2)^(1/4)` |
| `"1/((-2 + 3*x^2)*(-1 + 3*x^2)^(1/4))".ToEntity().Integrate("x")` | `integral(...)` | `-(arctan(u) + artanh(u))/(2 sqrt(6))`, `u = sqrt(3) x/(sqrt(2) (-1 + 3 x^2)^(1/4))` |

### The third case of a binomial differential is right for a negative `x` too

**Answers where there were none.** Chebyshev's third case, `x^m (a + b x^n)^(p/q)` with
`(m + 1)/n + p/q` whole, is integrated under `x = 1/y`, and came in after 2.5.0, which declined these.
On the unreleased master it wrote its root back as `(b + a/x^n)^(1/q)`, which is that root for a
positive `x`, and for a negative one only when `q` is odd: `1/(1 + x^4)^(5/4)` came out as
`1/(1 + 1/x^4)^(1/4)`, an even function, whose derivative is the integrand's negative for every
negative `x`. The root is written
back as `(a + b x^n)^(1/q)/x^(n/q)` now, which has the same `q`-th power everywhere, and the answers
are right on both sides of zero ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"1/(1 + x^4)^(5/4)".ToEntity().Integrate("x")` | `integral(...)` | `1 / ((1 + x ^ 4) ^ (1/4) / x)`, which is `x/(1 + x^4)^(1/4)` |
| `"x^2/(1 + x^4)^(3/4)".ToEntity().Integrate("x")` | `integral(...)` | logarithms and an arctangent of `(1 + x^4)^(1/4)/x` |
| `"x^6*(3 + 4*x^4)^(1/4)".ToEntity().Integrate("x")` | `integral(...)` | the same in `(3 + 4 x^4)^(1/4)/x` |

### A rational function of the sine and cosine with symbols in it is integrated by the half angle

`sin(x)^2/(a + b cos(x))` was left unevaluated while `sin(x)^2/(2 + 3 cos(x))` was answered. Under
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4470,7 +4470,13 @@ Entity ConstantNegative()
/// <c>x = 1/y</c> turns <c>x^m (a + b x^n)^(p/q) dx</c> into
/// <c>-y^m' (b + a y^n)^(p/q) dy</c> with <c>m' = -m - 2 - n p/q</c>, a whole number, and
/// <c>(m' + 1)/n = -(s + p/q)</c>, whole — so it is the second case in <c>y</c>, with the
/// roles of <c>a</c> and <c>b</c> exchanged, and <c>y = 1/x</c> put back afterwards.
/// roles of <c>a</c> and <c>b</c> exchanged. Its <c>u</c>, whose <c>q</c>-th power is
/// <c>b + a/x^n</c>, is put back as <c>(a + b x^n)^(1/q)/x^(n/q)</c>, which has that power
/// at every <c>x</c>. <c>(b + a/x^n)^(1/q)</c> is the same number for a positive <c>x</c>,
/// and for a negative one only under an odd root, which is real here:
/// <c>1/(1 + x^4)^(5/4)</c> came out as <c>1/(1 + 1/x^4)^(1/4)</c>, an even function
/// whose derivative is the integrand's negative for every negative <c>x</c>, where
/// <c>x/(1 + x^4)^(1/4)</c> is right on the whole line.
/// <c>x^6 (3 + 4x^4)^(1/4)</c> and <c>(x^3 - 1)/(2 + x^3)^(1/3)</c> are this. Chebyshev
/// proved there is no fourth case: outside these the integrand has no elementary
/// antiderivative at all, which is worth knowing before anyone goes looking.
Expand All @@ -4490,11 +4496,15 @@ Entity ConstantNegative()
if (!TryReadABinomialDifferential(expr, x, out var power, out var exponent,
out var inner, out var free, out var leading, out var factor))
return null;
var u = Variable.CreateUnique(expr, "u_binom");
var bracket = (Number.Rational.Create(free)
+ Number.Rational.Create(leading) * MathS.Pow(x, Number.Integer.Create(inner))).InnerSimplified;
var root = MathS.Pow(bracket, Number.Rational.Create(EInteger.One, exponent.Denominator));

// The second case: s = (m + 1)/n whole.
if ((power + 1) % inner == 0)
return IntegrateABinomialDifferentialInTheSecondCase(
power, inner, exponent, free, leading, x, factor);
power, inner, exponent, free, leading, u, root, factor);

// The third: s + p/q whole, taken to the second by x = 1/y.
var sPlusP = ERational.Create(power + 1, inner).Add(exponent);
Expand All @@ -4504,21 +4514,23 @@ Entity ConstantNegative()
if (!nTimesP.IsInteger() || !nTimesP.Numerator.CanFitInInt32())
return null;
var reflectedPower = -power - 2 - nTimesP.ToLowestTerms().Numerator.ToInt32Unchecked();
var y = Variable.CreateUnique(expr, "y_binom");
var back = root / MathS.Pow(x, Number.Rational.Create(ERational.Create(EInteger.FromInt32(inner), exponent.Denominator)));
if (IntegrateABinomialDifferentialInTheSecondCase(
reflectedPower, inner, exponent, leading, free, y, Number.Integer.MinusOne) is not { } inY)
reflectedPower, inner, exponent, leading, free, u, back, Number.Integer.MinusOne) is not { } reflected)
return null;
return (factor * inY.Substitute(y, 1 / x)).InnerSimplified;
return (factor * reflected).InnerSimplified;
}

/// <summary>
/// <c>int factor * v^m (a + b v^n)^(p/q) dv</c> with <c>(m + 1)/n</c> whole: a polynomial
/// in <c>u = (a + b v^n)^(1/q)</c> expanded term by term for <c>s >= 1</c>, and a rational
/// function of <c>u</c> handed to the rational integrator for <c>s &lt;= 0</c>.
/// <c>int factor * v^m (a + b v^n)^(p/q) dv</c> with <c>(m + 1)/n</c> whole, in
/// <paramref name="u"/> and then written as <paramref name="back"/>, anything whose
/// <c>q</c>-th power is <c>a + b v^n</c>: a polynomial in <c>u</c> expanded term by term for
/// <c>s >= 1</c>, and a rational function of <c>u</c> handed to the rational integrator for
/// <c>s &lt;= 0</c>.
/// </summary>
private static Entity? IntegrateABinomialDifferentialInTheSecondCase(
int power, int inner, ERational exponent, ERational free, ERational leading,
Entity.Variable v, Entity factor)
Entity.Variable u, Entity back, Entity factor)
{
if ((power + 1) % inner != 0)
return null;
Expand All @@ -4527,11 +4539,8 @@ Entity ConstantNegative()
var p = exponent.Numerator.ToInt32Checked();
var a = Number.Rational.Create(free);
var b = Number.Rational.Create(leading);
var u = Variable.CreateUnique(v + factor, "u_binom");
var outside = Number.Integer.Create(q)
/ (Number.Integer.Create(inner) * MathS.Pow(b, Number.Integer.Create(s)));
var bracket = (a + b * MathS.Pow(v, Number.Integer.Create(inner))).InnerSimplified;
var back = MathS.Pow(bracket, Number.Rational.Create(EInteger.One, exponent.Denominator));

if (s < 1)
{
Expand Down
31 changes: 31 additions & 0 deletions Sources/Tests/UnitTests/Calculus/BinomialDifferentialTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,37 @@ public void TheMonomialCaseIsUnchangedInSubstance(string integrand)
[InlineData("x^2*(1 + x^2)^(-3/2)")]
public void TheThirdCaseThroughTheReciprocal(string integrand) => DifferentiatesBack(integrand);

/// <summary>
/// The third case's <c>u</c> has <c>b + a/x^n</c> for its <c>q</c>-th power, and written
/// as <c>(b + a/x^n)^(1/q)</c> it is that root for a positive <c>x</c>, and for a negative
/// one only where <c>q</c> is odd: <c>1/(1 + x^4)^(5/4)</c> came out as
/// <c>1/(1 + 1/x^4)^(1/4)</c>, which is even, and its derivative was the integrand's
/// negative for every negative <c>x</c>. Written as <c>(a + b x^n)^(1/q)/x^(n/q)</c> it is
/// right on both sides of zero under either root, and each of these is real on both.
/// </summary>
[Theory]
[InlineData("1/(1 + x^4)^(5/4)")]
[InlineData("x^2/(1 + x^4)^(3/4)")]
[InlineData("x^6*(3 + 4*x^4)^(1/4)")]
[InlineData("x^3/(2 + x^3)^(1/3)")]
[InlineData("1/(2 + x^3)^(1/3)")]
[InlineData("(x^3 - 1)/(2 + x^3)^(1/3)")]
public void TheThirdCaseOnBothSidesOfZero(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
var derivative = integral.Substitute("C", 0).Differentiate("x");
var original = integrand.ToEntity();
foreach (var at in new[] { -1.1, -0.7, -0.35, 0.35, 0.83, 1.4 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
Assert.True(Math.Abs((double)want.ImaginaryPart) < 1e-12, $"the integrand at x = {at} is {want}, not real");
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart) < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}

/// <summary>
/// Outside all three of Chebyshev's cases there is — and this is the part worth knowing
/// before anyone goes looking — no elementary antiderivative at all, which he proved.
Expand Down
Loading