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
24 changes: 24 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,30 @@ same answer as on the unreleased master, in 1.4 seconds rather than 30
| `"1/((d + k*x)*(f + g*x)*(a + b*x + c*x^2)^2)".ToEntity().Integrate("x")` | `integral(...)` | 24,143 characters |
| `"1/((d + k*x)^3*(a + b*x + c*x^2)^2)".ToEntity().Integrate("x")` | `integral(...)` | 9,593 characters |

### Powers of linears beside a symbolic root of a quadratic are taken apart over the linears

**Answers where there were none.** `sqrt(a + b x + c x^2)/((d + k x)(f + g x)^2)` was declined: with no quadratic
below the bar, powers of linears were left to Euler's substitution, which writes the root of a symbolic leading
coefficient into every coefficient and does not finish. They are taken apart over the linears now where the
integrand has a symbol in it, each piece closed beside the root. The answers are large because each piece's cases,
by the sign of the radicand at its pole and of `c`, multiply in the sum
([#1874](https://github.com/asc-community/AngouriMath/issues/1874)): the fourth row is 27 cases of about 3,000
characters each ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sqrt(a + b*x + c*x^2)/((d + k*x)*(f + g*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 12,694 characters |
| `"sqrt(a + b*x + c*x^2)/((d + k*x)*(f + g*x)^3)".ToEntity().Integrate("x")` | `integral(...)` | 20,029 characters |
| `"sqrt(a + b*x + c*x^2)/((d + k*x)*(f + g*x)^4)".ToEntity().Integrate("x")` | `integral(...)` | 29,203 characters |
| `"(a + b*x + c*x^2)^(3/2)/((d + k*x)*(f + g*x)^3)".ToEntity().Integrate("x")` | `integral(...)` | 79,744 characters |
| `"1/((d + k*x)*(f + g*x)^2*sqrt(a + b*x + c*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 11,335 characters |
| `"1/((d + k*x)*(f + g*x)^3*sqrt(a + b*x + c*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 18,103 characters |
| `"sqrt(a + b*x^2 + c*x^4)/(x^3*(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 5,902 characters |
| `"(a + b*x^2 + c*x^4)^(3/2)/(x^3*(d + k*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 27,184 characters |
| `"1/(x^3*(d + k*x^2)*sqrt(a + b*x^2 + c*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 5,515 characters |
| `"asec(a + b*x)/x^4".ToEntity().Integrate("x")` | `integral(...)` | 11,873 characters |
| `"acsc(a + b*x)/x^4".ToEntity().Integrate("x")` | `integral(...)` | 11,864 characters |

### A trinomial the numerator holds is divided out of a power of it

**A shorter answer, where 2.5.0 had none.** `(d + g x + f x^2)(a + b x^2 + c x^4)/(a + b x^2 + c x^4)^4`, written
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18636,8 +18636,10 @@ static Entity WithoutConditions(Entity coefficient)
// the linears alone the reciprocal writes a sign of each, `sgn(sqrt(1 + x) - 1)` once
// `sqrt(x + sqrt(1 + x))/x^2` is taken back from `u = sqrt(1 + x)`, whose derivative
// is not read where the argument is not shown real -- and Euler's substitution, after
// this, answers those without one.
if (beyondTheLinears && !aQuadratic)
// this, answers those without one. With a symbol in them it does not: the leading
// coefficient's root is in every coefficient it writes, and `sqrt(a + b x + c x^2)/(x^2 (d + e x))`,
// which is Rubi's 1.2.2.4 under `u = x^2`, was declined.
if (beyondTheLinears && !aQuadratic && !expr.Vars.Any(symbol => symbol != x))
return null;
// Distinct: two factors with the same root are one repeated, which is not this.
// Beyond the linears the split decides that, and merges linears with one root.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// 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 rational function with powers of linears below the bar, beside a half-odd power of a
/// quadratic with symbols in it, taken apart over the linears: Euler's substitution, which
/// the split left these to, writes the root of a symbolic leading coefficient into every
/// coefficient. Rubi's 1.2.2.4 <c>sqrt(a + b x^2 + c x^4)/(x^3 (d + e x^2))</c> is one under
/// <c>u = x^2</c>, written here with <c>k</c> for <c>e</c>, which is Euler's number.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class PowersOfLinearsBesideASymbolicRootIntegralTest
{
private static void DifferentiatesBack(string integrand, double[] points, params (string, double)[] pins)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Assert.DoesNotContain("NaN", integral.Stringize());

// Pinned before it is differentiated: the answer carries `sgn(x + d/k)`, whose
// derivative is read where its argument is a number.
var pinned = integral.Substitute("C", 0);
Entity original = integrand.ToEntity();
foreach (var (name, value) in pins)
{
pinned = pinned.Substitute(name, value);
original = original.Substitute(name, value);
}
var derivative = pinned.Differentiate("x");

var compared = 0;
foreach (var at in points)
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
if (got.IsNaN || want.IsNaN)
continue;
compared++;
var difference = Math.Abs((double)(got - want).RealPart)
+ Math.Abs((double)(got - want).ImaginaryPart);
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
Assert.True(difference / scale < 1e-8,
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, "
+ $"where the integrand is {want}");
}
Assert.True(compared >= 3,
$"only {compared} of {points.Length} points were comparable for {integrand}");
}

[Theory]
[InlineData("sqrt(a+b*x+c*x^2)/(x^2*(d+k*x))")]
[InlineData("1/(x^2*(d+k*x)*sqrt(a+b*x+c*x^2))")]
[InlineData("sqrt(a+b*x^2+c*x^4)/(x^3*(d+k*x^2))")]
[InlineData("1/((d+k*x)*(f+g*x)^2*sqrt(a+b*x+c*x^2))")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
var points = new[] { -1.4, -0.3, 0.8, 2.25 };
DifferentiatesBack(integrand, points, ("a", 2), ("b", 1.0 / 3), ("c", 1.5), ("d", 1.25), ("k", 3), ("f", 7.0 / 3), ("g", -0.4));
DifferentiatesBack(integrand, points, ("a", -1), ("b", 3), ("c", 2), ("d", -2), ("k", 1.5), ("f", 0.5), ("g", 5.0 / 3));
}
}
}
Loading