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
17 changes: 17 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,23 @@ written in `u = c + d x` first. Rubi's 1.1.3.2, 1.2.3.2 and 1.3.1 write whole se
| `"(c*m + d*m*x)^3/(a + b*(c + d*x)^3)^2".ToEntity().Integrate("x")` | `integral(...)` | `m^3` times a rational function of `c + d x`, the logarithms and the arctangent |
| `"(c + d*x)^4/(a + b*(c + d*x)^2 + k*(c + d*x)^4)^2".ToEntity().Integrate("x")` | `integral(...)` | a rational function of `c + d x` and arctangents |

### A trigonometric function times its reciprocal is integrated

**Answers where there were none, and shorter ones where 2.5.0 had them.** `sin(x) csc(x)` and
`tan(x) cot(x)` were declined: the rule for products of powers of the six functions reads them as
powers of the sine and cosine, and refused a product whose powers cancel. That product is the
constant wherever it is defined, and its integral the constant times `x`. `cos(x)^2 sec(x)^2`,
`csc(x)^2 (1 + sin(x)^2)` and Rubi's 4.7.7 `(1 + cos(x)^2) sec(x)^2`, which 2.5.0 answered at length
and the unreleased master declined, are answered again, more briefly
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sin(x)*csc(x)".ToEntity().Integrate("x")` | `integral(...)` | `x` |
| `"tan(x)*cot(x)".ToEntity().Integrate("x")` | `integral(...)` | `x` |
| `"cos(x)^2*sec(x)^2".ToEntity().Integrate("x")` | `cos(x)^2 tan(x) + x - sin(2x)/2` | `x` |
| `"(1 + cos(x)^2)*sec(x)^2".ToEntity().Integrate("x")` | `cos(x)^2 tan(x) + x - sin(2x)/2 + tan(x)` | `x + tan(x)` |

### A root of a square inside a sum is integrated on each side of the square's zero

**Answers where there were none.** `1/(1 + (x^2)^(3/2))` is `1/(1 + x^3)` for a positive `x` and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4428,11 +4428,17 @@ private static (Entity Constant, bool Plus, bool IsSine)? ReadAsOnePlusMinusAFun
{
if (!asked && !Integration.AnsweringTheQuestionAsked)
return null;
if (!TryReadSineCosinePowers(expr, out var argument, out var sinePower, out var cosinePower, out var factor, out var throughAProduct))
if (!TryReadSineCosinePowers(expr, out var argument, out var sinePower, out var cosinePower, out var factor, out var throughAProduct,
aConstantProduct: true))
return null;
if (!TreeAnalyzer.TryGetPolyLinear(argument, x, out var rate, out _) || rate.Evaled == 0)
return null;
if (IntegrateAPowerOfSineTimesAPowerOfCosine(argument, sinePower, cosinePower, factor, rate) is not { } integral)
// Powers that cancel, `cos(x)^2 sec(x)^2`, are the constant wherever the integrand is
// defined, and its integral is the constant times x.
var integral = sinePower.IsZero && cosinePower.IsZero
? factor * x
: IntegrateAPowerOfSineTimesAPowerOfCosine(argument, sinePower, cosinePower, factor, rate);
if (integral is null)
return null;
if (!throughAProduct)
return integral;
Expand Down Expand Up @@ -7580,8 +7586,14 @@ private static bool TryReadSineCosinePowers(
/// and whether a fractional power of a product was read as the product of the powers:
/// true only up to a constant on each interval where the factors keep their signs.
/// </summary>
/// <remarks>
/// A product whose powers cancel, <c>sin(u) csc(u)</c>, is read only where
/// <paramref name="aConstantProduct"/> asks for it: it is the constant where it is
/// defined, and the readers that go on to divide by a power have nothing to divide.
/// </remarks>
private static bool TryReadSineCosinePowers(
Entity expr, out Entity argument, out ERational sinePower, out ERational cosinePower, out Entity factor, out bool throughAProduct)
Entity expr, out Entity argument, out ERational sinePower, out ERational cosinePower, out Entity factor, out bool throughAProduct,
bool aConstantProduct = false)
{
Entity? common = null;
var sine = ERational.Zero;
Expand All @@ -7594,7 +7606,7 @@ private static bool TryReadSineCosinePowers(
cosinePower = cosine;
factor = constant;
throughAProduct = distributed;
return read && common is not null && !(sine.IsZero && cosine.IsZero);
return read && common is not null && (aConstantProduct || !(sine.IsZero && cosine.IsZero));

bool Agrees(Entity candidate)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// 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 product of trigonometric powers whose powers of the sine and cosine cancel,
/// <c>cos(x)^2 sec(x)^2</c>, is the constant wherever it is defined, and its integral the
/// constant times x: it was declined, and with it <c>(1 + cos(x)^2) sec(x)^2</c>, Rubi's 4.7.7.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class TrigonometricTimesItsReciprocalIntegralTest
{
[Theory]
[InlineData("sin(x)*csc(x)")]
[InlineData("cos(x)^2*sec(x)^2")]
[InlineData("tan(x)*cot(x)")]
[InlineData("(1 + cos(x)^2)*sec(x)^2")]
[InlineData("csc(x)^2*(1 + sin(x)^2)")]
public void IsTheConstantTimesX(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, 2.3 })
{
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}");
}
}

/// <summary>The constant times x itself, not a function that is x only on one interval.</summary>
[Theory]
[InlineData("cos(x)^2*sec(x)^2")]
[InlineData("tan(x)*cot(x)")]
public void IsX(string integrand)
=> Assert.Equal(MathS.Var("x"), integrand.ToEntity().Integrate("x").Substitute("C", 0).InnerSimplified);
}
}
Loading