@@ -1057,6 +1057,14 @@ is var (multiple, leftover)
10571057 ?? Integration.ComputeIndefiniteIntegral(numerator / overTheConjugates, x, integrateByParts)) is { } overConjugates)
10581058 return overConjugates;
10591059
1060+ // A power of x beside two blocks or more in one power of x, `1/(x^7 (a + b x^3)(c + d x^3))`:
1061+ // split in `u = x^3` over the blocks, each fraction then a power of x over one block.
1062+ // Before the Hermite reduction, which reads the power of x as a repeated factor: what it
1063+ // leaves went to the split at the power of x, whose rest over both blocks had coefficients
1064+ // pages long, and the answer was 265,499 characters.
1065+ if (IntegrateOverBlocksInAPowerOfX(numerator, denominator, x, integrateByParts, besideAPowerOfX: true) is { } besideBlocksInAPower)
1066+ return besideBlocksInAPower;
1067+
10601068 // A denominator with a written repeated factor takes the Hermite reduction first:
10611069 // the rational part of the answer in one linear solve, and what is left is a proper
10621070 // fraction over a squarefree denominator for the splits below. `(1 + x^2)/(x (1 + x^3)^2)`
@@ -1622,19 +1630,27 @@ static Entity Binomial(int n, int k)
16221630 /// <c>n >= 2</c>, linear or quadratic in it, with a symbol among them: split in
16231631 /// <c>u = x^n</c> by <see cref="Functions.PartialFractions.TrySplitOverWrittenFactors"/>,
16241632 /// one residue of the numerator's powers modulo <c>n</c> at a time, each fraction then
1625- /// over one block in x.
1633+ /// over one block in x. With <paramref name="besideAPowerOfX"/>, a power of x beside two
1634+ /// blocks or more as well, and only then.
16261635 /// </summary>
16271636 /// <remarks>
16281637 /// <c>1/((a + b x^3)(c + d x^3))</c> was declined: over the rationals it does not factor,
16291638 /// and the split over written factors reads a block of the third degree as nothing. In
16301639 /// <c>u = x^3</c> it is <c>1/((a + b u)(c + d u))</c>, two linear factors. A numerator
16311640 /// term <c>x^r M(x^n)</c> keeps its <c>x^r</c> outside the split, which is exact since
16321641 /// the blocks are polynomials in <c>x^n</c> alone.
1642+ /// <para>
1643+ /// A power of x beside the blocks stays outside the split: <c>1/(x^7 (a + b x^3)(c + d x^3))</c>
1644+ /// is <c>(b/(x^7 (a + b x^3)) - d/(x^7 (c + d x^3)))/(b c - a d)</c>, a power of x over one
1645+ /// block twice. Split at the power of x first, the rest over both blocks had coefficients
1646+ /// that were pages of symbols, and the answer came to 265,499 characters.
1647+ /// </para>
16331648 /// https://github.com/asc-community/AngouriMath/issues/718
16341649 /// </remarks>
1635- private static Entity? IntegrateOverBlocksInAPowerOfX(Entity numerator, Entity denominator, Entity.Variable x, bool integrateByParts, bool oneBlockAtItsRoots = false)
1650+ private static Entity? IntegrateOverBlocksInAPowerOfX(Entity numerator, Entity denominator, Entity.Variable x, bool integrateByParts, bool oneBlockAtItsRoots = false, bool besideAPowerOfX = false )
16361651 {
16371652 var blocks = new List<(Dictionary<EInteger, Entity> Polynomial, int Power)>();
1653+ var powerOfX = 0;
16381654 Entity constant = Number.Integer.One;
16391655 foreach (var factor in Mulf.LinearChildren(denominator))
16401656 {
@@ -1646,6 +1662,13 @@ static Entity Binomial(int n, int k)
16461662 var (@base, power) = factor is Powf(var b, Number.Integer e) && e.EInteger.Sign > 0 && e.EInteger.CanFitInInt32()
16471663 ? (b, e.EInteger.ToInt32Unchecked())
16481664 : (factor, 1);
1665+ if (@base == x)
1666+ {
1667+ if (!besideAPowerOfX)
1668+ return null;
1669+ powerOfX += power;
1670+ continue;
1671+ }
16491672 if (!TreeAnalyzer.TryGetPolynomial(@base, x, out var polynomial) || polynomial.Count < 2)
16501673 return null;
16511674 foreach (var term in polynomial)
@@ -1660,6 +1683,8 @@ static Entity Binomial(int n, int k)
16601683 }
16611684 if (blocks.Count == 0 || !(numerator + denominator).Vars.Any(symbol => symbol != x))
16621685 return null;
1686+ if (besideAPowerOfX && (powerOfX == 0 || blocks.Count < 2 || powerOfX > MaximumPowerOfXOverABlock))
1687+ return null;
16631688 // n, the greatest common divisor of every power the blocks hold.
16641689 var n = EInteger.Zero;
16651690 foreach (var (polynomial, _) in blocks)
@@ -1678,6 +1703,8 @@ static Entity Binomial(int n, int k)
16781703 foreach (var term in above)
16791704 if (term.Key.Sign < 0 || !term.Key.CanFitInInt32() || term.Value.ContainsNode(x))
16801705 return null;
1706+ if (besideAPowerOfX && above.Values.Any(coefficient => coefficient is not (Number or Variable)))
1707+ return null;
16811708
16821709 var u = Entity.Variable.CreateUnique(numerator + denominator, "u");
16831710 Entity InU(Dictionary<EInteger, Entity> polynomial)
@@ -1735,11 +1762,33 @@ Entity InU(Dictionary<EInteger, Entity> polynomial)
17351762 }
17361763 if (!Functions.PartialFractions.TrySplitOverWrittenFactors(inU, below, u, out var decomposition))
17371764 return null;
1738- var outside = residue.Key == 0 ? (Entity)Number.Integer.One : residue.Key == 1 ? x : MathS.Pow(x, residue.Key);
1739- foreach (var fraction in Sumf.LinearChildren(decomposition))
1765+ // The power of x below stays outside the split, so each fraction is a power of x
1766+ // over one block, which the split at a power of x beside a block reads.
1767+ var outside = (residue.Key - powerOfX) switch
17401768 {
1769+ 0 => (Entity)Number.Integer.One,
1770+ 1 => x,
1771+ var power => MathS.Pow(x, power),
1772+ };
1773+ // The split comes as a sum over a constant. Beside a power of x it is taken term by
1774+ // term: whole, it is the power of x over both blocks again, for the split at that
1775+ // power, whose coefficients run to `a^54 b^6 c^46 d^14`.
1776+ var (terms, scale) = besideAPowerOfX && decomposition is Divf(var sum, var divisor) && !divisor.ContainsNode(u)
1777+ ? (sum, divisor)
1778+ : (decomposition, (Entity)Number.Integer.One);
1779+ foreach (var term in Sumf.LinearChildren(terms))
1780+ {
1781+ if (term == Number.Integer.Zero)
1782+ continue;
1783+ var fraction = scale == Number.Integer.One ? term : term / scale;
17411784 var inX = outside * fraction.Substitute(u, MathS.Pow(x, step));
1742- if (Integration.ComputeIndefiniteIntegral(inX, x, integrateByParts) is not { } integrated)
1785+ // Beside a power of x, each piece is a part of the question asked, over one block
1786+ // fewer, and answered at its level: the split at the power of x that reads it
1787+ // answers only the question asked or one below.
1788+ var integrated = besideAPowerOfX
1789+ ? Integration.ComputeAsTheSameQuestion(inX, x, integrateByParts)
1790+ : Integration.ComputeIndefiniteIntegral(inX, x, integrateByParts);
1791+ if (integrated is null)
17431792 return null;
17441793 total += integrated;
17451794 }
0 commit comments