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
32 changes: 32 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3679,6 +3679,38 @@ of the six functions read inside a power as it is
| `"(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)` |

### Fractional trigonometric powers two apart, and a sine raised from nothing, are integrated as derivatives

**Answers where there were none.** Rubi's trigonometric integrands of these shapes were declined or ran out of time
([#233](https://github.com/asc-community/AngouriMath/issues/233)):
- A sum of powers of one sine, cosine, secant or cosecant of a linear, the powers not whole and two apart, by the
reduction formula from the highest down: `(cos sin^(n - 1))' = (n - 1) sin^(n - 2) - n sin^n`, and its kind for the
others. Each power alone is elliptic, so split, `(3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x))` was two declined
integrals. Beside x, by parts once: `x/csc(c + d x)^(3/2) - x sqrt(csc(c + d x))/3`. The written function is kept.
- The derivative of a sine raised from nothing times powers of the cosine and sums in it, or of a cosine beside the
sine: `(a + a cos(x))^(3/2)/cos(x)^(5/4)` is `(4 a^2 sin(x)/(cos(x)^(1/4) sqrt(a + a cos(x))))'`. Beside x, by parts
once: `x (b + a sin(x))/(a + b sin(x))^2` is `x G - integral G` for `G = -cos(x)/(a + b sin(x))`.
- A cosine beside a function of the sine with a power of it that is not rational, in `s = sin(x)`:
`cos(x)/(sin(x) + sin(x)^sqrt(2))` is `1/(s + s^sqrt(2))`.
- `(c sin(a + b x)^n)^(1/n)` is `K sin(a + b x)` with `K = (c sin(a + b x)^n)^(1/n)/sin(a + b x)`, constant between the
zeros of the sine, and so for the cosine.
- Under the homogeneous substitution, factors written alike on both sides are cancelled where the quotient was
simplified: `sin(c + d x)^6/(a + b sin(c + d x)^2)^2`. Under the tangent, a whole power of a product is distributed
over its factors: `cot(c + d x)^2/(a + b sec(c + d x)^2)^2`. And beside a root of `1 - cos(x)`, the root of
`1 - cos(x)^2` is the product of the roots of `1 - cos(x)` and `1 + cos(x)`: `sqrt(1 - cos(x))/sqrt(a - cos(x))`.

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(3+3*sec(c+d*x)^2)/sec(c+d*x)^(1/2)".ToEntity().Integrate("x")` | `integral(...)` | 60 characters |
| `"x/csc(c+d*x)^(3/2)-1/3*x*sqrt(csc(c+d*x))".ToEntity().Integrate("x")` | `integral(...)` | 131 characters |
| `"(a+a*cos(c+d*x))^(3/2)/cos(c+d*x)^(5/4)".ToEntity().Integrate("x")` | `integral(...)` | 96 characters |
| `"x*(b+a*sin(x))/(a+b*sin(x))^2".ToEntity().Integrate("x")` | `integral(...)` | 64 characters |
| `"cos(x)/(sin(x)+sin(x)^sqrt(2))".ToEntity().Integrate("x")` | `integral(...)` | 86 characters |
| `"(c*sin(a+b*x)^n)^(1/n)".ToEntity().Integrate("x")` | `integral(...)` | 77 characters |
| `"sin(c+d*x)^6/(a+b*sin(c+d*x)^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 2,169 characters |
| `"cot(c+d*x)^2/(a+b*sec(c+d*x)^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 1,963 characters |
| `"sqrt(1-cos(x))/sqrt(a-cos(x))".ToEntity().Integrate("x")` | `integral(...)` | 70 characters |

### 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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,17 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A symbolic power of a sine, cosine, secant or cosecant times `alpha + beta f^2`, where it is
// the derivative of `cos sin^(m + 1)`, `sin cos^(m + 1)`, `tan sec^m` or `cot csc^m`.
if ((answer = IndefiniteIntegralSolver.SolveASymbolicPowerOfATrigonometricFunctionAsTheDerivativeOfAProduct(expr, x)) is { }) return answer;
// And numbers not whole for the exponents, two apart, by the reduction from the highest
// down: split, `1/csc(u)^(3/2) - sqrt(csc(u))/3` is two elliptic integrals.
if ((answer = IndefiniteIntegralSolver.SolveFractionalPowersOfATrigonometricFunctionTwoApartByTheReduction(expr, x)) is { }) return answer;
// And the derivative of a sine raised from nothing times powers of the cosine and sums in it,
// or of a cosine beside the sine, alone or beside x by parts: the half angle declined
// `(a + a cos(u))^(3/2)/cos(u)^(5/4)`, and parts integrated `x (b + a sin(x))/(a + b sin(x))^2`'s
// derivative into a function of tan(x/2) and ran out of time.
if ((answer = IndefiniteIntegralSolver.SolveAsTheDerivativeOfASineRaisedFromNothingBesidePowersOfTheCosine(expr, x, integrateByParts)) is { }) return answer;
// A cosine beside a function of the sine with a power off the rationals in it, in the sine:
// the substitution search declined `cos(x)/(sin(x) + sin(x)^sqrt(2))`.
if ((answer = IndefiniteIntegralSolver.SolveACosineBesideAFunctionOfTheSineWithAnIrrationalPowerInIt(expr, x, integrateByParts)) is { }) return answer;
// A power of a sine, cosine or hyperbolic one over a binomial in a power of it, over the
// binomial's roots, before the rules that spent the budget on `sinh(y)^4/(a + b sinh(y)^3)`.
if ((answer = IndefiniteIntegralSolver.SolveAPowerOverABinomialInAPowerOfASineOverItsRoots(expr, x)) is { }) return answer;
Expand Down Expand Up @@ -1096,6 +1107,9 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A fractional power of a constant times an even power of a function, the function
// taken out with its sign: `(a sin(x)^2)^(5/2)` is `a^(5/2) sgn(sin(x)) sin(x)^5`.
if ((answer = IndefiniteIntegralSolver.SolveByTakingAFunctionOutOfAPowerOfItsEvenPower(expr, x, integrateByParts)) is { }) return answer;
// And a symbolic root of a power, `(c sin(u)^n)^(1/n)`, the function out and the rest a
// constant written back.
if ((answer = IndefiniteIntegralSolver.SolveByTakingAFunctionOutOfASymbolicRootOfItsPower(expr, x, integrateByParts)) is { }) return answer;
// A sum of two roots below the bar, multiplied above and below by its conjugate:
// `1/(sqrt(A) - sqrt(B))` is `(sqrt(A) + sqrt(B))/(A - B)`.
if ((answer = IndefiniteIntegralSolver.SolveByRationalisingASumOfRoots(expr, x, integrateByParts)) is { }) return answer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// 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 cosine beside a function of the sine with a power of it off the rationals, integrated in the
/// sine, and a sine beside the cosine the same: Rubi's 4.7.7 <c>cos(x)/(sin(x) + sin(x)^sqrt(2))</c>.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ACosineBesideAFunctionOfTheSineWithAnIrrationalPowerIntegralTest
{
[Theory]
[InlineData("cos(x)/(sin(x)+sin(x)^sqrt(2))")]
[InlineData("cos(c+d*x)/(sin(c+d*x)+sin(c+d*x)^p)")]
[InlineData("sin(c+d*x)*cos(c+d*x)^pi*(1+cos(c+d*x))")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Entity Pinned(Entity e) => e.Substitute("c", 0.3).Substitute("d", 0.75).Substitute("p", 2.5);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
if (want.IsNaN)
continue;
compared++;
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}");
}
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// 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 symbolic root of a constant times a power of a function, <c>(c sin(u)^n)^(1/n)</c>, as the
/// function times a factor constant between its zeros, written back: Rubi's 4.1.7 and 4.2.0.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AFunctionOutOfASymbolicRootOfItsPowerIntegralTest
{
[Theory]
[InlineData("(c*sin(a+b*x)^n)^(1/n)")]
[InlineData("(c*cos(a+b*x)^m)^(1/m)")]
[InlineData("x*(c*cos(x)^m)^(1/m)")]
[InlineData("(c*sin(x)^n)^(2/n)")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Entity Pinned(Entity e) => e.Substitute("a", 0.3).Substitute("b", 0.75).Substitute("c", 1.3).Substitute("n", 2.5).Substitute("m", 0.4);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
if (want.IsNaN)
continue;
compared++;
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}");
}
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
}

[Fact]
public void LeavesAPowerOfXToTheRulesForBinomials()
{
// (c x^n)^(1/n) is the rule's for a symbolic power of a monomial: `x (c x^n)^(1/n)/2`,
// whose derivative is `(c x^n)^(1/n)/2 + x (c x^n)^(1/n)/(2 x)`, the integrand on every
// branch, since d/dx (c x^n)^(1/n) is (c x^n)^(1/n)/x on every one.
var integral = "(c*x^n)^(1/n)".ToEntity().Integrate("x").Stringize();
Assert.Equal("x * (c * x ^ n) ^ (1 / n) / 2 + C", integral);
}
}
}
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 quotient of sines and cosines with symbols in it, written in the tangent with the factors alike
/// on both sides of the bar cancelled rather than simplified: Rubi's 4.1.7 and 4.5.7, whose
/// simplification ran past the budget.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AQuotientInTheTangentWithItsSymbolsCancelledIntegralTest
{
[Theory]
[InlineData("sin(c+d*x)^6/(a+b*sin(c+d*x)^2)^2")]
[InlineData("csc(c+d*x)^2/(a-b*sin(c+d*x)^4)^2")]
[InlineData("cot(c+d*x)^2/(a+b*sec(c+d*x)^2)^2")]
[InlineData("sin(x)^4/(a+b*sin(x)^2)^2")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.3).Substitute("d", 0.75);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
if (want.IsNaN)
continue;
compared++;
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}");
}
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// 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>
/// The derivative of a sine raised from nothing times powers of the cosine, the secant and sums
/// in them, or of a cosine beside the sine, alone or beside x by parts: Rubi's 4.2.2.1, 4.5.1.2,
/// 4.5.4.2 and 4.7.7.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ASineRaisedFromNothingBesidePowersOfTheCosineIntegralTest
{
// Points where the cosine is negative too, where the integrand is complex and the answer's
// principal powers still differentiate to it.
[Theory]
[InlineData("(a+a*cos(c+d*x))^(3/2)/cos(c+d*x)^(5/4)")]
[InlineData("(a+a*cos(c+d*x))^(3/2)*sec(c+d*x)^(5/4)")]
[InlineData("(a+a*sec(c+d*x))^(3/2)/sec(c+d*x)^(1/4)")]
[InlineData("(a+a*sec(c+d*x))^n*(-a*A*n-a*Q*(1+n)*sec(c+d*x))/(a*(1+n)*sec(c+d*x)^n)+sec(c+d*x)^(-1-n)*(a+a*sec(c+d*x))^n*(A+Q*sec(c+d*x)^2)")]
[InlineData("x*(b+a*sin(x))/(a+b*sin(x))^2")]
[InlineData("x*(b+a*cos(c+d*x))/(a+b*cos(c+d*x))^2")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.3).Substitute("d", 0.75)
.Substitute("A", 0.6).Substitute("Q", 1.25).Substitute("n", 0.4);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -2.3, -1.1, -0.7, 0.4, 1.9, 2.8 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
if (want.IsNaN)
continue;
compared++;
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}");
}
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
}

[Fact]
public void IsTheRaisedProductInClosedForm()
{
// 4 a^2 sin/(cos^(1/4) sqrt(a + a cos)), Rubi's, with no half angle in it.
var integral = "(a+a*cos(x))^(3/2)/cos(x)^(5/4)".ToEntity().Integrate("x").Stringize();
Assert.DoesNotContain("x / 2", integral);
Assert.Contains("cos(x) ^ (-1/4)", integral);
}
}
}
Loading
Loading