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 @@ -396,6 +396,23 @@ in Rubi's 1.1.3.4 come out shorter as well, and those that took two seconds take
| `"1/(x^8*(a + b*x^3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,217 characters |
| `"1/(x^6*(a + b*x^4)*(c + d*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 795 characters |

### A linear below the cube root of a quadratic is integrated in closed form

**Answers where there were none.** `1/((d + e x) (d^2 + 3 e^2 x^2)^(1/3))` was declined, and so were six more of Rubi's
1.2.1.2. With `y = x + b/(2c)`, `1/((d + e x) (a + b x + c x^2)^(1/3))` is a linear in `y` below the cube root of
`a' + c y^2`, and where `c d'^2 = 3 a' e^2` or `c d'^2 = -9 a' e^2` it has a closed form of two or three logarithms and
an arctangent ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"1/((d+k*x)*(d^2+3*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 773 characters |
| `"1/((2+3*i*x)*(4-27*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 398 characters |
| `"1/((1+x^2)^(1/3)*(x+sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 290 characters |
| `"1/((1+x^2)^(1/3)*(-x+sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 302 characters |
| `"1/((d+k*x)*(d^2-9*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 397 characters |
| `"1/((d+k*x)*(c^2*d^2-b*c*d*k+b^2*k^2+3*b*c*k^2*x+3*c^2*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 1,934 characters |
| `"1/((d+k*x)*(-c^2*d^2+b*c*d*k+2*b^2*k^2+9*b*c*k^2*x+9*c^2*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 1,274 characters |

### A linear and a quadratic to one power are integrated as one binomial

**Answers where there were none.** `1/((1 + x)^(1/3) (1 - x + x^2)^(1/3))` was declined. A linear and a quadratic to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19988,6 +19988,83 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres
return null;
}

/// <summary>
/// A linear below the cube root of a quadratic, <c>1/((d + e x) (a + b x + c x^2)^(1/3))</c>,
/// in closed form where the shifted quadratic <c>a' + c y^2</c>, <c>y = x + b/(2 c)</c>, and
/// the linear <c>d' + e y</c> satisfy <c>c d'^2 = 3 a' e^2</c> or <c>c d'^2 = -9 a' e^2</c>.
/// </summary>
/// <remarks>
/// With <c>q^3 = 6 c^2 e^2/d'^2</c> and <c>R = (a + b x + c x^2)^(1/3)</c> the integral is
/// <c>-sqrt(3) c e/(d'^2 q^2) arctan(1/sqrt(3) + 2 c (d' - e y)/(sqrt(3) d' q R))
/// - 3 c e/(2 d'^2 q^2) ln(d + e x) + 3 c e/(2 d'^2 q^2) ln(c d' - c e y - d' q R)</c>, Rubi's
/// 1.2.1.2 rule. Where <c>c d'^2 = -9 a' e^2</c>, with <c>t = e y/d'</c>, <c>u = 1 - 3 t</c>
/// and <c>k^3 = a'</c>, it is <c>(-3/8 ln(2 R + k u) + 1/4 ln(d + e x) + 1/8 ln(u)
/// - sqrt(3)/4 arctan(sqrt(3) k u/(4 R - k u)))/(e k)</c>: on the curve
/// <c>w^3 = 1 - 9 t^2</c> the tangent at each point over <c>t = -1</c> meets it again at
/// <c>(1/3, 0)</c>, so <c>w + r (1 - 3 t)/2</c> for the cube roots of unity <c>r</c> are the
/// functions whose logarithms carry the residues. Otherwise the integral is elliptic. The
/// answer is differentiated back.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveALinearBelowACubeRootOfAQuadraticInClosedForm(Entity expr, Entity.Variable x)
{
var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
if (numerator.ContainsNode(x))
return null;
Entity? linear = null, quadratic = null;
foreach (var factor in Mulf.LinearChildren(denominator))
{
if (!factor.ContainsNode(x))
numerator /= factor;
else if (quadratic is null && factor is Powf(var @base, Number.Rational power) && power == Number.Rational.Create(1, 3))
quadratic = @base;
else if (linear is null && TreeAnalyzer.TryGetPolyLinear(factor, x, out _, out _))
linear = factor;
else
return null;
}
if (linear is null || quadratic is null
|| !TreeAnalyzer.TryGetPolyLinear(linear, x, out var e, out var d)
|| !TreeAnalyzer.TryGetPolynomial(quadratic, x, out var read)
|| read.Keys.Any(key => key.Sign < 0 || key.CompareTo(EInteger.FromInt32(2)) > 0)
|| !read.TryGetValue(EInteger.FromInt32(2), out var c) || VanishesIdentically(c.Expand()))
return null;
var a = read.TryGetValue(EInteger.Zero, out var a0) ? a0 : Number.Integer.Zero;
var b = read.TryGetValue(EInteger.One, out var b0) ? b0 : Number.Integer.Zero;
var shift = (b / (2 * c)).InnerSimplified;
var dShifted = (d - e * shift).InnerSimplified;
var aShifted = (a - b * b / (4 * c)).InnerSimplified;
if (VanishesIdentically(dShifted.Expand()) || VanishesIdentically(e.Expand()))
return null;
var three = Number.Integer.Create(3);
var root3 = MathS.Sqrt(three);
if (VanishesIdentically((c * dShifted * dShifted + 9 * aShifted * e * e).Expand().InnerSimplified.Expand()))
{
var k = MathS.Pow(aShifted, Number.Rational.Create(1, 3));
var cubeRoot = MathS.Pow(quadratic, Number.Rational.Create(1, 3));
var u = 1 - 3 * e * (x + shift) / dShifted;
var closed = numerator / (e * k) * (
-Number.Rational.Create(3, 8) * MathS.Ln(2 * cubeRoot + k * u)
+ Number.Rational.Create(1, 4) * MathS.Ln(linear)
+ Number.Rational.Create(1, 8) * MathS.Ln(u)
- root3 / 4 * MathS.Arctan(root3 * k * u / (4 * cubeRoot - k * u)));
closed = closed.InnerSimplified;
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(closed, expr, x) ? closed : null;
}
if (!VanishesIdentically((c * dShifted * dShifted - 3 * aShifted * e * e).Expand().InnerSimplified.Expand()))
return null;
var q = MathS.Pow(6 * c * c * e * e / (dShifted * dShifted), Number.Rational.Create(1, 3));
var r = MathS.Pow(quadratic, Number.Rational.Create(1, 3));
var y = x + shift;
var scale = c * e / (dShifted * dShifted * q * q);
var answer = numerator * (
-root3 * scale * MathS.Arctan(1 / root3 + 2 * c * (dShifted - e * y) / (root3 * dShifted * q * r))
- three / 2 * scale * MathS.Ln(linear)
+ three / 2 * scale * MathS.Ln(c * dShifted - c * e * y - dShifted * q * r));
answer = answer.InnerSimplified;
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
}

