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 @@ -505,6 +505,21 @@ two more of Rubi's 4.5.2.3. The two sums multiply to `-a c tan^2`, so in `u = a
| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*(c - c*sec(e + f*x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | 469 characters |
| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*sqrt(c - c*sec(e + f*x))".ToEntity().Integrate("x")` | `integral(...)` | 268 characters |

### Powers of `a + a sec` and `c - c sec` that add to a negative whole number are integrated in the first sum

**Answers where there were none.** `sec(e + f x) (a + a sec(e + f x))^m (c - c sec(e + f x))^(-1 - m)` was declined,
with two more of Rubi's 4.5.2.3. When the two powers add to `-k`, a whole number up to 12, the integral is the
integrand over the derivative of `u = a + a sec` times a polynomial in `u` and `a - a sec`, of degree `k + 1`, for a
symbolic `m` too, and the cosecant's is the same
([#718](https://github.com/asc-community/AngouriMath/issues/718), PR
[#1861](https://github.com/asc-community/AngouriMath/pull/1861)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*(c - c*sec(e + f*x))^(-1 - m)".ToEntity().Integrate("x")` | `integral(...)` | 245 characters |
| `"sec(e + f*x)*(a + a*sec(e + f*x))^m*(c - c*sec(e + f*x))^(-3 - m)".ToEntity().Integrate("x")` | `integral(...)` | 402 characters |
| `"csc(x)*(2 + 2*csc(x))^m*(3 - 3*csc(x))^(-2 - m)".ToEntity().Integrate("x")` | `integral(...)` | 233 characters |

### A secant and a tangent in one sum below the bar are integrated in the half angle

**Answers where there were none.** `1/(a + c sec(x) + b tan(x))` was declined, and so was
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5223,7 +5223,7 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
var order = sums[0].Exponent is Number.Rational { ERational.Sign: > 0 } && IsHalfOdd(sums[0].Exponent) ? new[] { 0, 1 } : new[] { 1, 0 };
var (half, other) = (sums[order[0]], sums[order[1]]);
if (!IsHalfOdd(half.Exponent))
return null;
return WherePowersOfConjugateSumsAddToANegativeWhole(expr, x, function, sums[0], sums[1]);
var n = ((Number.Rational)half.Exponent).ERational;
// With u the other sum, the half-odd one is c (2 - u/a), whatever the signs.
var u = Variable.CreateUnique(expr, "u_conjugate");
Expand All @@ -5246,6 +5246,45 @@ static bool IsHalfOdd(Entity exponent) => exponent is Number.Rational rational a
return expr * inTermsOfU.Substitute(u, other.Radicand) / inX;
}

/// <summary>
/// <c>sec(y) (a ± a sec(y))^m (c ∓ c sec(y))^n</c> with <c>m + n</c> a negative whole
/// number <c>-k</c> and neither half-odd: with <c>u = a ± a sec(y)</c>, the antiderivative is
/// the integrand over <c>u'</c> times the polynomial
/// <c>(2a)^(-k) Σ_j C(k - 1, j) u^(j + 1) (2a - u)^(k - j)/(m + j + 1/2)</c>, for any <c>m</c>.
/// </summary>
/// <remarks>
/// Rubi's <c>sec(e + f x) (a + a sec(e + f x))^m (c - c sec(e + f x))^(-1 - m)</c> and its
/// <c>-2 - m</c> and <c>-3 - m</c>, 4.5.2.3, were declined. In <c>u</c> the integrand is
/// <c>u^(m - 1/2) (2 - u/a)^(n - 1/2)</c> up to a constant, which is <c>v^(m - 1/2) (1 + v)^(k - 1)</c>
/// in <c>v = u/(2a - u)</c>, a finite sum; written back, the powers of <c>u</c> and
/// <c>2a - u</c> the integrand has divide out and leave the polynomial. For <c>k = 1</c> that is
/// <c>-tan(y) (a + a sec(y))^m (c - c sec(y))^(-1 - m)/(2m + 1)</c>. Kept only where it
/// differentiates back to the integrand at the sampled points.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
private static Entity? WherePowersOfConjugateSumsAddToANegativeWhole(Entity expr, Entity.Variable x, Entity function,
(Entity Radicand, Entity Exponent, Entity A, bool Plus) first, (Entity Radicand, Entity Exponent, Entity A, bool Plus) second)
{
// Simplified, not InnerSimplified, which leaves `m + -1 - m` as it is.
if ((first.Exponent + second.Exponent).Simplify() is not Number.Integer { EInteger.Sign: < 0 } total
|| total.EInteger.CompareTo(EInteger.FromInt32(-12)) < 0)
return null;
var k = -total.EInteger.ToInt32Checked();
var (u, a) = (first.Radicand, first.A);
// `2a - u`, written as the conjugate sum: `a - a sec(y)` for `u = a + a sec(y)`.
var conjugate = first.Plus ? a - a * function : a + a * function;
Entity polynomial = Number.Integer.Zero;
var binomial = EInteger.One;
for (var j = 0; j < k; j++)
{
polynomial += Number.Integer.Create(binomial) * MathS.Pow(u, j + 1) * MathS.Pow(conjugate, k - j)
/ (first.Exponent + j + Number.Rational.Create(1, 2)).Simplify();
binomial = binomial.Multiply(EInteger.FromInt32(k - 1 - j)).Divide(EInteger.FromInt32(j + 1));
}
var antiderivative = expr / u.Differentiate(x) * (MathS.Pow(2 * a, total) * polynomial).InnerSimplified;
return Functions.PartialFractions.HoldsAtSampledPoints(antiderivative.Differentiate(x), expr, x) ? antiderivative : null;
}

/// <summary>
/// An odd whole power of <c>tan(y)</c> beside a function of <c>sec(y)</c>, integrated in
/// <c>u = sec(y)</c>: <c>du = sec(y) tan(y) dy</c> and the even power of the tangent left over
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>
/// <c>sec(y) (a + a sec(y))^m (c - c sec(y))^n</c> with <c>m + n</c> a negative whole number,
/// for any <c>m</c>: Rubi's 4.5.2.3.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ConjugateSecantSumsWhosePowersAddToANegativeWholeTest
{
// The two sums multiply to -a c tan(y)^2, so they have one sign only where a and c do not:
// with a = 2 and c = -3 the first rows are real where sec(y) > 1. The rest are complex
// there, and are checked off the real line the same way.
[Theory]
[InlineData("sec(e+f*x)*(a+a*sec(e+f*x))^m*(c-c*sec(e+f*x))^(-1-m)", 300, true)]
[InlineData("sec(e+f*x)*(a+a*sec(e+f*x))^m*(c-c*sec(e+f*x))^(-2-m)", 400, true)]
[InlineData("sec(e+f*x)*(a+a*sec(e+f*x))^m*(c-c*sec(e+f*x))^(-3-m)", 500, true)]
[InlineData("sec(x)*(2+2*sec(x))^(5/3)*(3-3*sec(x))^(-8/3)", 200, false)]
[InlineData("sec(x)*(1-sec(x))^m*(1+sec(x))^(-1-m)", 200, false)]
[InlineData("csc(x)*(2+2*csc(x))^m*(3-3*csc(x))^(-2-m)", 300, false)]
public void DifferentiatesBack(string integrand, int atMost, bool real)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < atMost, $"{text.Length} characters of answer for {integrand}");
Entity At(Entity expression) => expression.Substitute("C", 0).Substitute("m", "1/3".ToEntity())
.Substitute("a", 2).Substitute("c", -3).Substitute("e", "1/5".ToEntity()).Substitute("f", "3/2".ToEntity());
var derivative = At(integral).Differentiate("x");
var original = At(integrand.ToEntity());
// Points where sec (or csc) of the argument is past 1: x and 1/5 + 3/2 x in (0, pi/2).
foreach (var at in integrand.StartsWith("csc") ? new[] { "1.1", "1.3", "1.5" } : new[] { "0.3", "0.6", "0.9" })
{
var want = original.Substitute("x", at.ToEntity()).EvalNumerical();
if (real)
Assert.True(Math.Abs((double)want.ImaginaryPart) < 1e-12, $"{integrand} is not real at x = {at}");
var got = derivative.Substitute("x", at.ToEntity()).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