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
16 changes: 16 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,22 @@ Rubi's `x^m (a + b x^n)^p` and `(a + b x^n)^p (c + d x^n)^q`
| `"1/(2+3/x^2)^3".Integrate("x")`, `1/(a + b/x^3)` | left unevaluated | an antiderivative |
| `"(a+b*x^n)*(c+d*x^n)^3".Integrate("x")` | left unevaluated | written out, eight powers of `x` integrated: `a c^3 x + ... + b d^3 x^(4 n + 1)/(4 n + 1)` |

### A half-odd power of a constant over a polynomial of either sign is integrated with its sign

**Answers where there were none.** `sqrt(1/(1 - x^2))`, the arcsine's derivative where it is real,
was declined, with every half-odd power of a constant over a polynomial whose sign changes: the
reading that writes a power of a quotient apart does so only where the denominator is positive. For
a real `Q` other than zero, `(A/Q)^r` is `(A/Q)^r Q^r` times `Q^(-r)`, the first factor's derivative
being zero wherever it has one, and for a positive number `A` that factor is `A^r sgn(Q)`. The power
is written so, after the substitutions, and the factor goes in front of the answer
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sqrt(1/(1 - x^2))".ToEntity().Integrate("x")` | `integral(...)` | `sgn(1 - x^2) arcsin(x)` |
| `"(1/(1 - x^2))^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | `sgn(1 - x^2) x/sqrt(1 - x^2)` |
| `"(c/(a + b*x^2))^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | `(c/(a + b x^2))^(3/2) (a + b x^2)^(3/2) x/(a sqrt(a + b x^2))` |

### A whole power of a quotient with a symbol in it is integrated as the quotient of the powers

**Answers where there were none, and a slowdown since 2.5.0 undone.** `(c/(a + c x^2))^2` was
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16911,6 +16911,44 @@ Entity Without(Entity side)
return answer;
}

/// <summary>
/// A half-odd power of a constant over a polynomial whose sign changes, among the factors
/// of the integrand, written apart with the factor that keeps it right on both sides of the
/// polynomial's zeros: for a real <c>Q</c> other than zero, <c>(A/Q)^r</c> is
/// <c>K Q^(-r)</c> with <c>K = (A/Q)^r Q^r</c>, whose derivative is zero wherever it has one,
/// and for a positive number <c>A</c> that is <c>A^r</c> times the sign of <c>Q</c>, the
/// root of a negative being <c>i</c> times the root of its modulus on both sides of the bar.
/// </summary>
/// <remarks>
/// <c>sqrt(1/(1 - x^2))</c> is <c>sgn(1 - x^2)/sqrt(1 - x^2)</c>, the arcsine's derivative
/// where it is real, and was declined, with every such power over a polynomial that is not
/// positive: the reading that writes a power of a quotient apart does so only where it is.
/// Late in the chain, after the substitutions, which answer some of these without the
/// factor -- <c>x sqrt(1/(4 - x^2))</c> is <c>-(1/(4 - x^2))^(-1/2)</c> -- and asked as the
/// same question, as the sign taken out above is.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveByWritingAHalfOddPowerOfAReciprocalWithItsSign(Entity expr, Entity.Variable x, bool integrateByParts)
{
foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
{
if (underneath || factor is not Powf(var @base, Number.Rational exponent)
|| !exponent.ERational.Denominator.Equals(EInteger.FromInt32(2)) || !@base.ContainsNode(x))
continue;
var (above, below) = Functions.SingleQuotient.Of(@base);
if (below == Number.Integer.One || above.ContainsNode(x) || !below.ContainsNode(x)
|| !TreeAnalyzer.TryGetPolynomial(below, x, out var read) || read.Count == 0
|| read.Keys.Max()!.CompareTo(EInteger.FromInt32(4)) > 0 || IsPositiveForReal(below, x))
continue;
var positive = above.Evaled is Number.Real { IsPositive: true };
var inFront = positive ? MathS.Signum(below) : factor * MathS.Pow(below, exponent);
var replacement = positive ? MathS.Pow(above, exponent) * MathS.Pow(below, -exponent) : MathS.Pow(below, -exponent);
var written = expr.Replace(node => node == factor ? replacement : node);
return Integration.ComputeAsTheSameQuestion(written, x, integrateByParts) is { } answer ? inFront * answer : null;
}
return null;
}

/// <summary>
/// <c>x^(n - 1) g(x^n)</c> with a symbolic <c>n</c>, which is <c>g(u)/n</c> under
/// <c>u = x^n</c>: the power in front is the derivative of the power inside up to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// A rational function with symbols in it beside a root of a linear, split into partial
// fractions first, each term the question of one factor beside the root.
if ((answer = IndefiniteIntegralSolver.SolveARationalFunctionBesideARootOfALinearSplitFirst(expr, x, integrateByParts)) is { }) return answer;
// A half-odd power of a constant over a polynomial of either sign, written apart with
// its sign in front. After the substitutions, which answer some of these without it.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAHalfOddPowerOfAReciprocalWithItsSign(expr, x, integrateByParts)) is { }) return answer;
// The sign of a real-valued factor is constant between its zeros, and goes in
// front of the antiderivative of the rest. After every rule that reads the sign
// where it stands: `cos(x) sgn(sin(x))` is `|sin(x)|` by the substitution, and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// 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 half-odd power of a constant over a polynomial whose sign changes, written apart with the
/// factor that keeps it right on both sides of the polynomial's zeros: <c>(A/Q)^r</c> is
/// <c>(A/Q)^r Q^r</c> times <c>Q^(-r)</c>, and for a positive number <c>A</c> that factor is
/// <c>A^r sgn(Q)</c>. <c>sqrt(1/(1 - x^2))</c>, the arcsine's derivative where it is real, was
/// declined.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
/// <remarks>
/// Checked by differentiating back with <c>a = 2.3</c>, <c>b = -0.7</c>, <c>c = 1.3</c>, so that
/// <c>a + b x^2</c> changes sign as well, at points on both sides of every denominator's zeros;
/// where the denominator is negative the integrand is imaginary, and the two are compared as
/// complex numbers.
/// </remarks>
[Trait("Area", "Calculus")]
public sealed class HalfOddPowerOfAReciprocalIntegralTest
{
[Theory]
[InlineData("sqrt(1/(1 - x^2))")]
[InlineData("sqrt(1/(x^2 - 1))")]
[InlineData("(1 - x^2)*sqrt(1/(2 - x^2))")]
[InlineData("(1/(1 - x^2))^(3/2)")]
[InlineData("(c/(a + b*x^2))^(3/2)")]
[InlineData("x^2*(c/(a + b*x^2))^(3/2)")]
public void IsWrittenApartWithItsSign(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Entity Pinned(Entity e) => e.Substitute("a", 2.3).Substitute("b", -0.7).Substitute("c", 1.3);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
foreach (var at in new[] { -2.4, -1.6, -0.6, 0.6, 1.6, 2.4 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.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) + Math.Abs((double)want.ImaginaryPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}
}
}
Loading