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 @@ -762,6 +762,21 @@ taken apart into pieces each closed the same way
| `"(2 + x)/((2 + 4*x - 3*x^2)*(1 + 3*x + 2*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative |
| `"1/((x^2 + 1)*sqrt(x^2 + x + 1))".ToEntity().Integrate("x")` | `integral(...)` | a logarithm and an arctangent |

### A rational function of `x^n` beside a power of `x` and the root of a binomial is integrated

**Answers where there were none.** `x/((a + b x^3)^(2/3) (c + d x^3))` and `x^4 (a + b x^3)^(1/3)/(c + d x^3)`,
Rubi's 1.1.3.4, were declined, while the same without the power of `x` in front was answered under
`u = x/(a + b x^n)^(1/n)`. That substitution takes `x^j (a + b x^n)^(k - (j + 1)/n)` beside a rational
function of `x^n` to `u^j` times a rational function of `u`, for any `j` below `n`, and is taken for
those now. Each answer holds on both sides of zero, an odd root of a negative being real here
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"x/((a + b*x^3)^(2/3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | logarithms and an arctangent of `x (a + b x^3)^(-1/3)` |
| `"x^4*(a + b*x^3)^(1/3)/(c + d*x^3)".ToEntity().Integrate("x")` | `integral(...)` | the same, beside a rational function of it |
| `"x/((1 + x^3)^(2/3)*(2 + x^3))".ToEntity().Integrate("x")` | `integral(...)` | the same with numbers |

### A rational function of `x^n` beside a power of a symbolic binomial is integrated without a search

**Answers where there were none.** A rational function of `x^n` beside `(c + d x^n)^(k - 1/n)` is a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4185,6 +4185,14 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
/// The identity holds wherever the root is real, which is where the answer is
/// asked; <c>u</c> goes back in as <c>x (c + d x^n)^(-1/n)</c>.
/// </para>
/// <para>
/// And beside a power of <c>x</c> that is not one of <c>x^n</c>: <c>x^j</c> times a
/// rational function of <c>x^n</c> times <c>(c + d x^n)^(k - (j + 1)/n)</c> is
/// <c>u^j</c> times the same in <c>u</c>, since <c>x^j (c + d x^n)^(-j/n)</c> is
/// <c>u^j</c>. Rubi's 1.1.3.4: <c>x/((a + b x^3)^(2/3) (c + d x^3))</c> is
/// <c>u/(c - (b c - a d) u^3)</c>, and <c>x^4 (a + b x^3)^(1/3)/(c + d x^3)</c> is the
/// same with <c>x^3</c> beside it; each was declined.
/// </para>
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveByDividingByTheRoot(Entity expr, Entity.Variable x)
Expand All @@ -4195,6 +4203,8 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
Entity? c = null, d = null;
var n = 0;
var k = EInteger.Zero;
// The power of x beside it, below n: the exponent is `k - (j + 1)/n`.
var j = 0;
Entity rest = Number.Integer.One;
var (above, below) = Functions.SingleQuotient.Of(expr);
foreach (var (side, isBelow) in new[] { (above, false), (below, true) })
Expand All @@ -4206,15 +4216,18 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
if (radicand is not null || !TryReadAsABinomialIn(@base, x, out var readC, out var readD, out var readN)
|| readN < 2 || !signed.Denominator.Equals(EInteger.FromInt32(readN)))
return null;
// The numerator of the exponent is k n - 1.
var shifted = signed.Numerator.Add(EInteger.One);
if (!shifted.Remainder(EInteger.FromInt32(readN)).IsZero)
return null;
// The numerator of the exponent is k n - j - 1, for j from 0 to n - 1.
var nAsEInteger = EInteger.FromInt32(readN);
var residue = signed.Numerator.Add(EInteger.One).Remainder(nAsEInteger);
if (residue.Sign < 0)
residue = residue.Add(nAsEInteger);
var readJ = residue.IsZero ? 0 : readN - residue.ToInt32Checked();
radicand = @base;
c = readC;
d = readD;
n = readN;
k = shifted.Divide(EInteger.FromInt32(readN));
j = readJ;
k = signed.Numerator.Add(EInteger.FromInt32(readJ + 1)).Divide(nAsEInteger);
continue;
}
if (factor.ContainsNode(x) && !IsRationalIn(factor, x))
Expand All @@ -4223,8 +4236,25 @@ Entity Carried(int numerator, int denominator, int nextSine, int nextCosine)
}
if (radicand is null || c is null || d is null || TreeAnalyzer.IsZero(c) || TreeAnalyzer.IsZero(d))
return null;
// The rest as a rational function of x^n: written in a stand-in for x^n where every
// power of x is a multiple of n, and declined otherwise.
// The rest as a rational function of x^n, x^j taken out of it first: written in a
// stand-in for x^n where every power of x is a multiple of n, and declined otherwise.
if (j != 0)
{
// Out of the polynomial above the bar, each of whose powers is then a multiple of n.
var (restAbove, restBelow) = Functions.SingleQuotient.Of(rest);
if (!TreeAnalyzer.TryGetPolynomial(restAbove, x, out var aboveTerms) || aboveTerms.Count == 0)
return null;
Entity lowered = Number.Integer.Zero;
foreach (var term in aboveTerms)
{
var power = term.Key.Subtract(EInteger.FromInt32(j));
if (power.Sign < 0 || term.Value.ContainsNode(x))
return null;
var monomial = power.IsZero ? term.Value : term.Value * MathS.Pow(x, Number.Integer.Create(power));
lowered = lowered == Number.Integer.Zero ? monomial : lowered + monomial;
}
rest = restBelow == Number.Integer.One ? lowered : lowered / restBelow;
}
var xToTheN = Variable.CreateUnique(expr, "x_n");
var inXToTheN = rest.Replace(node =>
node is Powf(var xAgain, Number.Integer power) && xAgain == x && power.EInteger.Remainder(EInteger.FromInt32(n)).IsZero
Expand All @@ -4243,8 +4273,11 @@ node is Powf(var xAgain, Number.Integer power) && xAgain == x && power.EInteger.
// base is its polynomial in u first, which collects what the substitution writes twice --
// `1 - (-u^2) - u^2` is 1 under a secant's half angle -- and the factors stay as they are
// written, which the rules after this read better than their product expanded.
var restInU = inXToTheN.Substitute(xToTheN, c * uToTheN / oneMinusDUToTheN);
if (j != 0)
restInU = MathS.Pow(u, j) * restInU;
var integrand = WithTheFactorsWrittenOnBothSidesCancelled(u, Functions.SingleQuotient.Combine(
inXToTheN.Substitute(xToTheN, c * uToTheN / oneMinusDUToTheN)
restInU
* (k.IsZero ? Number.Integer.One : MathS.Pow(binomialInU, Number.Integer.Create(k)))
/ oneMinusDUToTheN).InnerSimplified);
if (integrand is Providedf(var inner, _))
Expand Down
15 changes: 15 additions & 0 deletions Sources/Tests/UnitTests/Calculus/DividedByTheRootIntegralTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,20 @@ public void TimofeevsQuarticBesideAFourthRoot()
[InlineData("1/((a + b*x^2)^(9/2)*(1 + x^2))")]
public void AHalfOddPowerOfASymbolicQuadratic(string integrand)
=> DifferentiatesBack(integrand, new[] { -1.3, -0.4, 0.3, 0.9, 1.7 }, ("a", 1.3), ("b", 0.7));

/// <summary>
/// Beside a power of x that is not one of x^n: <c>x^j</c> times a rational function of
/// <c>x^n</c> times <c>(a + b x^n)^(k - (j + 1)/n)</c> is <c>u^j</c> times the same in
/// <c>u</c>. Rubi's 1.1.3.4; each was declined. On both sides of zero, the cube root of a
/// negative being real.
/// </summary>
[Theory]
[InlineData("x/((a + b*x^3)^(2/3)*(c + d*x^3))")]
[InlineData("x^4/((a + b*x^3)^(2/3)*(c + d*x^3))")]
[InlineData("x^7/((a + b*x^3)^(2/3)*(c + d*x^3))")]
[InlineData("x^4*(a + b*x^3)^(1/3)/(c + d*x^3)")]
[InlineData("x/((1 + x^3)^(2/3)*(2 + x^3))")]
public void BesideAPowerOfX(string integrand)
=> DifferentiatesBack(integrand, new[] { -2.7, -1.3, -0.6, 0.4, 1.3, 2.6 }, ("a", 2.3), ("b", 0.7), ("c", 1.3), ("d", 1.7));
}
}
Loading