Skip to content

Commit 56caf2c

Browse files
Integrate cube roots of two linears beside a power of a third, and a root over a power of x (b + c x) (#1976)
(a + b x)^(1/3) (c + d x)^(2/3)/(e + f x)^2 and its kin in Rubi's 1.1.1.3 are rational in t = (a + b x)^(1/3)/(c + d x)^(1/3). The substitution that reached it left a binomial in t^3 spelled as k (b - d t^3) + f (c t^3 - a), and the rational rules ran out of time on it. It is written as that binomial now, with the compound coefficients named, where the denominator in t is not squarefree. (A + B x) sqrt(d + e x)/(b x + c x^2)^3 and its kin in 1.2.1.3 are rational in t = sqrt(d + e x) with the quadratic kept as x (b + c x). The substitution that reached it late in the chain multiplied it out. Both rules decline where the linears share a root, decided as a value. Rubi pocket (2,824 rows of family 1): 2777 -> 2794 solved. The moves re-run alone gain 19 rows (1.1.1.3 x12, 1.2.1.2:447, 1.2.1.3:1394-1398). Two sampled rows master answers in 23-24 s timed out under load; at a 60 s budget both builds answer them with the same allocation to 0.0001%. Part of #233. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent a3bc645 commit 56caf2c

5 files changed

Lines changed: 417 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4049,6 +4049,28 @@ in both quadratics, the cube's coefficients grow past what the reduction takes,
40494049
| `"(7 + 13*x)/((5 + x + 2*x^2)^3*sqrt(2 + x + 3*x^2))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative |
40504050
| `"sqrt(a + a*sec(x))/(c + d*sec(x))^2".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative in `tan(x/2)` |
40514051

4052+
### Cube roots of two linears beside a power of a third, and an odd half power of a linear over a power of `x (b + c x)`, are integrated
4053+
4054+
**Answers where there were none.** `(a + b x)^(1/3) (c + d x)^(2/3)/(e + f x)^2` and its kin, Rubi's 1.1.1.3, and
4055+
`(A + B x) sqrt(d + e x)/(b x + c x^2)^3` and its kin, 1.2.1.3, were declined. The first are rational in
4056+
`t = (a + b x)^(1/3)/(c + d x)^(1/3)` wherever the two powers add to a whole number, and exactly so for every complex
4057+
`x`; the substitution that reaches it left `k (b - d t^3) + f (c t^3 - a)` where a binomial in `t^3` stands, and the
4058+
rational rules ran out of time on it. It is now written as that binomial from the coefficients, each compound
4059+
coefficient named by a symbol of its own and written back into the answer, where the denominator in `t` is not
4060+
squarefree, as for a third linear below the bar at a power of `-2` or less. The second is rational in
4061+
`t = sqrt(d + e x)` with the quadratic kept as `x (b + c x)`, which the substitution that reaches it, late in the
4062+
chain, multiplied out; it is asked so now, named the same way, for the root above the bar and a third power or
4063+
higher. Each declines where its linears share a root, decided as a value
4064+
([#233](https://github.com/asc-community/AngouriMath/issues/233)).
4065+
4066+
| Input | Was (2.5.0) | Now |
4067+
|---|---|---|
4068+
| `"(a + b*x)^(1/3)*(c + d*x)^(2/3)/(k + f*x)^2".ToEntity().Integrate("x")` | `integral(...)` | 2,243 characters |
4069+
| `"1/((a + b*x)^(1/3)*(c + d*x)^(2/3)*(k + f*x)^3)".ToEntity().Integrate("x")` | `integral(...)` | 2,242 characters |
4070+
| `"(a + b*x)^(4/3)/((c + d*x)^(4/3)*(k + f*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 1,327 characters |
4071+
| `"(d + k*x)^(1/2)/(b*x + c*x^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 10,875 characters |
4072+
| `"(A + B*x)*(d + k*x)^(1/2)/(b*x + c*x^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 14,763 characters |
4073+
40524074
### Powers of two linears whose exponents sum to a whole number below `-2` are integrated
40534075

40544076
**Answers where there were none.** `(a + b x)^m (c + d x)^(-3 - m)`, `x^(n - 4)/(a + b x)^n` and

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

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13692,6 +13692,279 @@ static Entity WithWholePowersOfTheSecond(List<(EInteger Power, Entity Coefficien
1369213692
return Integration.ComputeAsTheSameQuestion(rewritten.InnerSimplified, x, integrateByParts);
1369313693
}
1369413694

13695+
/// <summary>
13696+
/// Cube roots of two linears whose powers add up to a whole number, beside a whole power
13697+
/// of a third linear, <c>(a + b x)^(p/3) (c + d x)^(r/3) (g + h x)^n</c> with <c>p + r</c>
13698+
/// a multiple of 3, as the rational function the quotient of the two roots makes of it,
13699+
/// <c>t = (a + b x)^(1/3)/(c + d x)^(1/3)</c>, with its compound coefficients named.
13700+
/// </summary>
13701+
/// <remarks>
13702+
/// <para>
13703+
/// With <c>D = a d - b c</c>, <c>W = b - d t^3</c> and <c>s = (p + r)/3</c>: <c>x</c> is
13704+
/// <c>(c t^3 - a)/W</c>, <c>c + d x = -D/W</c>, <c>g + h x = N/W</c> for
13705+
/// <c>N = (b g - a h) + (c h - d g) t^3</c>, and <c>dx = -3 D t^2 dt/W^2</c>; and the two
13706+
/// roots are <c>t^p (c + d x)^s</c>. So the integrand is
13707+
/// <c>3 (-D)^(s + 1) t^(p + 2) N^n / W^(s + n + 2)</c>, rational in <c>t</c>, and exactly so:
13708+
/// a whole power of the quotient of the principal roots is the quotient of their powers
13709+
/// for every complex <c>x</c>, as <see cref="SolveByAQuotientOfTwoLinearRadicals"/> says.
13710+
/// </para>
13711+
/// <para>
13712+
/// That rule makes the same substitution and declined these, past the budget: its
13713+
/// rational function is written as the substitution leaves it, with
13714+
/// <c>k (b - d t^3) + f (c t^3 - a)</c> where <c>N</c> is, and the rational rules ran out
13715+
/// of time on <c>t^3/((b k - a f + (c f - d k) t^3) (b - d t^3)^2)</c> where
13716+
/// <c>t^3/((p + q t^3) (b - d t^3)^2)</c> is answered in a quarter of a second. Here
13717+
/// <c>N</c> and <c>W</c> are written as binomials in <c>t^3</c> from the coefficients, and a
13718+
/// coefficient that is a compound of the symbols is named by a symbol of its own and
13719+
/// written back into the answer. Naming loses no relation the decomposition needs:
13720+
/// <c>W</c> and <c>N</c> have the resultant <c>h D</c>, zero only where the third linear
13721+
/// is constant or the two under the roots are one, and those, and a third linear that is
13722+
/// a multiple of one of the two, are declined, each decided as a value.
13723+
/// </para>
13724+
/// <para>
13725+
/// Only cube roots, and only a third linear below the bar where the denominator in
13726+
/// <c>t</c> is not squarefree, <c>n &lt;= -2</c> or <c>W^2</c> beside <c>N</c>: Rubi's
13727+
/// <c>(a + b x)^m (c + d x)^n (e + f x)^p</c> rows with <c>m</c>, <c>n</c> thirds. Of the
13728+
/// rest, the third linear above the bar or below it once beside <c>W</c> at most once, the
13729+
/// substitution rule answers most, and they are left to it as they are.
13730+
/// https://github.com/asc-community/AngouriMath/issues/233
13731+
/// </para>
13732+
/// </remarks>
13733+
internal static Entity? SolveCubeRootsOfTwoLinearsBesideAWholePowerOfAThird(Entity expr, Entity.Variable x, bool integrateByParts)
13734+
{
13735+
Entity constant = Number.Integer.One;
13736+
var powers = new List<(Entity Base, ERational Exponent)>();
13737+
foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
13738+
{
13739+
if (!factor.ContainsNode(x))
13740+
{
13741+
constant = underneath ? constant / factor : constant * factor;
13742+
continue;
13743+
}
13744+
var (@base, exponent) = factor switch
13745+
{
13746+
Powf(var powerBase, Number.Rational power) => (powerBase, power.ERational),
13747+
Powf => (factor, (ERational?)null),
13748+
_ => (factor, ERational.One),
13749+
};
13750+
if (exponent is null)
13751+
return null;
13752+
if (underneath)
13753+
exponent = exponent.Negate();
13754+
var index = powers.FindIndex(pair => pair.Base == @base);
13755+
if (index >= 0)
13756+
powers[index] = (@base, powers[index].Exponent.Add(exponent));
13757+
else
13758+
powers.Add((@base, exponent));
13759+
}
13760+
powers.RemoveAll(pair => pair.Exponent.IsZero);
13761+
var roots = powers.Where(pair => !pair.Exponent.IsInteger()).ToList();
13762+
var whole = powers.Where(pair => pair.Exponent.IsInteger()).ToList();
13763+
if (roots.Count != 2 || whole.Count != 1)
13764+
return null;
13765+
var three = ERational.FromInt32(3);
13766+
var thirds = roots.Select(pair => pair.Exponent.Multiply(three)).ToList();
13767+
if (thirds.Any(third => !third.IsInteger()))
13768+
return null;
13769+
var read = new[] { thirds[0].ToEInteger(), thirds[1].ToEInteger(), whole[0].Exponent.ToEInteger() };
13770+
if (read.Any(power => !power.CanFitInInt32()))
13771+
return null;
13772+
var (p, r, n) = (read[0].ToInt32Checked(), read[1].ToInt32Checked(), read[2].ToInt32Checked());
13773+
if ((p + r) % 3 != 0 || System.Math.Abs(p) > MaximumCubeRootNumerator || System.Math.Abs(r) > MaximumCubeRootNumerator
13774+
|| System.Math.Abs(n) > MaximumThirdLinearPower)
13775+
return null;
13776+
var s = (p + r) / 3;
13777+
// Only the third linear below the bar with a repeated factor below it, N^2 or W^2 beside
13778+
// N: there the substitution rule ran out of time. Above, or once below beside W at most
13779+
// once, it answers most of them, and the question is left to it.
13780+
if (n >= 0 || n == -1 && s + n + 2 < 2)
13781+
return null;
13782+
var (first, second, third) = (roots[0].Base, roots[1].Base, whole[0].Base);
13783+
if (!TreeAnalyzer.TryGetPolyLinear(first, x, out var b, out var a)
13784+
|| !TreeAnalyzer.TryGetPolyLinear(second, x, out var d, out var c)
13785+
|| !TreeAnalyzer.TryGetPolyLinear(third, x, out var h, out var g)
13786+
|| VanishesIdentically(b) || VanishesIdentically(d) || VanishesIdentically(h))
13787+
return null;
13788+
var determinant = a * d - b * c;
13789+
var (free, slope) = (b * g - a * h, c * h - d * g);
13790+
if (VanishesIdentically(determinant) || VanishesIdentically(free) || VanishesIdentically(slope))
13791+
return null;
13792+
13793+
var t = Variable.CreateUnique(expr, "t_cube");
13794+
var named = new List<(Variable Name, Entity Value)>();
13795+
Entity Named(Entity value)
13796+
{
13797+
if (value is Number or Variable || value is Mulf(Number, Variable) || value is Powf(Variable, Number))
13798+
return value;
13799+
var name = Variable.CreateUnique(expr + t + named.Aggregate((Entity)Number.Integer.Zero, (sum, pair) => sum + pair.Name), "k_named");
13800+
named.Add((name, value));
13801+
return name;
13802+
}
13803+
var cube = MathS.Pow(t, 3);
13804+
var w = Named(Reduced(b)) + Named(Reduced(-d)) * cube;
13805+
var nOfT = Named(Reduced(free)) + Named(Reduced(slope)) * cube;
13806+
// With nothing to name the rational function is the substitution rule's, which answers it.
13807+
if (named.Count == 0)
13808+
return null;
13809+
// One quotient, the powers whole on both sides, as the rational rules read it.
13810+
Entity above = Number.Integer.One, below = Number.Integer.One;
13811+
foreach (var (factor, power) in new[] { ((Entity)t, p + 2), (w, -(s + n + 2)), (nOfT, n) })
13812+
{
13813+
if (power > 0)
13814+
above *= power == 1 ? factor : MathS.Pow(factor, power);
13815+
else if (power < 0)
13816+
below *= power == -1 ? factor : MathS.Pow(factor, -power);
13817+
}
13818+
var inT = (above / below).InnerSimplified;
13819+
if (Integration.ComputeAsAQuestionOfItsOwn(inT, t, integrateByParts) is not { } answer
13820+
|| answer.Nodes.Any(node => node == MathS.NaN))
13821+
return null;
13822+
foreach (var (name, value) in named)
13823+
answer = answer.Substitute(name, value);
13824+
var root = Number.Rational.Create(1, 3);
13825+
return Reduced(3 * constant * MathS.Pow(-determinant, s + 1)) * answer.Substitute(t, MathS.Pow(first, root) / MathS.Pow(second, root));
13826+
13827+
static Entity Reduced(Entity constant)
13828+
=> constant.Vars.Any() ? Functions.PartialFractions.InLowestTermsOverTheSymbols(constant) : constant.InnerSimplified;
13829+
}
13830+
13831+
/// <summary>
13832+
/// The largest numerator of a third <see cref="SolveCubeRootsOfTwoLinearsBesideAWholePowerOfAThird"/> takes.
13833+
/// </summary>
13834+
private const int MaximumCubeRootNumerator = 12;
13835+
13836+
/// <summary>
13837+
/// The largest power of the third linear <see cref="SolveCubeRootsOfTwoLinearsBesideAWholePowerOfAThird"/> takes.
13838+
/// </summary>
13839+
private const int MaximumThirdLinearPower = 8;
13840+
13841+
/// <summary>
13842+
/// A polynomial times an odd half power of a linear over a whole power of a quadratic with
13843+
/// a root at zero, <c>P(x) (d + e x)^(j/2)/(b x + c x^2)^m</c> with <c>j</c> odd and
13844+
/// positive and <c>m &gt;= 3</c>, under <c>t = sqrt(d + e x)</c> with the quadratic written
13845+
/// as its two linears, <c>x (b + c x)</c>.
13846+
/// </summary>
13847+
/// <remarks>
13848+
/// <para>
13849+
/// With <c>x = (t^2 - d)/e</c>, <c>b + c x = (c t^2 + r)/e</c> for <c>r = b e - c d</c>, and
13850+
/// <c>dx = 2 t dt/e</c>, the integrand is
13851+
/// <c>2 e^(2m - 1) P((t^2 - d)/e) t^(j + 1)/((t^2 - d)^m (c t^2 + r)^m)</c>, and exactly so:
13852+
/// <c>t^j</c> is <c>(d + e x)^(j/2)</c> for the principal root and every complex <c>x</c>.
13853+
/// The two factors below the bar have the resultant <c>b e</c>, and a compound coefficient
13854+
/// is named by a symbol of its own and written back into the answer, as in
13855+
/// <see cref="SolveCubeRootsOfTwoLinearsBesideAWholePowerOfAThird"/>.
13856+
/// </para>
13857+
/// <para>
13858+
/// Rubi's <c>(A + B x) (d + e x)^(j/2)/(b x + c x^2)^3</c> ran past the budget: the rational
13859+
/// function in <c>t</c> is answered in a second, and the substitution that reaches it is
13860+
/// late in the chain and multiplies the quadratic out. Only the root above the bar and a
13861+
/// third power or higher, with a symbol to name: the root below the bar, the second power,
13862+
/// and numbers for coefficients are answered as they are, and left to that. Declined where
13863+
/// <c>d</c> or <c>r</c> is zero as a value, the linear then sharing a root with the quadratic.
13864+
/// https://github.com/asc-community/AngouriMath/issues/233
13865+
/// </para>
13866+
/// </remarks>
13867+
internal static Entity? SolveAnOddHalfPowerOfALinearOverAPowerOfAQuadraticWithARootAtZero(Entity expr, Entity.Variable x, bool integrateByParts)
13868+
{
13869+
Entity constant = Number.Integer.One;
13870+
Entity polynomial = Number.Integer.One;
13871+
(Entity Base, ERational Exponent)? root = null;
13872+
(Entity Base, int Power)? quadratic = null;
13873+
foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
13874+
{
13875+
if (!factor.ContainsNode(x))
13876+
constant = underneath ? constant / factor : constant * factor;
13877+
else if (factor is Powf(var radicand, Number.Rational power) && power is not Number.Integer)
13878+
{
13879+
if (root is not null || underneath)
13880+
return null;
13881+
root = (radicand, power.ERational);
13882+
}
13883+
else if (underneath)
13884+
{
13885+
if (quadratic is not null || factor is not Powf(var @base, Number.Integer { EInteger: var m }) || !m.CanFitInInt32())
13886+
return null;
13887+
quadratic = (@base, m.ToInt32Checked());
13888+
}
13889+
else
13890+
polynomial *= factor;
13891+
}
13892+
if (root is not var (linear, half) || quadratic is not var (q, n) || half.Sign <= 0
13893+
|| half.Multiply(ERational.FromInt32(2)) is not { } twice || !twice.IsInteger() || !twice.ToEInteger().CanFitInInt32()
13894+
|| n < 3 || n > MaximumQuadraticPowerUnderARoot)
13895+
return null;
13896+
var j = twice.ToEInteger().ToInt32Checked();
13897+
if (j > MaximumHalfPowerOfTheLinear
13898+
|| !TreeAnalyzer.TryGetPolyLinear(linear, x, out var e, out var d)
13899+
|| !TreeAnalyzer.TryGetPolyQuadratic(q, x, out var c, out var b, out var free)
13900+
|| !TreeAnalyzer.TryGetPolynomial(polynomial, x, out var read)
13901+
|| read.Any(pair => pair.Key.Sign < 0 || pair.Value.ContainsNode(x)))
13902+
return null;
13903+
var degree = read.Keys.Max()!.ToInt32Checked();
13904+
var r = b * e - c * d;
13905+
if (degree > MaximumQuadraticPowerUnderARoot || !VanishesIdentically(free) || VanishesIdentically(b) || VanishesIdentically(c)
13906+
|| VanishesIdentically(e) || VanishesIdentically(d) || VanishesIdentically(r))
13907+
return null;
13908+
13909+
var t = Variable.CreateUnique(expr, "t_root");
13910+
var named = new List<(Variable Name, Entity Value)>();
13911+
Entity Named(Entity value)
13912+
{
13913+
if (value is Number or Variable || value is Mulf(Number, Variable) || value is Powf(Variable, Number))
13914+
return value;
13915+
var name = Variable.CreateUnique(expr + t + named.Aggregate((Entity)Number.Integer.Zero, (sum, pair) => sum + pair.Name), "k_named");
13916+
named.Add((name, value));
13917+
return name;
13918+
}
13919+
// P((t^2 - d)/e) e^degree as a polynomial in t^2: the coefficient of t^(2l) is the sum
13920+
// over i of p_i e^(degree - i) binomial(i, l) (-d)^(i - l).
13921+
Entity above = Number.Integer.Zero;
13922+
for (var l = 0; l <= degree; l++)
13923+
{
13924+
Entity coefficient = Number.Integer.Zero;
13925+
foreach (var (i, p) in read.Select(pair => (pair.Key.ToInt32Checked(), pair.Value)).Where(pair => pair.Item1 >= l))
13926+
coefficient += p * MathS.Pow(e, degree - i) * Binomial(i, l) * MathS.Pow(-d, i - l);
13927+
// t^0 is written as nothing: InnerSimplified makes it 1 provided t is not 0.
13928+
if (!VanishesIdentically(coefficient))
13929+
above += l == 0 ? Named(Reduced(coefficient)) : Named(Reduced(coefficient)) * MathS.Pow(t, 2 * l);
13930+
}
13931+
var square = MathS.Sqr(t);
13932+
var below = MathS.Pow(square + Named(Reduced(-d)), n) * MathS.Pow(Named(Reduced(c)) * square + Named(Reduced(r)), n);
13933+
// With nothing to name the coefficients are numbers, and the rules after this answer them.
13934+
if (named.Count == 0)
13935+
return null;
13936+
var inT = (MathS.Pow(t, j + 1) * above / below).InnerSimplified;
13937+
if (Integration.ComputeAsAQuestionOfItsOwn(inT, t, integrateByParts) is not { } answer
13938+
|| answer.Nodes.Any(node => node == MathS.NaN))
13939+
return null;
13940+
foreach (var (name, value) in named)
13941+
answer = answer.Substitute(name, value);
13942+
return Reduced(2 * constant * MathS.Pow(e, 2 * n - 1 - degree)) * answer.Substitute(t, MathS.Sqrt(linear));
13943+
13944+
static Entity Reduced(Entity constant)
13945+
=> constant.Vars.Any() ? Functions.PartialFractions.InLowestTermsOverTheSymbols(constant) : constant.InnerSimplified;
13946+
13947+
static Entity Binomial(int i, int l)
13948+
{
13949+
var value = EInteger.One;
13950+
for (var k = 0; k < l; k++)
13951+
value = value.Multiply(i - k).Divide(k + 1);
13952+
return Number.Integer.Create(value);
13953+
}
13954+
}
13955+
13956+
/// <summary>
13957+
/// The largest power of the quadratic, and degree of the polynomial,
13958+
/// <see cref="SolveAnOddHalfPowerOfALinearOverAPowerOfAQuadraticWithARootAtZero"/> takes.
13959+
/// </summary>
13960+
private const int MaximumQuadraticPowerUnderARoot = 8;
13961+
13962+
/// <summary>
13963+
/// The largest odd <c>j</c> of <c>(d + e x)^(j/2)</c>
13964+
/// <see cref="SolveAnOddHalfPowerOfALinearOverAPowerOfAQuadraticWithARootAtZero"/> takes.
13965+
/// </summary>
13966+
private const int MaximumHalfPowerOfTheLinear = 15;
13967+
1369513968
/// <summary>
1369613969
/// A polynomial over a power of a linear beside the square roots of two others,
1369713970
/// <c>P(x) (a + b x)^(m/2) (c + d x)^(n/2) / (g + h x)^k</c> with <c>m</c> and <c>n</c>

0 commit comments

Comments
 (0)