@@ -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 <= -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 >= 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