Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ Entity PolynomialFromCoefficients(Entity[] coefficients)
/// <paramref name="polynomial"/> in <paramref name="x"/> at <paramref name="root"/>,
/// each in lowest terms over the symbols; null where it does not read as one.
/// </summary>
private static Entity[]? TaylorCoefficientsAtTheRoot(Entity polynomial, Entity root, int count, Variable x)
internal static Entity[]? TaylorCoefficientsAtTheRoot(Entity polynomial, Entity root, int count, Variable x)
{
var offset = Variable.CreateUnique(polynomial + root, "t");
var shifted = WithWholePowersOfPowersMultiplied(Bare(polynomial.Substitute(x, root + offset).Expand().InnerSimplified));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,108 @@ over is Entity.Powf(var @base, var power) ?
_ => null
};

/// <summary>
/// A polynomial numerator over a denominator with a power of a linear in it beside a
/// factor that is not a polynomial -- <c>P(x)/((a + b x)^k S(x))</c> -- with the
/// polynomial written in powers of the linear at its root: what the power divides goes
/// over <c>S</c> alone, and each lower power over its own power of the linear.
/// </summary>
/// <remarks>
/// <para>
/// <c>P(x) = p_0 + p_1 (a + b x) + ... + (a + b x)^k Q(x)</c> is a polynomial identity,
/// its coefficients the Taylor coefficients of <c>P</c> at <c>-a/b</c> over the powers
/// of <c>b</c>, so the integrand is <c>Q/S + sum p_j /((a + b x)^(k-j) S)</c> exactly and
/// everywhere. <c>(A + B x + C x^2 + D x^3)/((a + b x) sqrt(c + d x))</c> was answered
/// through the substitution <c>u = sqrt(c + d x)</c> term by term, a cubic in
/// <c>u^2 - c</c> over a symbolic quadratic in <c>u</c> each time, in a hundred kilobytes
/// of piecewise that did not evaluate within the corpus's budget; reduced, it is a
/// quadratic over the root, three powers, and one <c>p_0/((a + b x) sqrt(c + d x))</c>.
/// </para>
/// <para>
/// Only beside a factor that is not a polynomial in <c>x</c>: a quotient of polynomials
/// is the partial fractions' and they do this and more.
/// </para>
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveByReducingThePolynomialOverALinearFactor(Entity expr, Entity.Variable x, bool integrateByParts)
{
if (!TryReadAsQuotient(expr, out var numerator, out var denominator))
return null;
if (!TreeAnalyzer.TryGetPolynomial(numerator, x, out var polynomial) || polynomial.Count == 0
|| polynomial.Keys.Any(power => power.Sign < 0) || polynomial.Values.Any(coefficient => coefficient.ContainsNode(x)))
return null;
var degree = polynomial.Keys.Max()!;
if (!degree.CanFitInInt32() || degree.ToInt32Unchecked() < 1)
return null;

Entity? linear = null;
var multiplicity = 0;
Entity rest = Number.Integer.One;
var somethingElse = false;
foreach (var factor in Mulf.LinearChildren(denominator))
{
if (!factor.ContainsNode(x))
{
rest *= factor;
continue;
}
var (@base, power) = factor is Powf(var repeated, Number.Integer { EInteger.Sign: > 0 } times) && times.EInteger.CanFitInInt32()
? (repeated, times.EInteger.ToInt32Unchecked()) : (factor, 1);
if (linear is null && TreeAnalyzer.TryGetPolynomial(@base, x, out var read) && read.Count > 0
&& read.Keys.Max()!.Equals(EInteger.One) && read.Values.All(coefficient => !coefficient.ContainsNode(x)))
{
linear = @base;
multiplicity = power;
continue;
}
if (!TreeAnalyzer.TryGetPolynomial(factor, x, out _))
somethingElse = true;
rest *= factor;
}
if (linear is null || !somethingElse)
return null;

if (!TreeAnalyzer.TryGetPolynomial(linear, x, out var line))
return null;
var beta = line[EInteger.One];
var alpha = line.TryGetValue(EInteger.Zero, out var constantTerm) ? constantTerm : Number.Integer.Zero;
var root = Functions.PartialFractions.Bare((-alpha / beta).InnerSimplified);
var count = degree.ToInt32Unchecked() + 1;
if (Functions.PartialFractions.TaylorCoefficientsAtTheRoot(numerator, root, count, x) is not { } coefficients)
return null;

Entity total = Number.Integer.Zero;
for (var order = 0; order < multiplicity && order < count; order++)
{
if (coefficients[order] == Number.Integer.Zero)
continue;
var over = multiplicity - order;
var coefficient = Functions.PartialFractions.InLowestTermsOverTheSymbols(coefficients[order] / MathS.Pow(beta, order));
var piece = coefficient / ((over == 1 ? linear : MathS.Pow(linear, over)) * rest);
if (Integration.ComputeIndefiniteIntegral(piece, x, integrateByParts) is not { } integrated)
return null;
total += integrated;
}
Entity quotient = Number.Integer.Zero;
var offset = x - root;
for (var order = multiplicity; order < count; order++)
{
if (coefficients[order] == Number.Integer.Zero)
continue;
var coefficient = Functions.PartialFractions.InLowestTermsOverTheSymbols(coefficients[order] / MathS.Pow(beta, multiplicity));
var shift = order - multiplicity;
quotient += shift == 0 ? coefficient : shift == 1 ? coefficient * offset : coefficient * MathS.Pow(offset, shift);
}
if (quotient != Number.Integer.Zero)
{
var expanded = Functions.PartialFractions.Bare(quotient.Expand().InnerSimplified);
if (Integration.ComputeIndefiniteIntegral(expanded / rest, x, integrateByParts) is not { } integrated)
return null;
total += integrated;
}
return total;
}

