Skip to content

Commit ded0d68

Browse files
Integrate a linear below a cube root of a quadratic in closed form (#1900)
* Integrate a linear below a cube root of a quadratic in closed form Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Integrate the linear below a cube root of a quadratic where c d^2 = -9 a e^2 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the cube-root closed forms in the 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 55a8044 commit ded0d68

4 files changed

Lines changed: 171 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,23 @@ in Rubi's 1.1.3.4 come out shorter as well, and those that took two seconds take
396396
| `"1/(x^8*(a + b*x^3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,217 characters |
397397
| `"1/(x^6*(a + b*x^4)*(c + d*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 795 characters |
398398

399+
### A linear below the cube root of a quadratic is integrated in closed form
400+
401+
**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
402+
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
403+
`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
404+
an arctangent ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
405+
406+
| Input | Was (2.5.0) | Now |
407+
|---|---|---|
408+
| `"1/((d+k*x)*(d^2+3*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 773 characters |
409+
| `"1/((2+3*i*x)*(4-27*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 398 characters |
410+
| `"1/((1+x^2)^(1/3)*(x+sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 290 characters |
411+
| `"1/((1+x^2)^(1/3)*(-x+sqrt(3)))".ToEntity().Integrate("x")` | `integral(...)` | 302 characters |
412+
| `"1/((d+k*x)*(d^2-9*k^2*x^2)^(1/3))".ToEntity().Integrate("x")` | `integral(...)` | 397 characters |
413+
| `"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 |
414+
| `"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 |
415+
399416
### A linear and a quadratic to one power are integrated as one binomial
400417

401418
**Answers where there were none.** `1/((1 + x)^(1/3) (1 - x + x^2)^(1/3))` was declined. A linear and a quadratic to

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19988,6 +19988,83 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres
1998819988
return null;
1998919989
}
1999019990

