Skip to content

Commit 7e5768b

Browse files
Split a power of x over a sum of two cubes over the cube roots of unity (#1895)
* Split a power of x over a sum of two cubes over the cube roots of unity Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the answers the split over the cube roots of unity gives 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 b4f5fbe commit 7e5768b

4 files changed

Lines changed: 164 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,19 @@ case is most of its length ([#718](https://github.com/asc-community/AngouriMath/
447447
| `"x^4/((a + b*x^2 + c*x^4)*sqrt(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 28,551 characters |
448448
| `"1/(x^2*(a + b*x^2 + c*x^4)*sqrt(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 10,194 characters |
449449

450+
### A power of x over a sum of two cubes is split over the cube roots of unity
451+
452+
**Answers where there were none.** `x^k/((3 a + b x^2)^3 + 27 a^2 c x^3)` was declined: the integrator's resultant needs
453+
numbers for coefficients. A sextic `d6 (x^2 + s)^3 + d3 x^3` is now split over the cube roots of unity into three
454+
quadratics, `x^2 + w m x + s` with `m^3 = d3/d6`. For two of them `w` is complex, so the answer carries `i` the way
455+
Rubi's does ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
456+
457+
| Input | Was (2.5.0) | Now |
458+
|---|---|---|
459+
| `"x^2/(27*a^3+27*a^2*b*x^2+27*a^2*c*x^3+9*a*b^2*x^4+b^3*x^6)".ToEntity().Integrate("x")` | `integral(...)` | 849 characters |
460+
| `"1/(27*a^3+27*a^2*b*x^2+27*a^2*c*x^3+9*a*b^2*x^4+b^3*x^6)".ToEntity().Integrate("x")` | `integral(...)` | 2,091 characters |
461+
| `"1/(x^2*(27*a^3+27*a^2*b*x^2+27*a^2*c*x^3+9*a*b^2*x^4+b^3*x^6))".ToEntity().Integrate("x")` | `integral(...)` | 3,820 characters |
462+
450463
### A polynomial of degree six with a large leading coefficient is factored
451464

452465
**Answers where there were none.** The factoring clears a leading coefficient by rescaling the variable, and declined

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

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19588,6 +19588,95 @@ void Add(int j, int k, Entity[] poly)
1958819588
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
1958919589
}
1959019590

19591+
/// <summary>
19592+
/// A power of <c>x</c> over a sextic that is a sum of two cubes,
19593+
/// <c>d6 ((x^2 + s)^3 + q x^3)</c>, split over the cube roots of unity:
19594+
/// <c>1/(U^3 + V^3) = sum_j w_j/(3 V^2 (U + w_j V))</c> for <c>U = x^2 + s</c>,
19595+
/// <c>V = m x</c>, <c>m^3 = q</c>, so that <c>x^k/(U^3 + V^3)</c> is
19596+
/// <c>sum_j w_j x^(k - 2)/(3 m^2 (x^2 + w_j m x + s))</c>, three quadratics.
19597+
/// </summary>
19598+
/// <remarks>
19599+
/// The sextic is read as <c>d6 x^6 + d4 x^4 + d3 x^3 + d2 x^2 + d0</c> with
19600+
/// <c>d4^2 = 3 d6 d2</c> and <c>d2^2 = 3 d4 d0</c>, which is what makes
19601+
/// <c>d6 x^6 + d4 x^4 + d2 x^2 + d0</c> the cube <c>d6 (x^2 + s)^3</c> with
19602+
/// <c>s = d4/(3 d6)</c>. The identity is the partial fractions of <c>1/(U^3 + V^3)</c> in
19603+
/// <c>U</c>, whose poles <c>-w_j V</c> have residues <c>1/(3 w_j^2 V^2) = w_j/(3 V^2)</c>; any
19604+
/// cube root <c>m</c> of <c>q</c> gives it. The three quadratics are one integral
19605+
/// <c>x^n/(x^2 + B x + S)</c> asked with <c>B</c> and <c>S</c> named. Rubi's 1.3.1
19606+
/// <c>x^k/(27 a^3 + 27 a^2 b x^2 + 27 a^2 c x^3 + 9 a b^2 x^4 + b^3 x^6)</c> was declined,
19607+
/// the integrator's resultant needing numbers for coefficients.
19608+
/// https://github.com/asc-community/AngouriMath/issues/718
19609+
/// </remarks>
19610+
internal static Entity? SolveAPowerOfXOverASumOfTwoCubes(Entity expr, Entity.Variable x, bool integrateByParts)
19611+
{
19612+
if (!Integration.AnsweringTheQuestionAsked || !expr.Vars.Any(symbol => symbol != x))
19613+
return null;
19614+
var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
19615+
Entity? sextic = null;
19616+
var power = 0;
19617+
Entity constant = Number.Integer.One;
19618+
foreach (var (side, below) in new[] { (numerator, false), (denominator, true) })
19619+
foreach (var factor in Mulf.LinearChildren(side))
19620+
{
19621+
if (!factor.ContainsNode(x))
19622+
{
19623+
constant = below ? constant / factor : constant * factor;
19624+
continue;
19625+
}
19626+
if (factor == x || factor is Powf(var bx, Number.Integer { EInteger: var wx }) && bx == x && wx.CanFitInInt32())
19627+
{
19628+
var whole = factor == x ? 1 : ((Number.Integer)((Powf)factor).Exponent).EInteger.ToInt32Unchecked();
19629+
power += below ? -whole : whole;
19630+
continue;
19631+
}
19632+
if (below && sextic is null)
19633+
{
19634+
sextic = factor;
19635+
continue;
19636+
}
19637+
return null;
19638+
}
19639+
if (sextic is null || Functions.PartialFractions.HoldsTheImaginaryUnit(sextic)
19640+
|| !TreeAnalyzer.TryGetPolynomial(sextic, x, out var read) || read.Keys.Any(key => key.Sign < 0 || key.CompareTo(EInteger.FromInt32(6)) > 0))
19641+
return null;
19642+
Entity At(int degree) => read.TryGetValue(EInteger.FromInt32(degree), out var coefficient) ? coefficient.InnerSimplified : Number.Integer.Zero;
19643+
var (d6, d5, d4, d3, d2, d1, d0) = (At(6), At(5), At(4), At(3), At(2), At(1), At(0));
19644+
if (!VanishesIdentically(d5) || !VanishesIdentically(d1)
19645+
|| VanishesIdentically(d6) || VanishesIdentically(d4) || VanishesIdentically(d3) || VanishesIdentically(d0)
19646+
|| !VanishesIdentically((d4 * d4 - 3 * d6 * d2).Expand()) || !VanishesIdentically((d2 * d2 - 3 * d4 * d0).Expand()))
19647+
return null;
19648+
var shift = Functions.PartialFractions.InLowestTermsOverTheSymbols(d4 / (3 * d6));
19649+
var cube = Functions.PartialFractions.InLowestTermsOverTheSymbols(d3 / d6);
19650+
var root = MathS.Pow(cube, Number.Rational.Create(1, 3));
19651+
var namedB = Variable.CreateUnique(expr, "B_c");
19652+
var namedS = Variable.CreateUnique(expr + namedB, "S_c");
19653+
var term = MathS.Pow(x, Number.Integer.Create(power - 2)) / (MathS.Pow(x, Number.Integer.Create(2)) + namedB * x + namedS);
19654+
if (Integration.ComputeAsAQuestionOfItsOwn(term, x, integrateByParts) is not { } each)
19655+
return null;
19656+
// The integral over a real quadratic splits on the sign of 4 S - B^2, which means nothing
19657+
// for the complex B of two of the three: its first arm, the arctangent over
19658+
// sqrt(4 S - B^2), differentiates back to the integrand for any B with 4 S != B^2, and
19659+
// the answer is checked below.
19660+
each = each.Replace(node => node switch
19661+
{
19662+
Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expression,
19663+
Providedf provided => provided.Expression,
19664+
_ => node,
19665+
});
19666+
var half = Number.Rational.Create(1, 2);
19667+
var unities = new Entity[]
19668+
{
19669+
Number.Integer.One,
19670+
-half + half * MathS.Sqrt(3) * MathS.i,
19671+
-half - half * MathS.Sqrt(3) * MathS.i,
19672+
};
19673+
Entity answer = Number.Integer.Zero;
19674+
foreach (var unity in unities)
19675+
answer = answer + unity * each.Substitute(namedB, unity * root).Substitute(namedS, shift);
19676+
answer = (constant / (3 * d6 * MathS.Pow(root, Number.Integer.Create(2))) * answer).InnerSimplified;
19677+
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
19678+
}
19679+
1959119680
/// <summary>
1959219681
/// A power of a linear that is not whole, times a linear, over a whole power of a quadratic,
1959319682
/// with the power of the quadratic lowered one at a time:

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
678678
// A rational function over a whole power of a symbolic quadratic, the power lowered one
679679
// at a time, before the partial fractions take the quadratic apart over its roots.
680680
if ((answer = IndefiniteIntegralSolver.SolveARationalFunctionOverAPowerOfASymbolicQuadratic(expr, x, integrateByParts)) is { }) return answer;
681+
// A power of x over a sextic that is a sum of two cubes, split over the cube roots of
682+
// unity into three quadratics before the partial fractions try the symbolic sextic.
683+
if ((answer = IndefiniteIntegralSolver.SolveAPowerOfXOverASumOfTwoCubes(expr, x, integrateByParts)) is { }) return answer;
681684
// A root or a power of a product beside a power of one of its factors, the product
682685
// split: `(a + b x^2)^(5/2)/sqrt(a^2 - b^2 x^4)` is `K (a + b x^2)^2/sqrt(a - b x^2)`.
683686
// Early, since what reads the product whole is slower and longer: Rubi's 1.2.1.2:2218,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 power of <c>x</c> over a sextic that is a sum of two cubes,
16+
/// <c>(3 a + b x^2)^3 + 27 a^2 c x^3</c>, split over the cube roots of unity into three
17+
/// quadratics. Rubi's 1.3.1 rows; the cube root of <c>27 a^2 c/b^3</c> is the principal one,
18+
/// complex for a negative <c>c</c>, and the three quadratics are still the factors.
19+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
20+
/// </summary>
21+
[Trait("Area", "Calculus")]
22+
public sealed class ASumOfTwoCubesIntegralTest
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("x^4/(27*a^3+27*a^2*b*x^2+27*a^2*c*x^3+9*a*b^2*x^4+b^3*x^6)")]
50+
[InlineData("x^2/(27*a^3+27*a^2*b*x^2+27*a^2*c*x^3+9*a*b^2*x^4+b^3*x^6)")]
51+
[InlineData("1/(27*a^3+27*a^2*b*x^2+27*a^2*c*x^3+9*a*b^2*x^4+b^3*x^6)")]
52+
[InlineData("1/(x^2*(27*a^3+27*a^2*b*x^2+27*a^2*c*x^3+9*a*b^2*x^4+b^3*x^6))")]
53+
public void IsAnsweredAndDifferentiatesBack(string integrand)
54+
{
55+
DifferentiatesBack(integrand, ("a", 2), ("b", 3), ("c", 5));
56+
DifferentiatesBack(integrand, ("a", -1), ("b", 2), ("c", -3));
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)