/// <summary>
/// A power of a linear that is not whole, times a linear, over a whole power of a quadratic,
/// with the power of the quadratic lowered one at a time:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,8 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// Two linears to one power that is not whole, as one quadratic to that power, before each
// linear is written once: `(2 + 3x)^4/((1 - 2x)^(5/2) (3 + 5x)^(5/2))` took forty-five
// seconds there, and over `(3 - x - 10x^2)^(5/2)` it takes half of one.
// A linear below the cube root of a quadratic, in closed form where Rubi has one.
if ((answer = IndefiniteIntegralSolver.SolveALinearBelowACubeRootOfAQuadraticInClosedForm(expr, x)) is { }) return answer;
if ((answer = IndefiniteIntegralSolver.SolveTwoLinearsToOnePowerAsOneQuadratic(expr, x, integrateByParts)) is { }) return answer;
// A linear and a quadratic to one power as one binomial, `(1 + x) (1 - x + x^2)` as
// `1 + x^3`, which the rules for a power of a binomial answer.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
// Website: https://am.angouri.org.
//

using System;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// A linear below the cube root of a quadratic, <c>1/((d + e x) (a + b x + c x^2)^(1/3))</c>,
/// where the shifted quadratic and the linear satisfy <c>c d^2 = 3 a e^2</c> or
/// <c>c d^2 = -9 a e^2</c>: Rubi's 1.2.1.2 closed forms, an arctangent and logarithms. Sampled on both sides of the linear's root,
/// and where the quadratic is negative, so the cube root is complex.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ALinearBelowACubeRootOfAQuadraticIntegralTest
{
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());

var pinned = integral.Substitute("C", 0);
Entity original = integrand.ToEntity();
foreach (var (name, value) in pins)
{
pinned = pinned.Substitute(name, value);
original = original.Substitute(name, value);
}
var derivative = pinned.Differentiate("x");
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
Assert.True(difference / scale < 1e-8,
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}

[Theory]
[InlineData("1/((1+x^2)^(1/3)*(x+sqrt(3)))")]
[InlineData("1/((1+x^2)^(1/3)*(-x+sqrt(3)))")]
[InlineData("1/((2+3*i*x)*(4-27*x^2)^(1/3))")]
[InlineData("1/((1+x)*(1-9*x^2)^(1/3))")]
public void WithNumbersIsAnsweredAndDifferentiatesBack(string integrand)
=> DifferentiatesBack(integrand);

[Theory]
[InlineData("1/((d+k*x)*(d^2+3*k^2*x^2)^(1/3))")]
[InlineData("1/((d+k*x)*(c^2*d^2-b*c*d*k+b^2*k^2+3*b*c*k^2*x+3*c^2*k^2*x^2)^(1/3))")]
[InlineData("1/((d+k*x)*(d^2-9*k^2*x^2)^(1/3))")]
[InlineData("1/((d+k*x)*(-c^2*d^2+b*c*d*k+2*b^2*k^2+9*b*c*k^2*x+9*c^2*k^2*x^2)^(1/3))")]
public void WithSymbolsIsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("b", 2), ("c", 3), ("d", 0.7), ("k", 0.5));
DifferentiatesBack(integrand, ("b", -2), ("c", 3), ("d", -0.7), ("k", 1.5));
}

[Fact]
public void IsNotAnsweredInClosedFormWithoutTheRelation()
{
// c d^2 = 2 a e^2: the integral is elliptic, and no closed form is claimed.
var integral = "1/((1+x)*(1+2*x^2)^(1/3))".ToEntity().Integrate("x");
Assert.DoesNotContain("arctan", integral.Stringize());
}
}
}
Loading