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 @@ -208,6 +208,19 @@ 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 |

### A quadratic with rational roots counts as two linears before the substitution search

**Answers where there were none.** `sec(x)^2 sin(x)/(a + b sin(x))^3` ran past thirty seconds: under the
half-angle tangent it is a rational function over `(1 - u^2)^2 (a u^2 + 2 b u + a)^3`, and the substitution
search spent eighty seconds on it before the partial fractions, which split it at once, were asked. The search
already declines a rational function over symbolic linear factors and a quadratic; `1 - u^2` is two linear
factors written together, and is counted so ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sec(x)^2*sin(x)/(a + b*sin(x))^3".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 5,017 characters |
| `"tan(x)^2/(a + b*sin(x))^3".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 4,816 characters |

### A sine of a double argument beside the argument is written as a product

**Answers where there were none.** `csc(a + b x)^3 sin(2a + 2b x)^7` was declined: the rule that writes multiples
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21277,6 +21277,12 @@ private static bool IsAProductOfSymbolicLinearFactors(Entity denominator, Entity
return false;
if (read.Keys.Max()!.Equals(EInteger.One))
linears++;
// A quadratic with rational coefficients and rational roots is two linear factors
// written together: the half-angle tangent's `1 - u^2` beside `a u^2 + 2 b u + a` is
// `(1 - u)(1 + u)` to the split, and the search spent eighty seconds declining Rubi's
// `sec(x)^2 sin(x)/(a + b sin(x))^3` before it was asked.
else if (read.Keys.Max()!.Equals(EInteger.FromInt32(2)) && HasRationalRoots(read))
linears += 2;
else if (read.Keys.Max()!.Equals(EInteger.FromInt32(2)))
quadratics++;
else
Expand All @@ -21286,6 +21292,23 @@ private static bool IsAProductOfSymbolicLinearFactors(Entity denominator, Entity
return (linears >= 1 || !aLinearAmongThem) && linears + quadratics >= 2 && symbolic;
}

/// <summary>
/// Whether a quadratic read as its coefficients has rational coefficients and a
/// discriminant that is the square of a rational.
/// </summary>
private static bool HasRationalRoots(Dictionary<EInteger, Entity> quadratic)
{
Number.Rational? Coefficient(int power)
=> quadratic.TryGetValue(EInteger.FromInt32(power), out var c) ? c.Evaled as Number.Rational : Number.Integer.Zero;
if (Coefficient(2) is not { } a || Coefficient(1) is not { } b || Coefficient(0) is not { } c)
return false;
var discriminant = (b * b - 4 * a * c).Evaled;
if (discriminant is not Number.Rational d || d.ERational.IsNegative)
return false;
var root = MathS.Sqrt(d).Evaled;
return root is Number.Rational;
}

/// <summary>
/// Whether a written factor of <paramref name="denominator"/> with a symbol in it stands
/// to a whole power of two or more: where the Hermite reduction's one solve takes those
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>
/// A rational function of the sine over a power of <c>a + b sin(x)</c> with the cosine below the bar: under
/// the half-angle tangent, <c>(1 - u^2)</c> beside <c>a u^2 + 2 b u + a</c>, split by its partial fractions
/// rather than searched for a substitution. Rubi's 4.1.1.2, 4.1.1.3 and 4.1.2.2.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AHalfAngleRationalFunctionOverOneMinusTheSquareIntegralTest
{
[Theory]
[InlineData("sec(x)^2*sin(x)/(a + b*sin(x))^3")]
[InlineData("tan(x)^2/(a + b*sin(x))^3")]
public void ByItsPartialFractions(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 20000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 2.3).Substitute("b", 0.7);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.9 })
{
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