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 @@ -210,6 +210,19 @@ answer on the far side of a root gets a real expression where it used to get a c
| `1/(x*(-4+x^2)^4)` | 8,472 ms | 1,878 ms |
| `1/((1+x)^3*(2+x)^3)` | 1,343 ms | 492 ms |

### A whole power of the radicand under Euler's substitution is read as one

**Answers where there were none, and none where they were wrong.** Euler's substitution writes the
radicand's powers in its variable as powers of the root, and read a whole power `Q^n` as the root
to the `n`th, which is `Q^(n/2)`, so it integrated another integrand than the one asked. It reads
it as the root to the `2n`th now. `(x^2 + 3)^2/(x + sqrt(x^2 + 3))`, past the substitution's
bound on the degree once read rightly, is declined, as 2.5.0 declined it; the unreleased master
answered it wrongly ([#1770](https://github.com/asc-community/AngouriMath/issues/1770)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"1/((x^2 + 3)^2*(x + sqrt(x^2 + 3)))".ToEntity().Integrate("x")` | `integral(...)` | `-2/((sqrt(x^2 + 3) + x)^2 + 3)^2` |

### A whole power of a sum of two square roots below the bar is rationalised

**Answers where there were none.** `x/(sqrt(a + b x) + sqrt(c + b x))^3` was declined, while the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16599,9 +16599,12 @@ private static bool VanishesIdentically(Entity expr)
}

var dxdt = xInT.Differentiate(t);
// `Q^(k/2)` is the root to the `k`th power, and a whole power `Q^n` the root to the
// `2n`th: read as the `n`th, it was `Q^(n/2)`, and `Q^2/(x + sqrt(Q))` was answered as
// `Q/(x + sqrt(Q))`. https://github.com/asc-community/AngouriMath/issues/1770
var rewritten = expr.Replace(node =>
node is Powf(var @base, Number.Rational half) && @base == radicand
? MathS.Pow(rootInT, Number.Integer.Create(half.ERational.Numerator))
node is Powf(var @base, Number.Rational power) && @base == radicand
? MathS.Pow(rootInT, Number.Integer.Create(power is Number.Integer ? power.ERational.Numerator.Multiply(2) : power.ERational.Numerator))
: node)
.Substitute(x, xInT) * dxdt;
if (powered)
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 System.Linq;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// Euler's substitution writes a whole power of the radicand, <c>Q^n</c>, as the root to the
/// <c>2n</c>th power. It wrote the <c>n</c>th, which is <c>Q^(n/2)</c>, and integrated a
/// different integrand from the one asked.
/// https://github.com/asc-community/AngouriMath/issues/1770
/// </summary>
[Trait("Area", "Calculus")]
public sealed class WholePowerOfTheRadicandUnderEulerTest
{
private static readonly double[] Points = { -1.7, -0.6, 0.4, 1.3, 2.2 };

private static void DifferentiatesBack(Entity integrand, Entity integral)
{
var derivative = integral.Differentiate("x");
foreach (var at in Points)
{
var got = (double)((Entity.Number.Complex)derivative.Substitute("x", at).EvalNumerical()).RealPart;
var want = (double)((Entity.Number.Complex)integrand.Substitute("x", at).EvalNumerical()).RealPart;
Assert.True(Math.Abs(got - want) < 1e-9 * Math.Max(1, Math.Abs(want)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}

[Theory]
[InlineData("1/((x^2 + 3)^2*(x + sqrt(x^2 + 3)))")]
[InlineData("1/((x^2 + 2*x + 3)^2*(x + sqrt(x^2 + 2*x + 3)))")]
public void ItIsAnswered(string written)
{
var integrand = written.ToEntity();
var integral = integrand.Integrate("x");
Assert.DoesNotContain(integral.Nodes, node => node is Entity.Integralf);
DifferentiatesBack(integrand, integral);
}

// Past the substitution's bound on the degree with the power read right: declined, as
// it may be, or answered rightly.
[Theory]
[InlineData("(x^2 + 3)^2/(x + sqrt(x^2 + 3))")]
[InlineData("(x^2 + 3)^3/(1 + sqrt(x^2 + 3))")]
public void ItIsNotAnsweredWrongly(string written)
{
var integrand = written.ToEntity();
var integral = integrand.Integrate("x");
if (!integral.Nodes.Any(node => node is Entity.Integralf))
DifferentiatesBack(integrand, integral);
}
}
}
Loading