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
13 changes: 13 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,19 @@ by the linear's coefficients, `(b d + 2c d x)^9`, which ran past a minute
| `"(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 |

### A trinomial the numerator holds is divided out of a power of it

**A shorter answer, where 2.5.0 had none.** `(d + g x + f x^2)(a + b x^2 + c x^4)/(a + b x^2 + c x^4)^4`, written
multiplied out as Rubi's 1.2.2.5 writes it, was declined after a minute by 2.5.0, and is answered on the unreleased
master in 98,237 characters. The reduction of a polynomial over a power of a trinomial in `x^n` divides the trinomial
out of the numerator first now, wherever it divides exactly, with the trinomial's content in the symbols taken out of
it, since it arrives made monic: the answer is that of `(d + g x + f x^2)/(a + b x^2 + c x^4)^3`, 19,037 characters
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(a*d + a*g*x + (b*d + a*f)*x^2 + b*g*x^3 + (c*d + b*f)*x^4 + c*g*x^5 + c*f*x^6)/(a + b*x^2 + c*x^4)^4".ToEntity().Integrate("x")` | `integral(...)` | 19,037 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 @@ -1856,6 +1856,47 @@ Entity InU(Dictionary<EInteger, Entity> polynomial)
/// </summary>
private const int MaximumPowerOfABinomial = 12;

/// <summary>
/// <paramref name="dividend"/> divided by <paramref name="divisor"/> as polynomials in all
/// their symbols, each over its own bar, where the division of what is above the bars is
/// exact; <see langword="null"/> where it is not, or where either is not a polynomial
/// over a bar free of <paramref name="x"/>.
/// </summary>
/// <remarks>
/// The bars are there because the partial fractions make a block monic before this reads
/// it: `a + b x^2 + c x^4` arrives as `a/c + b/c x^2 + x^4`.
/// </remarks>
private static Entity? ExactlyDivided(Entity dividend, Entity divisor, Entity.Variable x)
{
var (above, aboveBar) = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(dividend));
var (below, belowBar) = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(divisor));
if (aboveBar.ContainsNode(x) || belowBar.ContainsNode(x))
return null;
var variables = above.Vars.Concat(below.Vars).Distinct().OrderBy(v => v.Name, System.StringComparer.Ordinal).ToList();
if (variables.Count == 0 || variables.Count > MultivariatePolynomial.MaxVariables)
return null;
var indices = new Dictionary<Variable, int>();
for (var i = 0; i < variables.Count; i++)
indices[variables[i]] = i;
if (MultivariatePolynomial.TryParse(above, indices) is not { IsZero: false } top
|| MultivariatePolynomial.TryParse(below, indices) is not { IsConstant: false } bottom
|| !indices.TryGetValue(x, out var at))
return null;
// Its content in x goes below the bar: the quotient `a/c + b/c x^2 + x^4` is combined as
// `c (a + b x^2 + c x^4)` over `c^2`, and the numerator holds the trinomial, not the c.
var rest = Enumerable.Range(0, variables.Count).Where(variable => variable != at).ToList();
Entity content = Number.Integer.One;
if (Functions.PolynomialGcd.ContentIn(bottom, at, rest, 0) is { IsConstant: false } inTheSymbols
&& bottom.DivideExact(inTheSymbols) is { } primitive)
{
bottom = primitive;
content = inTheSymbols.ToEntity(variables);
}
if (top.DivideExact(bottom) is not { } quotient)
return null;
return (quotient.ToEntity(variables) * belowBar / (aboveBar * content)).InnerSimplified;
}

/// <summary>
/// A polynomial over a power of a trinomial in <c>x^n</c>,
/// <c>P(x)/(a + b x^n + c x^(2n))^k</c> with <c>n &gt;= 2</c> and <c>k &gt;= 2</c>, a power at
Expand Down Expand Up @@ -1904,6 +1945,14 @@ Entity InU(Dictionary<EInteger, Entity> polynomial)
else
return null;
}
// A numerator the trinomial divides is divided by it first: Rubi's 1.2.2.5 writes
// `(d + e x + f x^2)(a + b x^2 + c x^4)` multiplied out over the fourth power, and over it
// the answer was 99,000 characters where over the third it is 20,000.
while (trinomial is { } && power > 1 && ExactlyDivided(numerator, trinomial, x) is { } fewer)
{
numerator = fewer;
power--;
}
if (trinomial is null || power > MaximumPowerOfABinomial
|| !TreeAnalyzer.TryGetPolynomial(trinomial, x, out var below) || below.Count != 3
|| below.Keys.Any(degree => !degree.CanFitInInt32()) || below.Values.Any(coefficient => coefficient.ContainsNode(x))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// 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>
/// A polynomial over a power of a trinomial in <c>x^n</c> that holds the trinomial has it
/// divided out first. Rubi's 1.2.2.5.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ATrinomialTheNumeratorHoldsIntegralTest
{
[Fact]
public void DifferentiatesBack()
{
const string integrand = "(a*d + a*g*x + (b*d + a*f)*x^2 + b*g*x^3 + (c*d + b*f)*x^4 + c*g*x^5 + c*f*x^6)/(a + b*x^2 + c*x^4)^4";
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Entity Pinned(Entity e) => e.Substitute("a", 2).Substitute("b", 3).Substitute("c", 5)
.Substitute("d", -1).Substitute("g", 7).Substitute("f", 1);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
foreach (var at in new[] { -1.7, -0.6, 0.45, 1.3 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}
}
}
Loading