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
14 changes: 14 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,20 @@ after it
|---|---|---|
| `"(a*c + b*c*x)^(-3-2*p)*(f + g*x)*(a^2 + 2*a*b*x + b^2*x^2)^p".ToEntity().Integrate("x")` | `integral(...)`; an answer with no value on the unreleased master | the antiderivative |

### An odd power of a quadratic's derivative over a power of the quadratic is integrated in the quadratic

**Shorter answers, and answers where there were none.** `(b + 2c x)^9/(a + b x + c x^2)^3` was answered through the
partial fractions in 76,153 characters, with a case for each sign of the discriminant. In `u = a + b x + c x^2` an
odd power of a multiple of the derivative is a polynomial in `u` over a power of it, since
`(b + 2c x)^2 = 4c u + b^2 - 4ac`, and the answer is a few powers of the quadratic. The same with a symbol shared
by the linear's coefficients, `(b d + 2c d x)^9`, which ran past a minute
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(b + 2*c*x)^9/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute; 76,153 characters on the unreleased master | 607 characters |
| `"(b*d + 2*c*d*x)^9/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute | 611 characters |

### Half-odd powers of `a + a sec` and `c + d sec` together are integrated in the half angle's sine

**Answers where there were none.** `sec(e + f x) sqrt(a + a sec(e + f x))/sqrt(c + d sec(e + f x))` was declined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,70 @@ Entity Back(Entity mapped)
return (cancelled ? Back(above.ToEntity(variables)) : numerator, factoredBelow);
}

/// <summary>
/// <c>K L^m/Q^n</c> for a quadratic <c>Q = A x^2 + B x + C</c>, a linear <c>L</c> a multiple
/// <c>lambda</c> of its derivative and an odd <c>m &gt;= 3</c>, in <c>u = Q</c>: <c>L^m dx</c> is
/// <c>lambda^m (Q')^(m - 1) du</c>, and <c>(Q')^2 = 4A u + B^2 - 4AC</c>, so the integrand is
/// <c>K lambda^m (4A u + B^2 - 4AC)^((m - 1)/2)/u^n</c>, a polynomial over a power of <c>u</c>.
/// <see langword="null"/> for anything else.
/// </summary>
/// <remarks>
/// Rubi's 1.2.1.2 has <c>(b d + 2c d x)^9/(a + b x + c x^2)^3</c>; through the partial fractions
/// its answer was seventy-six thousand characters, with a case for each sign of the
/// discriminant, where in <c>u</c> it is a sum of a few powers.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
private static Entity? InTheQuadraticAnOddPowerOfItsDerivative(Entity numerator, Entity denominator, Entity.Variable x, bool integrateByParts)
{
Entity? linear = null;
var m = 0;
Entity constant = Number.Integer.One;
foreach (var factor in Mulf.LinearChildren(numerator))
{
if (!factor.ContainsNode(x))
{
constant *= factor;
continue;
}
if (linear is not null || factor is not Powf(var b, Number.Integer { EInteger: var power }) || !power.CanFitInInt32())
return null;
(linear, m) = (b, power.ToInt32Unchecked());
}
Entity? quadratic = null;
var n = 0;
foreach (var factor in Mulf.LinearChildren(denominator))
{
if (!factor.ContainsNode(x))
{
constant /= factor;
continue;
}
var (@base, power) = factor is Powf(var b, Number.Integer { EInteger: var p }) && p.CanFitInInt32() ? (b, p.ToInt32Unchecked()) : (factor, 1);
if (quadratic is not null)
return null;
(quadratic, n) = (@base, power);
}
if (linear is null || quadratic is null || m < 3 || m % 2 == 0 || n < 1
|| !TreeAnalyzer.TryGetPolyLinear(linear, x, out var p1, out var q1) || p1.ContainsNode(x) || q1.ContainsNode(x)
|| !TreeAnalyzer.TryGetPolynomial(quadratic, x, out var read) || read.Count == 0
|| !read.Keys.All(k => k.Sign >= 0 && k.CompareTo(EInteger.FromInt32(2)) <= 0) || !read.ContainsKey(EInteger.FromInt32(2)))
return null;
Entity Coefficient(int k) => read.TryGetValue(EInteger.FromInt32(k), out var c) ? c : Number.Integer.Zero;
var (a2, a1, a0) = (Coefficient(2), Coefficient(1), Coefficient(0));
if (a2.ContainsNode(x) || a1.ContainsNode(x) || a0.ContainsNode(x)
|| !Functions.PartialFractions.IsZeroAsAValue(p1 * a1 - 2 * a2 * q1))
return null;
var lambda = p1 / (2 * a2);
var u = Variable.CreateUnique(numerator / denominator, "u_quadratic");
var inU = (constant * MathS.Pow(lambda, Number.Integer.Create(m))
* MathS.Pow(4 * a2 * u + a1 * a1 - 4 * a2 * a0, Number.Integer.Create((m - 1) / 2))
/ MathS.Pow(u, Number.Integer.Create(n))).InnerSimplified;
if (Integration.ComputeAsAQuestionOfItsOwn(inU, u, integrateByParts) is not { } inTermsOfU
|| inTermsOfU.Nodes.Any(node => node == MathS.NaN))
return null;
return inTermsOfU.Substitute(u, quadratic);
}

/// <summary>
/// A quotient of polynomials, split into two smaller quotients and integrated in two
/// parts — at a rational root of the denominator where it has one, and otherwise at a
Expand Down Expand Up @@ -701,6 +765,12 @@ Entity Back(Entity mapped)
if (!TryReadAsQuotient(expr, out var numerator, out var denominator))
return null;

// An odd power of a multiple of the denominator's derivative over a power of a quadratic,
// in the quadratic: a polynomial over a power of one variable, where the partial
// fractions wrote seventy thousand characters for `(b + 2c x)^9/(a + b x + c x^2)^3`.
if (InTheQuadraticAnOddPowerOfItsDerivative(numerator, denominator, x, integrateByParts) is { } inTheQuadratic)
return inTheQuadratic;

// A power of a sum in the numerator whose coefficients share a symbol, `(b d + 2c d x)^9`,
// is that symbol's power times one of a sum without it: written so, it is a power of the
// denominator's derivative, and the division of the improper fraction as written ran past
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
// Website: https://am.angouri.org.
//

using System;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// An odd power of a multiple of a quadratic's derivative over a power of the quadratic, in the
/// quadratic: a few powers, where the partial fractions wrote tens of thousands of characters.
/// Rubi's 1.2.1.2.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AnOddPowerOfAQuadraticsDerivativeIntegralTest
{
[Theory]
[InlineData("(b + 2*c*x)^9/(a + b*x + c*x^2)^3")]
[InlineData("(b*d + 2*c*d*x)^9/(a + b*x + c*x^2)^3")]
public void InTheQuadratic(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 2000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.4).Substitute("c", 1.9).Substitute("d", 1.6).Substitute("f", 0.7).Substitute("g", 0.45);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -2.1, -0.4, 0.3, 0.6, 1.6, 2.5 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
if (want.IsNaN)
continue;
compared++;
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
}
}
}
Loading