/// <summary>
/// A polynomial in <c>x^2</c> over <c>a + b x^2 + c x^4</c> with a symbol in it, by the
/// two roots in <c>x^2</c>: with <c>q = sqrt(b^2 - 4 a c)</c> and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,11 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// split, since Hearn's `e^(x^2)/x + 2x e^(x^2) ln(x) + ...` is a sum whose terms are
// not elementary apart; at the top only, and only for a tower no closed rule reads.
if ((answer = IndefiniteIntegralSolver.SolveByRischNormanAnsatz(expr, x)) is { }) return answer;
// A polynomial over a power of a linear and something that is not a polynomial,
// the polynomial written in powers of the linear at its root: the part the power
// divides goes over the rest alone. Before the split, so that the whole polynomial
// is reduced once rather than each of its terms.
if ((answer = IndefiniteIntegralSolver.SolveByReducingThePolynomialOverALinearFactor(expr, x, integrateByParts)) is { }) return answer;
if (expr is Entity.Sumf or Entity.Minusf
&& (answer = IndefiniteIntegralSolver.SolveBySplittingSum(expr, x, integrateByParts)) is { })
return answer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,26 @@ public void SymbolicLinearBlocksBesideAQuadratic(string integrand)
Assert.True(integrand.ToEntity().Integrate("x").Stringize().Length < 4000, "the answer is a page");
}

/// <summary>
/// A polynomial over a power of a linear beside a radical is written in powers of the
/// linear at its root, so that what the power divides goes over the radical alone:
/// <c>(A + B x + C x^2 + D x^3)/((a + b x) sqrt(c + d x))</c> went through the
/// substitution <c>u = sqrt(c + d x)</c> term by term, a cubic in <c>u^2 - c</c> over a
/// symbolic quadratic each time, in a hundred kilobytes of piecewise that did not
/// evaluate within the corpus's budget; reduced it is a quadratic over the root and
/// one <c>p_0/((a + b x) sqrt(c + d x))</c>, under four kilobytes.
/// </summary>
[Theory]
[InlineData("(A + B*x + F*x^2 + G*x^3)/((a + b*x)*sqrt(c + d*x))")]
[InlineData("x^3/((a + b*x)*sqrt(c + d*x))")]
[InlineData("(A + B*x + F*x^2)/((a + b*x)*(c + d*x)^(1/3))")]
public void APolynomialOverALinearBesideARadicalIsReduced(string integrand)
{
DifferentiatesBack(integrand, "x", ("a", 1.7), ("b", 2.3), ("c", 0.4), ("d", 1.1), ("A", 0.5), ("B", 1.3), ("F", 0.7), ("G", 2.1));
var length = integrand.ToEntity().Integrate("x").Stringize().Length;
Assert.True(length < 4000, $"{length} characters of answer for {integrand}");
}

/// <summary>
/// A repeated symbolic quadratic beside the linears: the numerators over its powers are
/// the digits of the numerator over the other factors written in powers of the
Expand Down
Loading