@@ -2485,59 +2485,214 @@ over is Entity.Powf(var @base, var power) ?
24852485 if (Functions.PartialFractions.IsZeroAsAValue(discriminant))
24862486 return null;
24872487
2488- // The numerator in w = x^2, divided down by w^k (c w^2 + b w + a) to a remainder of a
2489- // degree below k + 2.
2488+ // The numerator in w = x^2 over w^k (c w^2 + b w + a).
24902489 var degree = above.Keys.Max() / 2;
2491- var inW = new Entity[System.Math.Max( degree, k + 1) + 1];
2492- for (var j = 0; j < inW.Length ; j++)
2490+ var inW = new Entity[degree + 1];
2491+ for (var j = 0; j <= degree ; j++)
24932492 inW[j] = above.TryGetValue(2 * j, out var at) ? at : Number.Integer.Zero;
2493+ var (polynomial, pole, d, e) = SplitOverAPowerAndAQuadratic(inW, k, a, b, c);
2494+ Entity polynomialPart = Number.Integer.Zero;
2495+ for (var j = polynomial.Length - 1; j >= 0; j--)
2496+ if (polynomial[j] != Number.Integer.Zero)
2497+ polynomialPart += polynomial[j] * MathS.Pow(x, 2 * j);
2498+
2499+ Entity total = Number.Integer.Zero;
2500+ if (polynomialPart != Number.Integer.Zero)
2501+ {
2502+ if (Integration.ComputeIndefiniteIntegral(polynomialPart, x, integrateByParts) is not { } whole)
2503+ return null;
2504+ total += whole;
2505+ }
2506+ // r_m x^(2m - 2k), whose exponent is odd and so never minus one.
2507+ for (var m = 0; m < k; m++)
2508+ {
2509+ if (pole[m] == Number.Integer.Zero)
2510+ continue;
2511+ var exponent = 2 * (m - k) + 1;
2512+ total += pole[m] * MathS.Pow(x, exponent) / exponent;
2513+ }
2514+ return ArctangentsOverABiquadratic(total, d, e, a, b, c, discriminant, x);
2515+ }
2516+
2517+ /// <summary>
2518+ /// A polynomial in <c>x^2</c> over a power of a linear in <c>x^2</c> and a biquadratic, with a
2519+ /// symbol in it: <c>N(x^2)/(L(x^2)^k Q(x^2))</c>, <c>L(w) = l (w - r)</c>, split in
2520+ /// <c>s = x^2 - r</c>. Written in powers of <c>s</c>, the quotient is the one
2521+ /// <see cref="SolveAnEvenPolynomialOverASymbolicBiquadratic"/> splits over <c>w^k Q</c>, and the
2522+ /// terms in <c>s^(-j)</c> go by the reduction
2523+ /// <c>int dx/(x^2 + p)^j = x/(2 p (j - 1) (x^2 + p)^(j - 1)) + (2 j - 3)/(2 p (j - 1)) int dx/(x^2 + p)^(j - 1)</c>,
2524+ /// <c>p = -r</c>, down to <c>atan(x/sqrt(p))/sqrt(p)</c>, which holds for every complex
2525+ /// <c>p</c> but zero, as the biquadratic's own terms do.
2526+ /// </summary>
2527+ /// <remarks>
2528+ /// Under <c>t = sqrt(c + d u)</c> Rubi's <c>sqrt(c + d u)/((a + b u)^n (1 + u^2))</c>, which the
2529+ /// tangent substitution makes of <c>sqrt(c + d tan(x))/(a + b tan(x))^n</c>, is
2530+ /// <c>t^2/((a d + b (t^2 - c))^n ((t^2 - c)^2 + d^2))</c> up to a constant. Split in <c>t</c>, the
2531+ /// sum of two squares went over its conjugates, and the answer to
2532+ /// <c>sqrt(c + d tan(x)) (A + B tan(x) + C tan(x)^2)/(a + b tan(x))^3</c> ran to 3.5 million
2533+ /// characters. Only one linear in <c>x^2</c>, to a power, beside one biquadratic: with
2534+ /// <c>r</c> zero it is <see cref="SolveAnEvenPolynomialOverASymbolicBiquadratic"/>'s, and a root
2535+ /// <c>r</c> shared with the biquadratic declines.
2536+ /// https://github.com/asc-community/AngouriMath/issues/718
2537+ /// </remarks>
2538+ internal static Entity? SolveAnEvenQuotientOverAPowerOfALinearInTheSquareAndABiquadratic(Entity expr, Entity.Variable x, bool integrateByParts)
2539+ {
2540+ if (!TryReadAsQuotient(expr, out var numerator, out var denominator) || !denominator.Vars.Any(v => v != x))
2541+ return null;
2542+ Entity constant = Number.Integer.One;
2543+ (Entity Zeroth, Entity First, int Power)? linear = null;
2544+ (Entity A, Entity B, Entity C)? quadratic = null;
2545+ foreach (var factor in Mulf.LinearChildren(denominator))
2546+ {
2547+ var (@base, power) = factor is Powf(var raised, Number.Integer { EInteger: var n }) && n.Sign > 0 && n.CanFitInInt32()
2548+ ? (raised, n.ToInt32Unchecked()) : (factor, 1);
2549+ if (!@base.ContainsNode(x))
2550+ {
2551+ constant *= factor;
2552+ continue;
2553+ }
2554+ if (!TreeAnalyzer.TryGetPolynomial(@base, x, out var read) || read.Count == 0
2555+ || read.Values.Any(coefficient => coefficient.ContainsNode(x))
2556+ || read.Keys.Any(p => p.Sign < 0 || !p.IsEven || !p.CanFitInInt32()))
2557+ return null;
2558+ Entity At(int p) => read.TryGetValue(EInteger.FromInt32(p), out var value) ? value : Number.Integer.Zero;
2559+ var top = read.Keys.Max()!.ToInt32Unchecked();
2560+ if (top == 2 && linear is null)
2561+ linear = (At(0), At(2), power);
2562+ else if (top == 4 && quadratic is null && power == 1)
2563+ quadratic = (At(0), At(2), At(4));
2564+ else
2565+ return null;
2566+ }
2567+ if (linear is not { } l || quadratic is not { } quartic)
2568+ return null;
2569+ if (!TreeAnalyzer.TryGetPolynomial(numerator, x, out var above) || above.Count == 0
2570+ || above.Values.Any(coefficient => coefficient.ContainsNode(x))
2571+ || above.Keys.Any(p => p.Sign < 0 || !p.IsEven || !p.CanFitInInt32()))
2572+ return null;
2573+ var (a, b, c) = quartic;
2574+ if (Functions.PartialFractions.IsZeroAsAValue(a) || Functions.PartialFractions.IsZeroAsAValue(c)
2575+ || Functions.PartialFractions.IsZeroAsAValue(l.First))
2576+ return null;
2577+ var r = Functions.PartialFractions.InLowestTermsOverTheSymbols(-l.Zeroth / l.First);
2578+ if (Functions.PartialFractions.IsZeroAsAValue(r))
2579+ return null;
2580+ var discriminant = Functions.PartialFractions.Bare((b * b - 4 * a * c).Simplify());
2581+ if (Functions.PartialFractions.IsZeroAsAValue(discriminant))
2582+ return null;
2583+ // The biquadratic in s: Q(r + s) = Q(r) + Q'(r) s + c s^2, and Q(r) is not zero, or the
2584+ // linear's root is one of the biquadratic's.
2585+ var atRoot = Functions.PartialFractions.InLowestTermsOverTheSymbols(a + b * r + c * r * r);
2586+ if (Functions.PartialFractions.IsZeroAsAValue(atRoot))
2587+ return null;
2588+ var slopeAtRoot = Functions.PartialFractions.InLowestTermsOverTheSymbols(b + 2 * c * r);
2589+
2590+ // N(r + s) in powers of s.
2591+ var degree = above.Keys.Max()!.ToInt32Unchecked() / 2;
2592+ var inW = new Entity[degree + 1];
2593+ for (var j = 0; j <= degree; j++)
2594+ inW[j] = above.TryGetValue(EInteger.FromInt32(2 * j), out var at) ? at : Number.Integer.Zero;
2595+ var inS = new Entity[degree + 1];
2596+ for (var m = 0; m <= degree; m++)
2597+ {
2598+ Entity sum = Number.Integer.Zero;
2599+ EInteger binomial = EInteger.One;
2600+ for (var j = m; j <= degree; j++)
2601+ {
2602+ if (j > m)
2603+ binomial = binomial * j / (j - m);
2604+ if (inW[j] != Number.Integer.Zero)
2605+ sum += inW[j] * Number.Integer.Create(binomial) * MathS.Pow(r, Number.Integer.Create(j - m));
2606+ }
2607+ inS[m] = Functions.PartialFractions.InLowestTermsOverTheSymbols(sum);
2608+ }
2609+ var k = l.Power;
2610+ var (polynomial, pole, dInS, eInS) = SplitOverAPowerAndAQuadratic(inS, k, atRoot, slopeAtRoot, c);
2611+ var square = MathS.Pow(x, 2) - r;
2612+
2613+ Entity total = Number.Integer.Zero;
24942614 Entity polynomialPart = Number.Integer.Zero;
2615+ for (var j = 0; j < polynomial.Length; j++)
2616+ if (polynomial[j] != Number.Integer.Zero)
2617+ polynomialPart += polynomial[j] * MathS.Pow(square, j);
2618+ if (polynomialPart != Number.Integer.Zero)
2619+ {
2620+ if (Integration.ComputeIndefiniteIntegral(polynomialPart.Expand(), x, integrateByParts) is not { } whole)
2621+ return null;
2622+ total += whole;
2623+ }
2624+ // pole[m] s^(m - k) is a term in 1/(x^2 - r)^j with j = k - m, by the reduction.
2625+ var p = -r;
2626+ var root = MathS.Sqrt(p);
2627+ Entity reduced = MathS.Arctan(x / root) / root;
2628+ var byPower = new Entity[k + 1];
2629+ byPower[1] = reduced;
2630+ for (var j = 2; j <= k; j++)
2631+ byPower[j] = x / (2 * p * (j - 1) * MathS.Pow(square, j - 1)) + Number.Integer.Create(2 * j - 3) / (2 * p * (j - 1)) * byPower[j - 1];
2632+ for (var m = 0; m < k; m++)
2633+ if (pole[m] != Number.Integer.Zero)
2634+ total += pole[m] * byPower[k - m];
2635+ // (d + e s)/Q in w = x^2 is (d - e r + e w)/Q(w).
2636+ var d = Functions.PartialFractions.InLowestTermsOverTheSymbols(dInS - eInS * r);
2637+ total = ArctangentsOverABiquadratic(total, d, eInS, a, b, c, discriminant, x);
2638+ return total / (constant * MathS.Pow(l.First, k));
2639+ }
2640+
2641+ /// <summary>
2642+ /// <c>N(s)/(s^k (a + b s + c s^2))</c>, the coefficients of <c>N</c> in <paramref name="above"/>,
2643+ /// as a polynomial in <c>s</c>, the terms in <c>s^(m - k)</c> for <c>m</c> below <c>k</c>, and
2644+ /// <c>(d + e s)/(a + b s + c s^2)</c>: <c>N</c> divided down by <c>s^k</c> times the quadratic first,
2645+ /// then the remainder <c>R</c> over it expanded at <c>s = 0</c> by
2646+ /// <c>r_m = (R_m - b r_(m-1) - c r_(m-2))/a</c>, and <c>d + e s</c> what
2647+ /// <c>(R - Q sum r_m s^m)/s^k</c> leaves, every lower coefficient cancelling by the recurrence.
2648+ /// </summary>
2649+ private static (Entity[] Polynomial, Entity[] Pole, Entity D, Entity E) SplitOverAPowerAndAQuadratic(
2650+ Entity[] above, int k, Entity a, Entity b, Entity c)
2651+ {
2652+ var degree = above.Length - 1;
2653+ var inS = new Entity[System.Math.Max(degree, k + 1) + 1];
2654+ for (var j = 0; j < inS.Length; j++)
2655+ inS[j] = j <= degree ? above[j] : Number.Integer.Zero;
2656+ var polynomial = new Entity[System.Math.Max(degree - k - 1, 0)];
2657+ for (var j = 0; j < polynomial.Length; j++)
2658+ polynomial[j] = Number.Integer.Zero;
24952659 for (var j = degree; j >= k + 2; j--)
24962660 {
2497- var lead = Functions.PartialFractions.InLowestTermsOverTheSymbols(inW [j] / c);
2661+ var lead = Functions.PartialFractions.InLowestTermsOverTheSymbols(inS [j] / c);
24982662 if (lead == Number.Integer.Zero)
24992663 continue;
2500- polynomialPart += lead * MathS.Pow(x, 2 * ( j - k - 2)) ;
2501- inW [j - 1] = Functions.PartialFractions.InLowestTermsOverTheSymbols(inW [j - 1] - lead * b);
2502- inW [j - 2] = Functions.PartialFractions.InLowestTermsOverTheSymbols(inW [j - 2] - lead * a);
2664+ polynomial[ j - k - 2] = lead ;
2665+ inS [j - 1] = Functions.PartialFractions.InLowestTermsOverTheSymbols(inS [j - 1] - lead * b);
2666+ inS [j - 2] = Functions.PartialFractions.InLowestTermsOverTheSymbols(inS [j - 2] - lead * a);
25032667 }
2504- // The remainder R over w^k Q is sum r_m w^(m - k) over m < k, the expansion of R/Q at
2505- // w = 0, plus (d + e w)/Q: r_m = (R_m - b r_(m-1) - c r_(m-2))/a, and d + e w is what
2506- // (R - Q sum r_m w^m)/w^k leaves, every lower coefficient cancelling by the recurrence.
25072668 var pole = new Entity[k];
25082669 for (var m = 0; m < k; m++)
25092670 {
2510- var term = inW [m];
2671+ var term = inS [m];
25112672 if (m >= 1) term -= b * pole[m - 1];
25122673 if (m >= 2) term -= c * pole[m - 2];
25132674 pole[m] = Functions.PartialFractions.InLowestTermsOverTheSymbols(term / a);
25142675 }
2515- var dTerm = inW[k];
2676+ if (k == 0)
2677+ return (polynomial, pole, inS[0], inS[1]);
2678+ var dTerm = inS[k];
25162679 if (k >= 1) dTerm -= b * pole[k - 1];
25172680 if (k >= 2) dTerm -= c * pole[k - 2];
2518- var eTerm = inW [k + 1];
2681+ var eTerm = inS [k + 1];
25192682 if (k >= 1) eTerm -= c * pole[k - 1];
2520- var d = k == 0 ? inW[0] : Functions.PartialFractions.InLowestTermsOverTheSymbols(dTerm);
2521- var e = k == 0 ? (degree >= 1 ? inW[1] : Number.Integer.Zero) : Functions.PartialFractions.InLowestTermsOverTheSymbols(eTerm);
2683+ return (polynomial, pole, Functions.PartialFractions.InLowestTermsOverTheSymbols(dTerm),
2684+ Functions.PartialFractions.InLowestTermsOverTheSymbols(eTerm));
2685+ }
25222686
2687+ /// <summary>
2688+ /// <paramref name="total"/> plus the antiderivative of <c>(d + e x^2)/(a + b x^2 + c x^4)</c> by
2689+ /// the two roots in <c>x^2</c>, <paramref name="discriminant"/> being <c>b^2 - 4 a c</c> and not zero.
2690+ /// </summary>
2691+ private static Entity ArctangentsOverABiquadratic(Entity total, Entity d, Entity e, Entity a, Entity b, Entity c, Entity discriminant, Entity.Variable x)
2692+ {
25232693 var q = MathS.Sqrt(discriminant);
25242694 var firstRoot = (-b + q) / (2 * c);
25252695 var secondRoot = (-b - q) / (2 * c);
2526- Entity total = Number.Integer.Zero;
2527- if (polynomialPart != Number.Integer.Zero)
2528- {
2529- if (Integration.ComputeIndefiniteIntegral(polynomialPart, x, integrateByParts) is not { } whole)
2530- return null;
2531- total += whole;
2532- }
2533- // r_m x^(2m - 2k), whose exponent is odd and so never minus one.
2534- for (var m = 0; m < k; m++)
2535- {
2536- if (pole[m] == Number.Integer.Zero)
2537- continue;
2538- var exponent = 2 * (m - k) + 1;
2539- total += pole[m] * MathS.Pow(x, exponent) / exponent;
2540- }
25412696 // `1/(x^2 - r)` is `atan(x/s)/s` with `s = sqrt(-r)` for every complex `r` but zero:
25422697 // `d/dx atan(x/s)/s` is `1/(s^2 + x^2)` whatever `s` is, and for a positive `r` the
25432698 // arctangent of an imaginary argument is the hyperbolic one, `-atanh(x/sqrt(r))/sqrt(r)`,
0 commit comments