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 @@ -195,6 +195,21 @@ 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 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
first power of such a sum below the bar is multiplied above and below by its conjugate, the product
of the pair being the difference of the radicands. A whole power is multiplied by the same power of
the conjugate now; an even power over a constant difference is left as it was, since its conjugate
brings the root of the product of the radicands with it, and those were answered as written. Rubi's
1.3.2 ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"x/(sqrt(a + b*x) + sqrt(c + b*x))^3".ToEntity().Integrate("x")` | `integral(...)` | powers of the two roots over `(a - c)^3` |
| `"1/(sqrt(a + b*x) + sqrt(a + c*x))^3".ToEntity().Integrate("x")` | `integral(...)` | the same, with logarithms and arctangents |
| `"x^3/(sqrt(a + b*x) + sqrt(a + c*x))^2".ToEntity().Integrate("x")` | `integral(...)` | the same; 10 s on the unreleased master, a tenth of one now |

### Two square roots of linears with one slope are rationalised together

`sqrt(L1)` and `sqrt(L2)` with `L1 - L2` a constant are rational in their sum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20255,19 +20255,33 @@ private static Entity OverTheFunctionLessARoot(Entity root, bool ofTheSine, Enti
Entity rest = Number.Integer.One;
foreach (var factor in Mulf.LinearChildren(denominator))
{
if (conjugate is null && factor is Sumf or Minusf && Sumf.LinearChildren(factor).ToList() is { Count: 2 } terms
// Or a whole power of the pair, whose conjugate is the power of the pair's:
// `x/(sqrt(a + b x) + sqrt(c + b x))^3` was declined where its square was answered.
var (pair, power) = factor is Powf(var raised, Number.Integer { EInteger: var whole }) && whole.Sign > 0 && whole.CompareTo(EInteger.FromInt32(8)) <= 0
? (raised, whole.ToInt32Checked())
: (factor, 1);
if (conjugate is null && pair is Sumf or Minusf && Sumf.LinearChildren(pair).ToList() is { Count: 2 } terms
&& terms.All(term => IsARootOfAPolynomial(term)))
{
// The conjugate flips the sign of the second term; the product is the
// difference of the squares, each square a polynomial.
var first = terms[0];
var second = terms[1];
conjugate = first - second;
conjugate = power == 1 ? first - second : MathS.Pow(first - second, power).Expand();
product = (SquareOf(first) - SquareOf(second)).InnerSimplified;
// Not the same radicand twice: the difference is zero, the factor is zero
// or a multiple of one root, and neither is this rule's.
if (product.Evaled is Number.Complex { IsZero: true } || product.Simplify().Evaled is Number.Complex { IsZero: true })
return null;
// An even power of the conjugate has the root of the product of the radicands
// in it, `2 sqrt(A B)` in the square, which over a constant difference leaves a
// root of a quadratic where the power as written was answered: the squares over
// `sqrt(a + b x) + sqrt(c + b x)` ran past the budget so, and were answered in a
// second as they stood.
if (power % 2 == 0 && !product.Simplify().ContainsNode(x))
return null;
if (power != 1)
product = MathS.Pow(product, power);
continue;
}
rest = rest * factor;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// 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 sum of two square roots below the bar, multiplied above and below by the
/// same power of the conjugate: <c>x/(sqrt(a + b x) + sqrt(c + b x))^3</c> is
/// <c>x (sqrt(a + b x) - sqrt(c + b x))^3/(a - c)^3</c>. The first power was rationalised and
/// the cubes were declined. Rubi's 1.3.2.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class APowerOfASumOfRootsIntegralTest
{
[Theory]
[InlineData("x/(sqrt(a + b*x) + sqrt(c + b*x))^3")]
[InlineData("1/(sqrt(a + b*x) + sqrt(a + c*x))^3")]
[InlineData("x^2/(sqrt(a + b*x) + sqrt(a + c*x))^3")]
[InlineData("x^3/(sqrt(a + b*x) + sqrt(a + c*x))^2")]
public void ThroughTheConjugate(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
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());
foreach (var at in new[] { 0.3, 0.8, 1.5, 2.4 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
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}");
}
}
}
}
Loading