Skip to content

Commit 1991692

Browse files
A quotient in x^2 over a power of a linear in x^2 and a biquadratic is split in x^2 (#1809)
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 2d3dc6a commit 1991692

4 files changed

Lines changed: 259 additions & 31 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,23 @@ quotient of two such linears the sum is `(b - d t^2)^2 + (c t^2 - a)^2`. Rubi's
336336
| `"(c + d*tan(x))^(3/2)/(a + b*tan(x))^3".ToEntity().Integrate("x")` | `integral(...)` | the same |
337337
| `"1/((a + b*tan(x))^(3/2)*(c + d*tan(x))^(3/2))".ToEntity().Integrate("x")` | `integral(...)`; past a minute on the unreleased master | in the root of the quotient of the two, in a second |
338338

339+
### A quotient in `x^2` over a power of a linear in `x^2` and a biquadratic is split in `x^2`
340+
341+
**Shorter answers, sooner.** `sqrt(c + d tan(x)) (A + B tan(x) + C tan(x)^2)/(a + b tan(x))^3` was
342+
declined in 2.5.0 and answered in 3.3 million characters since; it is answered in seven thousand.
343+
Under `u = tan(x)` and `t = sqrt(c + d u)` it is a polynomial in `t^2` over
344+
`(a d + b (t^2 - c))^3 ((t^2 - c)^2 + d^2)`, which was split in `t`, the sum of two squares over its
345+
conjugates. Such a quotient, one linear in `x^2` to a power beside one biquadratic, is split in
346+
`s = x^2 - r` now, `r` the linear's root: in powers of `s` it is the split over a power of `x^2` and a
347+
biquadratic that the rule above makes, and the terms in `1/(x^2 - r)^j` go by their reduction to
348+
`atan(x/sqrt(-r))/sqrt(-r)` ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
349+
350+
| Input | Was (2.5.0) | Now |
351+
|---|---|---|
352+
| `"sqrt(c + d*tan(x))*(A + B*tan(x) + C*tan(x)^2)/(a + b*tan(x))^3".ToEntity().Integrate("x")` | `integral(...)`; 3,299,915 characters after 24 seconds on the unreleased master | 6,912 characters |
353+
| `"sqrt(c + d*tan(x))/(a + b*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)`; 40,540 characters on the unreleased master | 1,465 characters |
354+
| `"(c + d*tan(x))^(3/2)/(a + b*tan(x))^3".ToEntity().Integrate("x")` | `integral(...)`; 71,293 characters on the unreleased master | 2,786 characters |
355+
339356
### A quotient in `x^2` over a power of `x` and a biquadratic is split in `x^2`
340357

341358
**Shorter answers, sooner.** `cot(x)^(13/2) (a + b tan(x))^(5/2) (A + B tan(x))` was declined in 2.5.0

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

Lines changed: 186 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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)`,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,8 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
10171017
// in x^2 written with the root of the discriminant: partial fractions read a
10181018
// written factor, and `a + b x^2 + c x^4` is written as one.
10191019
if ((answer = IndefiniteIntegralSolver.SolveAnEvenPolynomialOverASymbolicBiquadratic(expr, x, integrateByParts)) is { }) return answer;
1020+
// And over a power of a linear in x^2 beside the biquadratic, split in x^2 minus its root.
1021+
if ((answer = IndefiniteIntegralSolver.SolveAnEvenQuotientOverAPowerOfALinearInTheSquareAndABiquadratic(expr, x, integrateByParts)) is { }) return answer;
10201022
if ((answer = IndefiniteIntegralSolver.SolveByPartialFractions(expr, x, integrateByParts)) is { }) return answer;
10211023
// A whole negative power of a polynomial of several terms among the factors,
10221024
// written below the bar and asked again: the gathering on the way in writes

0 commit comments

Comments
 (0)