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 @@ -733,6 +733,21 @@ The answers are as large as the split integrands' answers, which are piecewise i
| `"(a - b*x^2)^(3/2)/sqrt(a^2 - b^2*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 6,543 characters |
| `"1/((a - b*x^2)^(1/2)*sqrt(a^2 - b^2*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 877 characters |

### A symbolic power of a product beside a symbolic power of one of its factors is split

**Answers where there were none.** `(d + k x)^(-3 - 2p) (a d k + (c d^2 + a k^2) x + c d k x^2)^p` was declined:
the split of a power of a product beside a power of a factor took rational exponents only. `(P Q)^r` is
`K P^r Q^r` with `K` constant between the zeros of `P` and `Q` for any exponent free of x, so a symbolic one is
split too, and the powers of `P` gather ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(d + k*x)^(-3 - 2*p)*(a*d*k + (c*d^2 + a*k^2)*x + c*d*k*x^2)^p".ToEntity().Integrate("x")` | not done in 60 seconds | 349 characters |
| `"(d + k*x)^m/(a*d*k + (c*d^2 + a*k^2)*x + c*d*k*x^2)^m".ToEntity().Integrate("x")` | `integral(...)` | 215 characters |
| `"(A + B*x)*(a*c + b*c*x)^m/(a^2 + 2*a*b*x + b^2*x^2)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | 476 characters |
| `"(d + k*x)^m*(f + g*x)^3/(a*d*k + (c*d^2 + a*k^2)*x + c*d*k*x^2)^m".ToEntity().Integrate("x")` | `integral(...)` | 1,817 characters |
| `"(1 - x^4)^n/(1 + x + x^2 + x^3)^n".ToEntity().Integrate("x")` | `integral(...)` | 147 characters |

### A secant or a cosecant beside a power of a sum of them is read as a power of the cosine or the sine

**Answers where there were none.** `sec(x) sqrt(sec(x) + tan(x))` was declined, with the rest of Rubi's 4.7.7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19334,7 +19334,8 @@ Entity TakeOut(Entity node)
}

/// <summary>
/// A root of a product beside a root of one of its factors, the product split:
/// A root of a product beside a root of one of its factors, or a symbolic power of one
/// beside a symbolic power of a factor, the product split:
/// <c>(P Q)^r</c> is <c>K P^r Q^r</c>, where <c>K = (P Q)^r/(P^r Q^r)</c> is constant wherever
/// it is defined, for the same reason as in
/// <see cref="SolveByTakingTheWholePowersOutOfAPowerOfAProduct"/>. The powers of <c>P</c>
Expand All @@ -19350,7 +19351,7 @@ Entity TakeOut(Entity node)
{
if (!Integration.AnsweringTheQuestionAsked || splittingARootOfAProduct)
return null;
var roots = new List<(Entity Node, Entity Base, Number.Rational Exponent)>();
var roots = new List<(Entity Node, Entity Base, Entity Exponent)>();
Gather(expr);
if (roots.Count < 2)
return null;
Expand Down Expand Up @@ -19393,7 +19394,12 @@ void Gather(Entity node)
Gather(above);
Gather(below);
break;
case Powf(var @base, Number.Rational exponent) when exponent is not Number.Integer && @base.ContainsNode(x):
// A symbolic exponent as well: K is exp(2 pi i n r) for a whole n constant
// between the zeros of P and Q, whatever r is. Rubi's 1.2.1.2:2389,
// `(d + e x)^(-3 - 2 p) ((d + e x) (a e + c d x))^p`, gathers into
// `(d + e x)^(-3 - p) (a e + c d x)^p`.
case Powf(var @base, var exponent) when exponent is not Number.Integer && !exponent.ContainsNode(x) && @base.ContainsNode(x)
&& (exponent is Number.Rational || exponent.Vars.Any()):
roots.Add((node, @base, exponent));
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// 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 symbolic power of a product of two linears beside a symbolic power of one of them:
/// <c>(P Q)^p</c> is a constant times <c>P^p Q^p</c>, and the powers of <c>P</c> gather.
/// Rubi's 1.2.1.2 <c>(d + e x)^(-3 - 2 p) (a d e + (c d^2 + a e^2) x + c d e x^2)^p</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 ASymbolicPowerOfAProductBesideAPowerOfAFactorIntegralTest
{
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());

// Pinned before it is differentiated, with exponents whose denominators are even, so
// that every power of a negative base is read on the principal branch
// (https://github.com/asc-community/AngouriMath/issues/204).
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}");
}
}

[Theory]
[InlineData("(d+k*x)^(-3-2*p)*(a*d*k+(c*d^2+a*k^2)*x+c*d*k*x^2)^p")]
[InlineData("(d+k*x)^(-5-2*p)*(a*d*k+(c*d^2+a*k^2)*x+c*d*k*x^2)^p")]
[InlineData("(a*d*k+(c*d^2+a*k^2)*x+c*d*k*x^2)^p/(d+k*x)^p")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("a", 2), ("c", 1.5), ("d", 1.25), ("k", 2), ("p", 0.25));
DifferentiatesBack(integrand, ("a", -1), ("c", 2), ("d", -0.5), ("k", 1.5), ("p", -1.75));
}
}
}
Loading