19991+
/// <summary>
19992+
/// A linear below the cube root of a quadratic, <c>1/((d + e x) (a + b x + c x^2)^(1/3))</c>,
19993+
/// in closed form where the shifted quadratic <c>a' + c y^2</c>, <c>y = x + b/(2 c)</c>, and
19994+
/// 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>.
19995+
/// </summary>
19996+
/// <remarks>
19997+
/// 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
19998+
/// <c>-sqrt(3) c e/(d'^2 q^2) arctan(1/sqrt(3) + 2 c (d' - e y)/(sqrt(3) d' q R))
19999+
/// - 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
20000+
/// 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>
20001+
/// 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)
20002+
/// - sqrt(3)/4 arctan(sqrt(3) k u/(4 R - k u)))/(e k)</c>: on the curve
20003+
/// <c>w^3 = 1 - 9 t^2</c> the tangent at each point over <c>t = -1</c> meets it again at
20004+
/// <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
20005+
/// functions whose logarithms carry the residues. Otherwise the integral is elliptic. The
20006+
/// answer is differentiated back.
20007+
/// https://github.com/asc-community/AngouriMath/issues/718
20008+
/// </remarks>
20009+
internal static Entity? SolveALinearBelowACubeRootOfAQuadraticInClosedForm(Entity expr, Entity.Variable x)
20010+
{
20011+
var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
20012+
if (numerator.ContainsNode(x))
20013+
return null;
20014+
Entity? linear = null, quadratic = null;
20015+
foreach (var factor in Mulf.LinearChildren(denominator))
20016+
{
20017+
if (!factor.ContainsNode(x))
20018+
numerator /= factor;
20019+
else if (quadratic is null && factor is Powf(var @base, Number.Rational power) && power == Number.Rational.Create(1, 3))
20020+
quadratic = @base;
20021+
else if (linear is null && TreeAnalyzer.TryGetPolyLinear(factor, x, out _, out _))
20022+
linear = factor;
20023+
else
20024+
return null;
20025+
}
20026+
if (linear is null || quadratic is null
20027+
|| !TreeAnalyzer.TryGetPolyLinear(linear, x, out var e, out var d)
20028+
|| !TreeAnalyzer.TryGetPolynomial(quadratic, x, out var read)
20029+
|| read.Keys.Any(key => key.Sign < 0 || key.CompareTo(EInteger.FromInt32(2)) > 0)
20030+
|| !read.TryGetValue(EInteger.FromInt32(2), out var c) || VanishesIdentically(c.Expand()))
20031+
return null;
20032+
var a = read.TryGetValue(EInteger.Zero, out var a0) ? a0 : Number.Integer.Zero;
20033+
var b = read.TryGetValue(EInteger.One, out var b0) ? b0 : Number.Integer.Zero;
20034+
var shift = (b / (2 * c)).InnerSimplified;
20035+
var dShifted = (d - e * shift).InnerSimplified;
20036+
var aShifted = (a - b * b / (4 * c)).InnerSimplified;
20037+
if (VanishesIdentically(dShifted.Expand()) || VanishesIdentically(e.Expand()))
20038+
return null;
20039+
var three = Number.Integer.Create(3);
20040+
var root3 = MathS.Sqrt(three);
20041+
if (VanishesIdentically((c * dShifted * dShifted + 9 * aShifted * e * e).Expand().InnerSimplified.Expand()))
20042+
{
20043+
var k = MathS.Pow(aShifted, Number.Rational.Create(1, 3));
20044+
var cubeRoot = MathS.Pow(quadratic, Number.Rational.Create(1, 3));
20045+
var u = 1 - 3 * e * (x + shift) / dShifted;
20046+
var closed = numerator / (e * k) * (
20047+
-Number.Rational.Create(3, 8) * MathS.Ln(2 * cubeRoot + k * u)
20048+
+ Number.Rational.Create(1, 4) * MathS.Ln(linear)
20049+
+ Number.Rational.Create(1, 8) * MathS.Ln(u)
20050+
- root3 / 4 * MathS.Arctan(root3 * k * u / (4 * cubeRoot - k * u)));
20051+
closed = closed.InnerSimplified;
20052+
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(closed, expr, x) ? closed : null;
20053+
}
20054+
if (!VanishesIdentically((c * dShifted * dShifted - 3 * aShifted * e * e).Expand().InnerSimplified.Expand()))
20055+
return null;
20056+
var q = MathS.Pow(6 * c * c * e * e / (dShifted * dShifted), Number.Rational.Create(1, 3));
20057+
var r = MathS.Pow(quadratic, Number.Rational.Create(1, 3));
20058+
var y = x + shift;
20059+
var scale = c * e / (dShifted * dShifted * q * q);
20060+
var answer = numerator * (
20061+
-root3 * scale * MathS.Arctan(1 / root3 + 2 * c * (dShifted - e * y) / (root3 * dShifted * q * r))
20062+
- three / 2 * scale * MathS.Ln(linear)
20063+
+ three / 2 * scale * MathS.Ln(c * dShifted - c * e * y - dShifted * q * r));
20064+
answer = answer.InnerSimplified;
20065+
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
20066+
}
20067+
1999120068
/// <summary>
1999220069
/// A power of a linear that is not whole, times a linear, over a whole power of a quadratic,
1999320070
/// with the power of the quadratic lowered one at a time:

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,8 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
668668
// Two linears to one power that is not whole, as one quadratic to that power, before each
669669
// linear is written once: `(2 + 3x)^4/((1 - 2x)^(5/2) (3 + 5x)^(5/2))` took forty-five
670670
// seconds there, and over `(3 - x - 10x^2)^(5/2)` it takes half of one.
671+
// A linear below the cube root of a quadratic, in closed form where Rubi has one.
672+
if ((answer = IndefiniteIntegralSolver.SolveALinearBelowACubeRootOfAQuadraticInClosedForm(expr, x)) is { }) return answer;
671673
if ((answer = IndefiniteIntegralSolver.SolveTwoLinearsToOnePowerAsOneQuadratic(expr, x, integrateByParts)) is { }) return answer;
672674
// A linear and a quadratic to one power as one binomial, `(1 + x) (1 - x + x^2)` as
673675
// `1 + x^3`, which the rules for a power of a binomial answer.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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 linear below the cube root of a quadratic, <c>1/((d + e x) (a + b x + c x^2)^(1/3))</c>,
16+
/// where the shifted quadratic and the linear satisfy <c>c d^2 = 3 a e^2</c> or
17+
/// <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,
18+
/// and where the quadratic is negative, so the cube root is complex.
19+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
20+
/// </summary>
21+
[Trait("Area", "Calculus")]
22+
public sealed class ALinearBelowACubeRootOfAQuadraticIntegralTest
23+
{
24+
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
25+
{
26+
var integral = integrand.ToEntity().Integrate("x");
27+
Assert.DoesNotContain("integral(", integral.Stringize());
28+
29+
var pinned = integral.Substitute("C", 0);
30+
Entity original = integrand.ToEntity();
31+
foreach (var (name, value) in pins)
32+
{
33+
pinned = pinned.Substitute(name, value);
34+
original = original.Substitute(name, value);
35+
}
36+
var derivative = pinned.Differentiate("x");
37+
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
38+
{
39+
var got = derivative.Substitute("x", at).EvalNumerical();
40+
var want = original.Substitute("x", at).EvalNumerical();
41+
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
42+
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
43+
Assert.True(difference / scale < 1e-8,
44+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
45+
}
46+
}
47+
48+
[Theory]
49+
[InlineData("1/((1+x^2)^(1/3)*(x+sqrt(3)))")]
50+
[InlineData("1/((1+x^2)^(1/3)*(-x+sqrt(3)))")]
51+
[InlineData("1/((2+3*i*x)*(4-27*x^2)^(1/3))")]
52+
[InlineData("1/((1+x)*(1-9*x^2)^(1/3))")]
53+
public void WithNumbersIsAnsweredAndDifferentiatesBack(string integrand)
54+
=> DifferentiatesBack(integrand);
55+
56+
[Theory]
57+
[InlineData("1/((d+k*x)*(d^2+3*k^2*x^2)^(1/3))")]
58+
[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))")]
59+
[InlineData("1/((d+k*x)*(d^2-9*k^2*x^2)^(1/3))")]
60+
[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))")]
61+
public void WithSymbolsIsAnsweredAndDifferentiatesBack(string integrand)
62+
{
63+
DifferentiatesBack(integrand, ("b", 2), ("c", 3), ("d", 0.7), ("k", 0.5));
64+
DifferentiatesBack(integrand, ("b", -2), ("c", 3), ("d", -0.7), ("k", 1.5));
65+
}
66+
67+
[Fact]
68+
public void IsNotAnsweredInClosedFormWithoutTheRelation()
69+
{
70+
// c d^2 = 2 a e^2: the integral is elliptic, and no closed form is claimed.
71+
var integral = "1/((1+x)*(1+2*x^2)^(1/3))".ToEntity().Integrate("x");
72+
Assert.DoesNotContain("arctan", integral.Stringize());
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)