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 @@ -570,6 +570,20 @@ antiderivative on each side of it, as Rubi's is
| `"1/((2 + 3*x^2)^(1/4)*(4 + 3*x^2))".ToEntity().Integrate("x")` | `integral(...)` | the same in `(2 + 3 x^2)^(1/4)` |
| `"1/((-2 + 3*x^2)*(-1 + 3*x^2)^(1/4))".ToEntity().Integrate("x")` | `integral(...)` | `-(arctan(u) + artanh(u))/(2 sqrt(6))`, `u = sqrt(3) x/(sqrt(2) (-1 + 3 x^2)^(1/4))` |

### A power of `csc(x) - sin(x)` or `sec(x) - cos(x)` is integrated as sine and cosine powers

**Answers where there were none.** `(csc(x) - sin(x))^(5/2)` was declined, with the rest of Rubi's
4.7.7 to a power that is not whole of either difference, either way up. `csc(z) - sin(z)` is
`cos(z)^2/sin(z)` and `sec(z) - cos(z)` is `sin(z)^2/cos(z)`; written so, the power is a product of
powers of the sine and cosine, which are integrated through the tangent, a quotient of whole powers
of the six functions read inside a power as it is
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(csc(x) - sin(x))^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | powers of `sin(x)` times `(cos(x)^2/sin(x))^(5/2)` |
| `"1/(sec(x) - cos(x))^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | a root of `cos(x)` with a logarithm and an arctangent, times `(sin(x)^2/cos(x))^(-3/2)` |

### A product of powers of the trigonometric functions, some not whole, is integrated as sine and cosine powers

**Answers where there were none.** `(a sin(x))^(5/2) sqrt(b sec(x))` was declined, and so were
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9090,33 +9090,38 @@ private static bool TryReadAHomogeneousTrigonometricPolynomial(
var (@base, power) = factor is Powf(var b, var p) && p.Evaled is Number.Rational r
? (b, r)
: (factor, (Number.Rational)Number.Integer.One);
// A constant multiple of a function, and a whole power of one inside the power:
// `(b tan(z)^3)^(3/2)`.
if (@base is Mulf(var left, var right))
@base = !left.ContainsNode(x) ? right : !right.ContainsNode(x) ? left : @base;
if (@base is Powf(var inner, Number.Integer innerPower))
(@base, power) = (inner, (Number.Rational)(power * innerPower));
if (underneath)
power = (Number.Rational)(-power);
if (power is not Number.Integer)
notWhole = true;
// The powers of the sine and of the cosine each function is.
(Entity Of, int OnSine, int OnCosine)? read = @base switch
{
Sinf(var of) => (of, 1, 0),
Cosf(var of) => (of, 0, 1),
Tanf(var of) => (of, 1, -1),
Cotanf(var of) => (of, -1, 1),
Secantf(var of) => (of, 0, -1),
Cosecantf(var of) => (of, -1, 0),
_ => null
};
if (read is null || argument is not null && argument != read.Value.Of)
// Inside the power, a constant multiple of a product and quotient of whole powers of
// the functions: `(b tan(z)^3)^(3/2)`, and `(cos(z)^2/sin(z))^(5/2)`, which is how
// `(csc(z) - sin(z))^(5/2)` is written. Not whole where some function's power is not:
// `(a csc(z)^2)^(-7/2)` is a whole power of the modulus of the cosecant, which the rule for
// an even power under a root answers with its sign.
if (!TryReadAMonomialInTheTrigonometricFunctions(@base, x, out var parts))
return null;
var (functionOf, onSine, onCosine) = read.Value;
argument = functionOf;
sine = (Number.Rational)(sine + power * onSine);
cosine = (Number.Rational)(cosine + power * onCosine);
foreach (var (function, times) in parts)
{
// The powers of the sine and of the cosine each function is.
(Entity Of, int OnSine, int OnCosine)? read = function switch
{
Sinf(var of) => (of, 1, 0),
Cosf(var of) => (of, 0, 1),
Tanf(var of) => (of, 1, -1),
Cotanf(var of) => (of, -1, 1),
Secantf(var of) => (of, 0, -1),
Cosecantf(var of) => (of, -1, 0),
_ => null
};
if (read is null || argument is not null && argument != read.Value.Of)
return null;
var (functionOf, onSine, onCosine) = read.Value;
argument = functionOf;
var weight = (Number.Rational)(power * Number.Integer.Create(times));
if (weight is not Number.Integer)
notWhole = true;
sine = (Number.Rational)(sine + weight * onSine);
cosine = (Number.Rational)(cosine + weight * onCosine);
}
}
if (argument is null || !notWhole
|| !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || TreeAnalyzer.IsZero(slope))
Expand Down Expand Up @@ -9150,6 +9155,87 @@ private static bool TryReadAHomogeneousTrigonometricPolynomial(
}


/// <summary>
/// A difference of a trigonometric function and its reciprocal's partner written as the
/// quotient it is: <c>csc(z) - sin(z)</c> is <c>cos(z)^2/sin(z)</c> and <c>sec(z) - cos(z)</c>
/// is <c>sin(z)^2/cos(z)</c>, so that a power of either is a product of powers of the sine
/// and cosine; the integrand so written is asked as the same question.
/// </summary>
/// <remarks>
/// <c>(csc(x) - sin(x))^(5/2)</c> was declined, with the rest of Rubi's 4.7.7 to a power that
/// is not whole of either difference, either way up. As the quotient it is a product of
/// powers of the sine and cosine, which the rule beside it reads through the tangent.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveByWritingADifferenceOfReciprocalFunctionsAsAQuotient(Entity expr, Entity.Variable x, bool integrateByParts)
{
var rewritten = expr.Replace(node =>
{
if (!node.ContainsNode(x))
return node;
// Each term a signed function: `csc(z)`, `-sin(z)`, `-1 * cos(z)`.
(Entity Function, int Sign) Signed(Entity term, int sign) => term switch
{
Mulf(Number.Integer { EInteger: var one }, var f) when one.Equals(EInteger.FromInt32(-1)) => (f, -sign),
Mulf(var f, Number.Integer { EInteger: var one }) when one.Equals(EInteger.FromInt32(-1)) => (f, -sign),
_ => (term, sign)
};
List<(Entity Function, int Sign)>? signed = node switch
{
Minusf(var minuend, var subtrahend) => new() { Signed(minuend, 1), Signed(subtrahend, -1) },
Sumf(var left, var right) when left is not (Sumf or Minusf) && right is not (Sumf or Minusf) => new() { Signed(left, 1), Signed(right, 1) },
_ => null
};
if (signed is null)
return node;
var (first, second) = (signed[0], signed[1]);
if (first.Sign == second.Sign)
return node;
var (positive, negative) = first.Sign > 0 ? (first.Function, second.Function) : (second.Function, first.Function);
return (positive, negative) switch
{
(Cosecantf(var a), Sinf(var b)) when a == b => MathS.Pow(MathS.Cos(a), 2) / MathS.Sin(a),
(Sinf(var a), Cosecantf(var b)) when a == b => -MathS.Pow(MathS.Cos(a), 2) / MathS.Sin(a),
(Secantf(var a), Cosf(var b)) when a == b => MathS.Pow(MathS.Sin(a), 2) / MathS.Cos(a),
(Cosf(var a), Secantf(var b)) when a == b => -MathS.Pow(MathS.Sin(a), 2) / MathS.Cos(a),
_ => node
};
});
return rewritten == expr ? null : Integration.ComputeAsTheSameQuestion(rewritten, x, integrateByParts);
}

/// <summary>
/// <paramref name="expr"/> read as a constant times a product of whole powers of the six
/// trigonometric functions, each with how many times it divides or multiplies; false where
/// anything else holds <paramref name="x"/>.
/// </summary>
private static bool TryReadAMonomialInTheTrigonometricFunctions(Entity expr, Entity.Variable x, out List<(Entity Function, int Times)> parts)
{
var read = new List<(Entity Function, int Times)>();
parts = read;
return Read(expr, 1);

bool Read(Entity e, int times)
{
switch (e)
{
case var constant when !constant.ContainsNode(x):
return true;
case Mulf(var left, var right):
return Read(left, times) && Read(right, times);
case Divf(var above, var below):
return Read(above, times) && Read(below, -times);
case Powf(var inner, Number.Integer { EInteger: var power }) when power.CanFitInInt32():
return Read(inner, times * power.ToInt32Unchecked());
case Sinf or Cosf or Tanf or Cotanf or Secantf or Cosecantf:
read.Add((e, times));
return true;
default:
return false;
}
}
}

/// <summary>
/// <paramref name="factors"/> with a whole power of a product read as the product of the
/// powers, a negative one on the other side of the bar: <c>1/(cos(x)^(7/2) sqrt(sin(x)))</c>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// A power of the cotangent that is not whole beside the tangent, written as the
// tangent's with the constant that takes in front, which the substitution below reads.
if ((answer = IndefiniteIntegralSolver.SolveByWritingAPowerOfTheCotangentInTheTangent(expr, x, integrateByParts)) is { }) return answer;
// A difference of a function and its reciprocal's partner, `csc(z) - sin(z)`, as the
// quotient of powers it is, for the rule after it.
if ((answer = IndefiniteIntegralSolver.SolveByWritingADifferenceOfReciprocalFunctionsAsAQuotient(expr, x, integrateByParts)) is { }) return answer;
// A product of powers of the trigonometric functions of one argument, some not whole,
// whose powers of the sine and cosine add up to an even number: the tangent's form
// times a constant, which the substitution below reads.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// 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 that is not whole of <c>csc(x) - sin(x)</c> or <c>sec(x) - cos(x)</c>, which are
/// <c>cos(x)^2/sin(x)</c> and <c>sin(x)^2/cos(x)</c>: written so, a product of powers of the
/// sine and cosine, read through the tangent. Rubi's 4.7.7.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
/// <remarks>
/// Compared as complex numbers on both sides of the zeros of the sine and the cosine, where the
/// integrands are complex on some of the intervals.
/// </remarks>
[Trait("Area", "Calculus")]
public sealed class DifferenceOfReciprocalFunctionsIntegralTest
{
[Theory]
[InlineData("(csc(x) - sin(x))^(5/2)")]
[InlineData("(csc(x) - sin(x))^(3/2)")]
[InlineData("1/(csc(x) - sin(x))^(1/2)")]
[InlineData("1/(csc(x) - sin(x))^(7/2)")]
[InlineData("(sec(x) - cos(x))^(5/2)")]
[InlineData("1/(sec(x) - cos(x))^(3/2)")]
[InlineData("(sin(x) - csc(x))^(3/2)")]
public void IsAProductOfPowersOfTheSineAndCosine(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
var derivative = integral.Substitute("C", 0).Differentiate("x");
var original = integrand.ToEntity();
foreach (var at in new[] { -2.3, -1.1, -0.4, 0.4, 1.1, 1.9, 2.6 })
{
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