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
40 changes: 40 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2963,6 +2963,46 @@ without a common factor
| `"(c + d*tan(x))^(3/2)/(a + i*a*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)`; 470,119 characters on the unreleased master | 77,377 characters |
| `"(c + d*tan(x))^(1/2)/(a + i*a*tan(x))^2".ToEntity().Integrate("x")` | `integral(...)`; 258,943 characters on the unreleased master | 71,266 characters |

### A rational function of `x/sqrt(P)` for an even quartic `P`, high powers of two linears, and nine more algebraic shapes are integrated

**Answers where there were none, and shorter ones.** Rubi's algebraic integrands of these shapes were declined or ran
out of time ([#233](https://github.com/asc-community/AngouriMath/issues/233)):
- A rational function beside a half-odd power of `P = a + b x^2 + c x^4`, where it is a rational function of
`t = x/sqrt(P)`: `dt/dx = (a - c x^4)/P^(3/2)`, so `(a - c x^4)/((a d + a e x^2 + c d x^4) sqrt(P))` is
`dt/(d + (a e - b d) t^2)`. Which integrands are is read in `y = x^2` modulo `y^2 - w y + a/c`, `w = y + a/(c y)`;
an elliptic integral does not pass.
- `(c x)^t`, `t` not whole, written `x^t` beside `(c x)^t x^(-t)`, whose derivative is zero wherever it is defined:
`(c x)^(-1 - 2 n - n p) (a + b x^n)^p`.
- Two square roots whose radicands have a constant times an even power of x for their quotient are one root, up to a
factor whose square is constant: `(g + h x) sqrt(a + b x + c x^2)/(a d + b d x + c d x^2)^(3/2)`.
- A polynomial over high whole powers of two linears, under `t = L1/L2`: `x^2/((1 - a x)^16 (1 + a x)^11)`.
- An algebraic function of `1/x` alone, under `x = 1/u`: `1/((a + b/x)^(5/2) (c + d/x)^2)`.
- `x^m (a + b x^n)^p` for whole `m` and `n` and `p ≤ -2`, by the reduction that raises `p` to `-1`:
`1/(x^2 (a + b x^3)^5)`.
- A power of a sum of powers of x beside a symbolic power of a binomial, multiplied out into binomial differentials:
`(a + b x^n)^3 (c + d x^n)^(-4 - 1/n)`.
- A difference of radicands that is one power of x, written as one: `x^4/(sqrt(a + b x) + sqrt(a + c x))^3`.
- A rational function beside an odd root of a linear past the square, split first where a linear below meets the root
at an irrational value: `1/(x^3 (1 - x^2)^(1/3) (3 + x^2))`.
- An odd integrand beside a high power of a quadratic is left to `u = x^2`, and a quartic in one shifted linear to
`u = d + k x`, where the partial fractions spent the budget or wrote pages.

Answers the unreleased master gave change length: `x^2/((1 - a x)^16 (1 + a x)^11)` was 16,292 characters there,
after twenty seconds, and is 1,380 in a tenth of a second; `1/((a + b/x)^(5/2) (c + d/x)^2)` was 16,675 and is 4,667.
One is longer: `1/(x (1 - x^2)^(1/3) (3 + x^2))`, 342 characters there and 495 now.

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(a - c*x^4)/((a*d + a*k*x^2 + c*d*x^4)*sqrt(a + b*x^2 + c*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 1,949 characters |
| `"(g + h*x)*sqrt(a + b*x + c*x^2)/(a*d + b*d*x + c*d*x^2)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | 4,376 characters |
| `"x^2/((1 - a*x)^16*(1 + a*x)^11)".ToEntity().Integrate("x")` | `integral(...)` | 1,380 characters |
| `"1/((a + b/x)^(5/2)*(c + d/x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 4,667 characters |
| `"(c*x)^(-1 - 2*n - n*p)*(a + b*x^n)^p".ToEntity().Integrate("x")` | `integral(...)` | 255 characters |
| `"1/(x^2*(a + b*x^3)^5)".ToEntity().Integrate("x")` | `integral(...)` | 741 characters |
| `"(d + k*x)^4/(a + b*(d + k*x)^2 + c*(d + k*x)^4)".ToEntity().Integrate("x")` | no answer in twenty seconds | 452 characters |
| `"1/(x^5*(a + b*x^2)^10)".ToEntity().Integrate("x")` | `integral(...)` | 652 characters, where the unreleased master gave 67,828 |
| `"1/(x*(1 - x^2)^(1/3)*(3 + x^2))".ToEntity().Integrate("x")` | `integral(...)` | 503 characters, where the unreleased master gave 350 |

### A polynomial with symbols in it that is a binomial or an even quartic in a shifted variable is integrated in it

**Answers where there were none.** `1/(c^2 x^3 + 3 b c x^2 + 3 b^2 x + 3 a b)` was left unevaluated,
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A whole power of a linear in the inverse sine, cosine or hyperbolic cosine of
// `c + d x^2` with `c^2 = 1`, by its recurrence, which closes: by parts it went round.
if ((answer = IndefiniteIntegralSolver.SolveAPowerOfAnInverseFunctionOfAQuadraticByTheRecurrence(expr, x)) is { }) return answer;
// An algebraic function of 1/x alone with a root in it, under x = 1/u, before the linears
// are written once: `1/((a + b/x)^(5/2) (c + d/x)^2)` ran past the budget that way.
if ((answer = IndefiniteIntegralSolver.SolveAnAlgebraicFunctionOfTheReciprocalOfX(expr, x, integrateByParts)) is { }) return answer;
// A product of powers of quotients of linears with one linear in two of them, written
// over each linear once with the constant that takes in front. First, since it answers
// the question asked only, and the polynomial term below writes `1/u` as `u^(-1)` and
Expand Down Expand Up @@ -757,6 +760,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A rational function with numbers for coefficients over a denominator past the twelfth
// degree, by Hermite's reduction before anything searches: nothing below answered one.
if ((answer = IndefiniteIntegralSolver.SolveAHighDegreeRationalFunctionByHermite(expr, x)) is { }) return answer;
// And a polynomial over high whole powers of two linears with a symbol in them, under
// t = L1/L2, where the partial fractions ran past the budget: `x^2/((1 - a x)^16 (1 + a x)^11)`.
if ((answer = IndefiniteIntegralSolver.SolveAPolynomialOverHighWholePowersOfTwoLinears(expr, x)) is { }) return answer;
// A whole power of the square of a polynomial written out, written over its root: the rules
// for a power of a binomial read `(a + b x^2)^4` where `(a^2 + 2 a b x^2 + b^2 x^4)^2` ran
// past the budget.
Expand Down Expand Up @@ -1022,6 +1028,11 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// 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 rational function with numbers beside an odd root of a linear past the square, split over
// the rationals first where a linear below meets the root at an irrational value:
// `1/(u^2 (1 - u)^(1/3) (3 + u))`, whose substitution is over the cube root of 4, and which the
// cancellation below spent five seconds declining.
if ((answer = IndefiniteIntegralSolver.SolveARationalFunctionBesideAnIrrationalOddRootOfALinearSplitFirst(expr, x, integrateByParts)) is { }) return answer;
// A quotient of polynomials in x and the functions of x in it, cancelled with those
// functions as indeterminates and its denominator factored the same way:
// `(x^2 + 2x sin(x) + sin(x)^2)/(x + sin(x))^2` is `1`, and nothing else reads it.
Expand Down Expand Up @@ -1537,6 +1548,20 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A half-odd power of a constant over a polynomial of either sign, written apart with
// its sign in front. After the substitutions, which answer some of these without it.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAHalfOddPowerOfAReciprocalWithItsSign(expr, x, integrateByParts)) is { }) return answer;
// A power of x beside a whole power below -1 of a binomial with a symbol in it, by the
// reduction that raises the power to -1: `1/(x^2 (a + b x^3)^5)`, which the partial
// fractions declined past the first power.
if ((answer = IndefiniteIntegralSolver.SolveABinomialToAWholePowerBelowMinusOneByTheRecurrence(expr, x, integrateByParts)) is { }) return answer;
// A rational function beside a half-odd power of an even quartic P, as a rational
// function of x/sqrt(P) where it is one: Rubi's pseudo-elliptic
// `(a - c x^4)/((a d + a e x^2 + c d x^4) sqrt(a + b x^2 + c x^4))`. Late, since it fires
// on nothing a rule reading the root as written answers.
if ((answer = IndefiniteIntegralSolver.SolveByTheVariableOverTheRootOfAnEvenQuartic(expr, x, integrateByParts)) is { }) return answer;
// A factor `(c x)^t` as `x^t`, `(c x)^t x^(-t)` in front, constant on each interval.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAPowerOfAMultipleOfXAsAPowerOfX(expr, x, integrateByParts)) is { }) return answer;
// Two square roots as one, up to a factor whose square is a constant, in front:
// `sqrt(b - a/x^2)/sqrt(a - b x^2)` is that factor over x.
if ((answer = IndefiniteIntegralSolver.SolveByWritingTwoRootsAsOneUpToAConstantFactor(expr, x, integrateByParts)) is { }) return answer;
// The sign of a real-valued factor is constant between its zeros, and goes in
// front of the antiderivative of the rest. After every rule that reads the sign
// where it stands: `cos(x) sgn(sin(x))` is `|sin(x)|` by the substitution, and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// 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 power of x beside a whole power below -1 of a binomial with a symbol in it, by the reduction
/// that raises the power to -1, and the root of a perfect square that leaves one: Rubi's 1.2.3.2.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ABinomialToAWholePowerBelowMinusOneIntegralTest
{
[Theory]
[InlineData("1/(x^2*(a + b*x^3)^5)")]
[InlineData("1/(x^3*(a + b*x^3)^5)")]
[InlineData("1/(x^2*(a^2 + 2*a*b*x^3 + b^2*x^6)^(5/2))")]
[InlineData("1/(x^3*(a^2 + 2*a*b*x^3 + b^2*x^6)^(5/2))")]
public void RaisedToTheFirstPower(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4);
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}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// 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 factor <c>(c x)^t</c> written as <c>x^t</c>, with <c>(c x)^t x^(-t)</c> in front, whose
/// derivative is zero: Rubi's 1.1.3.2 and 1.2.3.2 with a symbol for the multiple of x.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class APowerOfAMultipleOfXAsAPowerOfXIntegralTest
{
[Theory]
[InlineData("(c*x)^(-1 - 2*n - n*p)*(a + b*x^n)^p")]
[InlineData("(c*x)^(-1 - 3*n - n*p)*(a + b*x^n)^p")]
[InlineData("(c*x)^(-1 - 4*n - n*p)*(a + b*x^n)^p")]
[InlineData("(d*x)^(-1 - 2*n*(1 + p))*(a^2 + 2*a*b*x^n + b^2*x^(2*n))^p")]
public void WrittenAsAPowerOfX(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
// n past the wrap, so that x^n at a negative x is not confined to a half-plane.
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", -0.4).Substitute("d", 0.3)
.Substitute("n", 2.3).Substitute("p", 0.6);
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}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// 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 whole power of a binomial in <c>x^n</c> beside a power of another with a symbol in its
/// exponent, written out with the power kept beside each term: each term is a binomial
/// differential in Chebyshev's third case. Rubi's 1.1.3.3.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AProductOfSumsBesideASymbolicPowerIntegralTest
{
[Theory]
[InlineData("(a + b*x^n)^3*(c + d*x^n)^(-4 - 1/n)")]
[InlineData("(a + b*x^n)^2*(c + d*x^n)^(-4 - 1/n)")]
public void WrittenOutBesideThePower(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 10000, $"{text.Length} characters of answer for {integrand}");
// n past the wrap, so that x^n at a negative x is not confined to a half-plane.
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("d", 0.3)
.Substitute("n", 2.3);
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}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// 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 power of a linear over a trinomial in its square, written in the linear rather than split
/// over the quartic's quadratics first: Rubi's 1.2.3.2, which ran past the budget split.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AQuarticInOneShiftedLinearIntegralTest
{
[Theory]
[InlineData("(d + k*x)^4/(a + b*(d + k*x)^2 + c*(d + k*x)^4)")]
[InlineData("(d + k*x)^2/(a + b*(d + k*x)^2 + c*(d + k*x)^4)")]
public void WrittenInTheShiftedLinear(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("d", 0.9).Substitute("k", 1.1);
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
Loading