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
12 changes: 12 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ since semantic versioning admits it.
Measured on the suite: **7 of 8,525 tests** referenced the old reading — five boolean-solver inputs
written `A | B`, and two that existed to record this spelling as at risk.

### A power of x is taken out of a bracket to find the derivative of a product of powers

**Answers where there were none.** `x^(2n) (c + d x)^n (2 c x + 3 d x^2)`, Rubi's 1.3.1, was declined. A bracket that
is a polynomial with a power of x in every term now has that power taken out to join the powers it multiplies, and the
integrand is then `x^(2n + 1) (c + d x)^n (2 c + 3 d x)`, the derivative of `x^(2n + 2) (c + d x)^(n + 1)/(n + 1)`
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"x^(2*n)*(c+d*x)^n*(2*c*x+3*d*x^2)".ToEntity().Integrate("x")` | `integral(...)` | 61 characters |
| `"x^(n-1)*(c+d*x^2)^n*(c*x+3*d*x^3)".ToEntity().Integrate("x")` | `integral(...)` | 65 characters |

### The last digits of an evaluated sine, cosine, tangent, arcsine or arctangent

**Silent, and in the hundredth digit.** `EvalNumerical` at the default hundred digits gives the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14618,6 +14618,38 @@ static bool More(Entity difference)
return common * bracket;
}

/// <summary>
/// <paramref name="expr"/> with each factor above the bar that is a sum and a polynomial in
/// <paramref name="x"/> divisible by a power of it written as that power times the quotient,
/// rebuilt from its coefficients; null where no factor is.
/// </summary>
private static Entity? WithAPowerOfXOutOfTheBracket(Entity expr, Entity.Variable x)
{
var (top, bottom) = Functions.SingleQuotient.Of(expr);
var changed = false;
Entity rebuilt = Number.Integer.One;
foreach (var factor in Mulf.LinearChildren(top))
{
if (factor is Sumf or Minusf && TreeAnalyzer.TryGetPolynomial(factor, x, out var read) && read.Count > 1
&& read.Values.All(value => !value.ContainsNode(x)) && read.Keys.All(key => key.Sign >= 0 && key.CanFitInInt32())
&& read.Keys.Min()! is { Sign: > 0 } lowest)
{
var shift = lowest.ToInt32Unchecked();
Entity reduced = Number.Integer.Zero;
foreach (var monomial in read)
{
var k = monomial.Key.ToInt32Unchecked() - shift;
reduced += monomial.Value * (k == 0 ? Number.Integer.One : MathS.Pow(x, Number.Integer.Create(k)));
}
rebuilt *= MathS.Pow(x, Number.Integer.Create(shift)) * reduced;
changed = true;
}
else
rebuilt *= factor;
}
return changed ? rebuilt / bottom : null;
}

/// <summary>
/// A product of powers times a sum that is the derivative of the product with some of
/// the powers raised by one: <c>e^x x^2 ln(x)^2 (3 + (3 + x) ln(x))</c> is
Expand Down Expand Up @@ -14657,6 +14689,12 @@ static bool More(Entity difference)
// elementary.
if (expr is Sumf or Minusf)
return WithTheCommonPowersOut(expr, x) is { } factored ? SolveAsTheDerivativeOfAProductOfPowers(factored, x) : null;
// A bracket that is a polynomial with a power of x in every term has that power taken out, to
// join the powers it multiplies: `x^(2n) (c + d x)^n (2 c x + 3 d x^2)` is
// `x^(2n + 1) (c + d x)^n (2 c + 3 d x)`, the derivative of `x^(2n + 2) (c + d x)^(n + 1)/(n + 1)`,
// and with the x in the bracket nothing raised x by enough.
if (WithAPowerOfXOutOfTheBracket(expr, x) is { } outOfTheBracket)
return SolveAsTheDerivativeOfAProductOfPowers(outOfTheBracket, x);
Entity constant = Number.Integer.One;
Entity? bracket = null;
var raisable = new List<(Entity Base, Entity Exponent)>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// 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>
/// The derivative of a product of powers with a power of x in every term of its bracket: taken out,
/// it joins the power of x it multiplies. Rubi's 1.3.1 <c>x^(2n) (c + d x)^n (2 c x + 3 d x^2)</c>.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class APowerOfXOutOfTheBracketOfADerivativeIntegralTest
{
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());

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");
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
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}");
}
}

// d with the sign that keeps the argument real at every sampled x, and |d| below 2/2.3^2
// so that the inverse sine and cosine stay inside [-1, 1]; off it the exact checks hold too.
[Theory]
[InlineData("x^(2*n)*(c+d*x)^n*(2*c*x+3*d*x^2)")]
[InlineData("x^(n-1)*(c+d*x^2)^n*(c*x+3*d*x^3)")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("c", 1.4), ("d", 0.75), ("n", 2.5));
DifferentiatesBack(integrand, ("c", 0.5), ("d", -0.3), ("n", 1.0 / 3));
}
}
}
Loading