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
14 changes: 14 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,20 @@ answered too ([#1852](https://github.com/asc-community/AngouriMath/issues/1852))
| `"x^m*sin(a + ln(k*x^n)*sqrt(-(1 + m)^2/n^2))".ToEntity().Integrate("x")` | `integral(...)` | 465 characters |
| `"x^2*sin(a + ln(k*x^n)*sqrt((-1)/n^2))^3".ToEntity().Integrate("x")` | `integral(...)` | 1,873 characters |

### A power of a linear beside a quadratic's derivative over a power of the quadratic is integrated by parts

**Answers where there were none.** `(b + 2 c x) sqrt(d + k x)/(a + b x + c x^2)^2` ran past any budget, the partial fractions
taking the symbolic quadratic apart twice over. With `u` the power of the linear and `dv` the derivative over the power
of the quadratic, parts leaves the quadratic one power lower, which is answered in under half a second
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(b + 2*c*x)*(d + k*x)^4/(a + b*x + c*x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 2,215 characters |
| `"(b + 2*c*x)*sqrt(d + k*x)/(a + b*x + c*x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 976 characters |
| `"(b + 2*c*x)/((a + b*x + c*x^2)^2*sqrt(d + k*x))".ToEntity().Integrate("x")` | `integral(...)` | 1,075 characters |
| `"(b + 2*c*x)*(d + k*x)^(3/2)/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 4,054 characters |

### An odd power of a quadratic's derivative over a power of the quadratic is integrated in the quadratic

**Shorter answers, and answers where there were none.** `(b + 2c x)^9/(a + b x + c x^2)^3` was answered through the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19342,6 +19342,82 @@ Entity TakeOut(Entity node)
return inTermsOfU.Substitute(u, Number.Integer.One / x);
}

/// <summary>
/// A power of a linear beside the derivative of a quadratic, over a whole power of that
/// quadratic, by parts once: <c>Q' L^m/Q^n</c> with <c>u = L^m</c> and
/// <c>dv = Q'/Q^n dx</c> is <c>-L^m/((n - 1) Q^(n - 1)) + m e/(n - 1) * integral L^(m - 1)/Q^(n - 1)</c>,
/// for <c>L = d + e x</c>, and the power of the quadratic is one lower in what is left.
/// </summary>
/// <remarks>
/// Rubi's 1.2.1.3 <c>(b + 2 c x) sqrt(d + e x)/(a + b x + c x^2)^2</c> and its kin took
/// thirty seconds or ran past the budget, the partial fractions taking the symbolic
/// quadratic apart twice over; what is left over its first power is answered in a third of
/// a second -- a whole power of the linear too, `(b + 2 c x) (d + e x)^4/(a + b x + c x^2)^2`
/// ran past the budget there. The numerator is read up to a constant multiple of <c>Q'</c>.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveAPowerOfALinearBesideTheDerivativeOfAQuadraticOverItsPower(Entity expr, Entity.Variable x, bool integrateByParts)
{
var (numerator, denominator) = Functions.SingleQuotient.Of(expr);
Entity? quadratic = null;
var power = 0;
Entity? linear = null;
Entity exponent = Number.Integer.Zero;
Entity? derivative = null;
Entity constant = Number.Integer.One;
foreach (var (side, below) in new[] { (numerator, false), (denominator, true) })
foreach (var factor in Mulf.LinearChildren(side))
{
if (!factor.ContainsNode(x))
{
constant = below ? constant / factor : constant * factor;
continue;
}
var (@base, raised) = factor is Powf(var b, var r) && !r.ContainsNode(x) ? (b, r) : (factor, (Entity)Number.Integer.One);
if (below && quadratic is null && raised is Number.Integer { EInteger: var whole } && whole.CompareTo(EInteger.FromInt32(2)) >= 0
&& whole.CanFitInInt32() && TreeAnalyzer.TryGetPolyQuadratic(@base, x, out var leading, out _, out _) && !VanishesIdentically(leading))
{
quadratic = @base;
power = whole.ToInt32Unchecked();
continue;
}
if (!TreeAnalyzer.TryGetPolyLinear(@base, x, out var slope, out _) || VanishesIdentically(slope))
return null;
// The derivative once, above the bar; the linear any number of times, either side.
if (!below && derivative is null && raised == Number.Integer.One && (linear is null || linear != @base))
{
derivative = @base;
continue;
}
if (linear is not null && linear != @base)
return null;
linear = @base;
exponent = below ? exponent - raised : exponent + raised;
}
if (quadratic is null || derivative is null || linear is null
|| !TreeAnalyzer.TryGetPolyQuadratic(quadratic, x, out var a, out var bq, out _)
|| !TreeAnalyzer.TryGetPolyLinear(derivative, x, out var s, out var t)
|| !TreeAnalyzer.TryGetPolyLinear(linear, x, out var e, out _))
return null;
// The derivative of Q is 2 a x + b; the numerator's linear is lambda times it.
var lambda = (s / (2 * a)).InnerSimplified;
if (!VanishesIdentically(t - lambda * bq))
return null;
exponent = exponent.InnerSimplified;
// A negative whole power of the linear is a rational function whose power of the
// linear parts would raise: `(b + 2 c x)/((d + e x) Q^3)` went from 6,220 characters
// to 14,351, the partial fractions answering it whole as before.
if (exponent == Number.Integer.Zero || exponent is Number.Integer { EInteger.Sign: < 0 })
return null;
var lower = power - 1;
var outside = -MathS.Pow(linear, exponent) / (lower * MathS.Pow(quadratic, Number.Integer.Create(lower)));
var left = exponent * e * MathS.Pow(linear, (exponent - 1).InnerSimplified) / (lower * MathS.Pow(quadratic, Number.Integer.Create(lower)));
if (Integration.ComputeAsAQuestionOfItsOwn(left.InnerSimplified, x, integrateByParts) is not { } rest)
return null;
var answer = (constant * lambda * (outside + rest)).InnerSimplified;
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
}

/// <summary>
/// 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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// the question asked only, and the polynomial term below writes `1/u` as `u^(-1)` and
// asks that a level down: `1/(e^atanh(a x) (c - c/(a x))^(3/2))` reached it nowhere.
if ((answer = IndefiniteIntegralSolver.SolveByWritingEachLinearOnce(expr, x, integrateByParts)) is { }) return answer;
// A power of a linear beside the derivative of a quadratic over a whole power of it, by
// parts once, before anything takes the symbolic quadratic apart.
if ((answer = IndefiniteIntegralSolver.SolveAPowerOfALinearBesideTheDerivativeOfAQuadraticOverItsPower(expr, x, integrateByParts)) is { }) return answer;
// A root or a power of a product beside a power of one of its factors, the product
// split: `(a + b x^2)^(5/2)/sqrt(a^2 - b^2 x^4)` is `K (a + b x^2)^2/sqrt(a - b x^2)`.
// Early, since what reads the product whole is slower and longer: Rubi's 1.2.1.2:2218,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// 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 power of a linear beside the derivative of a quadratic over a whole power of the
/// quadratic, by parts once. Rubi's 1.2.1.3 <c>(b + 2 c x) sqrt(d + e x)/(a + b x + c x^2)^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 TheDerivativeOfAQuadraticOverItsPowerIntegralTest
{
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, as the answers the rest of the integral writes
// carry signs and cases read where their arguments are numbers.
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("(b+2*c*x)*sqrt(d+k*x)/(a+b*x+c*x^2)^2")]
[InlineData("(b+2*c*x)/((a+b*x+c*x^2)^2*sqrt(d+k*x))")]
[InlineData("(b+2*c*x)*(d+k*x)^4/(a+b*x+c*x^2)^2")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("a", 2), ("b", 1.5), ("c", 1.25), ("d", 1.25), ("k", 2));
DifferentiatesBack(integrand, ("a", -1), ("b", 2), ("c", 2), ("d", -0.5), ("k", 1.5));
}
}
}
Loading