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
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,21 @@ or of its square over the root. Rubi's 1.3.3 names those coefficients, and its r
| `"(c - 2*d*x)/((c + d*x)*sqrt(c^3 + 4*d^3*x^3))".ToEntity().Integrate("x")` | `integral(...)` | `2 c/d` times an arctangent of `sqrt(3 c^3) (1 + 2 d x/c)/sqrt(c^3 + 4 d^3 x^3)` over `sqrt(3 c^3)`, or an inverse hyperbolic tangent, by the sign of `c^3` |
| `"(1+x)/((x-2)*sqrt(1+x^3))".ToEntity().Integrate("x")` | `integral(...)` | `-(2/3) artanh((1 + x)^2/(3 sqrt(1 + x^3)))`, written as a logarithm |

### `x` over a cubic binomial beside the root of another is integrated where that is elementary

**Answers where there were none.** `x/((a + b x^3) sqrt(c + d x^3))` is elementary at `4 b c = a d`
and at `8 b c + a d = 0`, Rubi's 1.1.3.4, as arctangents and inverse hyperbolic tangents of
`sqrt(c + d x^3)` and of `(1 + q x)/sqrt(c + d x^3)` for `q = (d/c)^(1/3)`, and Welz's
`x/((4 - x^3) sqrt(1 - x^3))` and the rest at those ratios were declined. They are answered in
closed form now, by the sign of `c`
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"x/((4 - x^3)*sqrt(1 - x^3))".ToEntity().Integrate("x")` | `integral(...)` | two arctangents and two inverse hyperbolic tangents, of `sqrt(1 - x^3)` and of `(1 - 2^(1/3) x)/sqrt(1 - x^3)` and its kind |
| `"x/((4*c + d*x^3)*sqrt(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | the same in `sqrt(c + d x^3)` and `q = (d/c)^(1/3)`, by the sign of `c` |
| `"x/((8 - d*x^3)*sqrt(1 + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | `(artanh((1 + d^(1/3) x)^2/(3 sqrt(1 + d x^3))) - artanh(sqrt(1 + d x^3)/3))/(18 d^(2/3)) - atan(sqrt(3) (1 + d^(1/3) x)/sqrt(1 + d x^3))/(6 sqrt(3) d^(2/3))`, written with logarithms |

### `x^2` over a three-quarter power of a quadratic binomial beside another is integrated where that is elementary

**Answers where there were none.** `x^2/((A + B x^2)^(3/4) (C + D x^2))` at `B C - 2 A D = 0`, Rubi's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4920,6 +4920,105 @@ Entity OverOnePlus(Entity k)
return holds ? answer : null;
}

/// <summary>
/// <c>x/((a + b x^3) sqrt(c + d x^3))</c> at the two ratios where its integral is elementary,
/// Rubi's 1.1.3.4: <c>4 b c = a d</c> and <c>8 b c + a d = 0</c>, in closed form.
/// </summary>
/// <remarks>
/// <para>
/// With <c>q = (d/c)^(1/3)</c>, the real root, and <c>y = sqrt(c + d x^3)</c>, at
/// <c>4 b c = a d</c>, for <c>r = sqrt(c)</c> where <c>c</c> is positive and
/// <c>r = sqrt(-c)</c> where it is negative:
/// <code>
/// c &gt; 0: q/(3 2^(2/3) b r) (artanh(y/r)/3 + atan(y/(sqrt(3) r))/sqrt(3)
/// - atan(sqrt(3) r (1 + 2^(1/3) q x)/y)/sqrt(3) - artanh(r (1 - 2^(1/3) q x)/y))
/// c &lt; 0: -q/(3 2^(2/3) b r) (atan(y/r)/3 + artanh(y/(sqrt(3) r))/sqrt(3)
/// + artanh(sqrt(3) r (1 + 2^(1/3) q x)/y)/sqrt(3) + atan(r (1 - 2^(1/3) q x)/y))
/// </code>
/// and at <c>8 b c + a d = 0</c>, where the integrand is <c>-(d/b) x/((8 c - d x^3) y)</c>, that
/// times
/// <code>
/// c &gt; 0: (artanh(r (1 + q x)^2/(3 y))/18 - artanh(y/(3 r))/18 - atan(sqrt(3) r (1 + q x)/y)/(6 sqrt(3)))/(r^3 q^2)
/// c &lt; 0: -(atan(r (1 + q x)^2/(3 y))/18 + atan(y/(3 r))/18 - artanh(sqrt(3) r (1 + q x)/y)/(6 sqrt(3)))/(r^3 q^2)
/// </code>
/// Rubi splits the second into three integrals, a substitution's in <c>x^3</c>, a linear
/// over a linear and a quadratic over a quadratic, each elementary, and their sum is the
/// form above; asked as three, two come back piecewise in the symbols and their sum holds
/// every combination of the arms. Every condition is decided as a value, and the answer is
/// differentiated back at sampled points before it is returned.
/// Welz's <c>x/((4 - x^3) sqrt(1 - x^3))</c> and <c>x/((8 - d x^3) sqrt(1 + d x^3))</c> were
/// declined, with the rest of 1.1.3.4 at those ratios.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </para>
/// </remarks>
internal static Entity? SolveXOverACubicBinomialBesideTheRootOfAnother(Entity expr, Entity.Variable x, bool integrateByParts)
{
var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
if (!TreeAnalyzer.TryGetPolynomial(numerator, x, out var above) || above.Count != 1
|| !above.TryGetValue(EInteger.One, out var multiple) || multiple.ContainsNode(x))
return null;
Entity? radicand = null;
Entity below = Number.Integer.One;
foreach (var factor in Mulf.LinearChildren(denominator))
if (factor is Powf(var @base, Number.Rational half) && half.ERational.Equals(ERational.Create(1, 2)) && @base.ContainsNode(x))
{
if (radicand is not null)
return null;
radicand = @base;
}
else
below = below == Number.Integer.One ? factor : below * factor;
if (radicand is null || ACubicBinomial(radicand) is not var (c, d) || ACubicBinomial(below) is not var (a, b))
return null;
static bool Zero(Entity condition) => Functions.PartialFractions.IsZeroAsAValue(condition);
if (Zero(a) || Zero(b) || Zero(c) || Zero(d) || Zero(b * c - a * d))
return null;
var y = MathS.Sqrt(radicand);
var q = MathS.Pow(LowestOverTheSymbols(d / c), Number.Rational.Create(1, 3));
var cubeRootOfTwo = MathS.Pow(2, Number.Rational.Create(1, 3));
var sqrtOfThree = MathS.Sqrt(3);
Entity? answer = null;
if (Zero(4 * b * c - a * d))
{
var r = MathS.Sqrt(c);
var wherePositive = q / (3 * MathS.Sqr(cubeRootOfTwo) * b * r)
* (MathS.Hyperbolic.Artanh(y / r) / 3 + MathS.Arctan(y / (sqrtOfThree * r)) / sqrtOfThree
- MathS.Arctan(sqrtOfThree * r * (1 + cubeRootOfTwo * q * x) / y) / sqrtOfThree
- MathS.Hyperbolic.Artanh(r * (1 - cubeRootOfTwo * q * x) / y));
var s = MathS.Sqrt(LowestOverTheSymbols(-c));
var whereNegative = -q / (3 * MathS.Sqr(cubeRootOfTwo) * b * s)
* (MathS.Arctan(y / s) / 3 + MathS.Hyperbolic.Artanh(y / (sqrtOfThree * s)) / sqrtOfThree
+ MathS.Hyperbolic.Artanh(sqrtOfThree * s * (1 + cubeRootOfTwo * q * x) / y) / sqrtOfThree
+ MathS.Arctan(s * (1 - cubeRootOfTwo * q * x) / y));
answer = BySign(c, wherePositive, whereNegative);
}
else if (Zero(8 * b * c + a * d))
{
// The integrand is -(d/b) x/((8 c - d x^3) y).
var r = MathS.Sqrt(c);
var wherePositive = (MathS.Hyperbolic.Artanh(r * MathS.Sqr(1 + q * x) / (3 * y)) / 18 - MathS.Hyperbolic.Artanh(y / (3 * r)) / 18
- MathS.Arctan(sqrtOfThree * r * (1 + q * x) / y) / (6 * sqrtOfThree)) / (MathS.Pow(r, 3) * MathS.Sqr(q));
var s = MathS.Sqrt(LowestOverTheSymbols(-c));
var whereNegative = -(MathS.Arctan(s * MathS.Sqr(1 + q * x) / (3 * y)) / 18 + MathS.Arctan(y / (3 * s)) / 18
- MathS.Hyperbolic.Artanh(sqrtOfThree * s * (1 + q * x) / y) / (6 * sqrtOfThree)) / (MathS.Pow(s, 3) * MathS.Sqr(q));
answer = LowestOverTheSymbols(-d / b) * BySign(c, wherePositive, whereNegative);
}
if (answer is null)
return null;
answer = (multiple * answer).InnerSimplified;
bool holds;
using (MathS.Settings.DowncastingEnabled.Set(false))
holds = Functions.PartialFractions.HoldsAtSampledPoints(answer.Differentiate(x), expr, x);
return holds ? answer : null;

// `p + r x^3`, as `(p, r)`, each free of x.
(Entity, Entity)? ACubicBinomial(Entity binomial)
=> TreeAnalyzer.TryGetPolynomial(binomial, x, out var read) && read.Count == 2
&& read.TryGetValue(EInteger.Zero, out var constant) && read.TryGetValue(EInteger.FromInt32(3), out var cubic)
&& !constant.ContainsNode(x) && !cubic.ContainsNode(x)
? (constant, cubic) : null;
}

/// <summary>
/// A root of a quadratic binomial beside another, <c>1/((A + B x^2)^(1/3) (C + D x^2))</c>
/// with <c>B C + 3 A D = 0</c> or <c>B C - 9 A D = 0</c>, and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// `(1 + x + sqrt(3))/((1 + x - sqrt(3)) sqrt(1 + x^3))`. Before the reduction over the
// linear, which writes it as `1` and `2 sqrt(3)` over the linear, neither elementary.
if ((answer = IndefiniteIntegralSolver.SolveAPseudoEllipticQuotientOverTheRootOfACubicBinomial(expr, x)) is { }) return answer;
// x over a cubic binomial beside the root of another, at the two ratios where that is
// elementary: Welz's `x/((4 - x^3) sqrt(1 - x^3))`.
if ((answer = IndefiniteIntegralSolver.SolveXOverACubicBinomialBesideTheRootOfAnother(expr, x, integrateByParts)) is { }) return answer;
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 { })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// 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>
/// <c>x/((a + b x^3) sqrt(c + d x^3))</c> at the two ratios where its integral is elementary,
/// Rubi's 1.1.3.4: <c>4 b c = a d</c>, in closed form by the sign of <c>c</c>, and
/// <c>8 b c + a d = 0</c>, as three integrals.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
/// <remarks>
/// Checked by differentiating back with <c>c</c> pinned to either sign and <c>d = 0.7</c>, on
/// both sides of 0 wherever the integrand is real.
/// </remarks>
[Trait("Area", "Calculus")]
public sealed class XOverACubicBinomialBesideTheRootOfAnotherIntegralTest
{
private static readonly double[] Points = { -2.3, -1.4, -0.8, -0.3, 0.4, 0.9, 1.6, 2.4 };

[Theory]
// 4 b c = a d.
[InlineData("x/((4 - x^3)*sqrt(1 - x^3))", 1.3)]
[InlineData("x/((4 - d*x^3)*sqrt(-1 + d*x^3))", 1.3)]
[InlineData("x/((4*c + d*x^3)*sqrt(c + d*x^3))", 1.3)]
[InlineData("x/((4*c + d*x^3)*sqrt(c + d*x^3))", -1.3)]
// 8 b c + a d = 0.
[InlineData("x/((8 - d*x^3)*sqrt(1 + d*x^3))", 1.3)]
[InlineData("x/((8*c - d*x^3)*sqrt(c + d*x^3))", 1.3)]
[InlineData("x/((8*c - d*x^3)*sqrt(c + d*x^3))", -1.3)]
[InlineData("3*x/((8 + x^3)*sqrt(-1 + x^3))", 1.3)]
public void IsIntegrated(string integrand, double c)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Entity Pinned(Entity e) => e.Substitute("c", c).Substitute("d", 0.7);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in Points)
{
var want = original.Substitute("x", at).EvalNumerical();
if (want.IsNaN || Math.Abs((double)want.ImaginaryPart) > 1e-12 * Math.Max(1, Math.Abs((double)want.RealPart)))
continue;
var got = derivative.Substitute("x", at).EvalNumerical();
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)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
Assert.True(compared >= 2, $"only {compared} points were real for {integrand}");
}
}
}
Loading