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 @@ -568,6 +568,20 @@ symbol in it and a denominator past a sextic is now left to the partial fraction
| `"(d + e*x^2 + f*x^4)/(x^4*(a + b*x^2 + c*x^4)^2)".ToEntity().Integrate("x")` | `integral(...)`; past a minute on the unreleased master | 3,715 characters |
| `"cos(e + f*x)^5/(a + b*sec(e + f*x)^2)^3".ToEntity().Integrate("x")` | `integral(...)`; past a minute on the unreleased master | 27,944 characters |

### An exponential of the sine or the cosine beside a polynomial in `x`, the sine and the cosine is integrated

**Answers where there were none.** `e^sin(x) sec(x)^2 (x cos(x)^3 - sin(x))` was declined, Rubi's 4.7.7, and so were
`e^cos(x) (x sin(x) - 1)` and `e^sin(x) (x cos(x) + 1)`. The ansatz for an exponential beside the trigonometric
functions read the exponent only when it was linear in `x`; it now also takes an exponent that is a polynomial in
the sine and the cosine, whose derivative joins each term the ansatz differentiates
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

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

### A symbolic quartic in `x^2` with a square discriminant is split into its two quadratics

**Answers where there were none.** `cot(c + d x)^3 sqrt(a + b sec(c + d x))` ran past a minute: the root of the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14906,8 +14906,9 @@ bool Read(Entity e, int index, int[] prefix)

/// <summary>
/// A rational function of <c>x</c> and of <c>sin(x)</c> and <c>cos(x)</c> together, with
/// an exponential <c>e^(a x)</c> in front or not, closed by the ansatz
/// <c>F = e^(a x) P/Q</c> with <c>P</c> and <c>Q</c> polynomials in the three.
/// an exponential <c>e^h</c> in front or not, <c>h</c> linear in <c>x</c> or a polynomial in
/// the three, closed by the ansatz <c>F = e^h P/Q</c> with <c>P</c> and <c>Q</c> polynomials
/// in the three.
/// </summary>
/// <remarks>
/// <para>
Expand All @@ -14929,6 +14930,12 @@ bool Read(Entity e, int index, int[] prefix)
/// tower first, and every substitution of it leaves powers of <c>1 + t^2</c> above and
/// below that nothing cancels; the ring needs no substitution.
/// </para>
/// <para>
/// An exponent in the ring has its derivative there too, so the bracket is
/// <c>(h' P Q + P' Q - P Q') D</c> and nothing else changes. Rubi's 4.7.7
/// <c>e^sin(x) sec(x)^2 (x cos(x)^3 - sin(x))</c> is <c>(e^sin(x) (x - sec(x)))'</c> so, a sum
/// of two terms neither of which has an elementary antiderivative.
/// </para>
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveByTrigonometricTowerAnsatz(Entity expr, Entity.Variable x)
Expand All @@ -14944,11 +14951,15 @@ bool Read(Entity e, int index, int[] prefix)
if (ReadOneExponentialTimesTheRest(expr, x) is not var (exponent, rest))
return null;
Entity a = Number.Integer.Zero;
// Or an exponent that is itself a polynomial in the three, read below: `e^sin(x)`'s
// derivative is `cos(x) e^sin(x)`, and the bracket takes `h' m Q` for `a m Q`.
var curved = false;
if (exponent is not null)
{
if (!TreeAnalyzer.TryGetPolyLinear(exponent, x, out var slope, out _) || slope.ContainsNode(x))
return null;
a = slope;
if (TreeAnalyzer.TryGetPolyLinear(exponent, x, out var slope, out _) && !slope.ContainsNode(x))
a = slope;
else
curved = true;
}
var overTheTwo = rest.Replace(node => node switch
{
Expand Down Expand Up @@ -14994,6 +15005,23 @@ static bool IsAWholeMultiple(Entity argument, Entity.Variable x, out int k)
var (above, below) = Functions.SingleQuotient.Of(inTheThree);
if (!TrigonometricPolynomial.TryRead(above, x, s, c, out var n) || !TrigonometricPolynomial.TryRead(below, x, s, c, out var d))
return null;
TrigonometricPolynomial? exponentPrime = null;
if (curved)
{
var exponentInTheThree = exponent!.Replace(node =>
{
if (node is Sinf(var argument) && IsAWholeMultiple(argument, x, out var k))
return MultipleAngle(k, s, c).Sine;
if (node is Cosf(var argument2) && IsAWholeMultiple(argument2, x, out var k2))
return MultipleAngle(k2, s, c).Cosine;
return node;
});
if (exponentInTheThree.Nodes.Any(node => node is Sinf or Cosf && node.ContainsNode(x))
|| !TrigonometricPolynomial.TryRead(exponentInTheThree, x, s, c, out var h)
|| h.DegreeInX > MaximumTrigonometricTowerDegree || h.TrigonometricDegree > MaximumTrigonometricTowerDegree)
return null;
exponentPrime = h.Derivative();
}
if (n.DegreeInX > MaximumTrigonometricTowerDegree || d.DegreeInX > MaximumTrigonometricTowerDegree
|| n.TrigonometricDegree > MaximumTrigonometricTowerDegree || d.TrigonometricDegree > MaximumTrigonometricTowerDegree)
return null;
Expand Down Expand Up @@ -15037,6 +15065,8 @@ static bool IsAWholeMultiple(Entity argument, Entity.Variable x, out int k)
var bracket = m.Derivative().Times(q).Minus(m.Times(qPrime));
if (a.Evaled is not Number.Complex { IsZero: true })
bracket = bracket.Plus(m.Times(q).Scaled(a));
if (exponentPrime is not null)
bracket = bracket.Plus(m.Times(q).Times(exponentPrime));
columns.Add(bracket.Times(d));
monomialsOfP.Add((i, k, flag));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// 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>
/// An exponential of a polynomial in <c>x</c>, <c>sin(x)</c> and <c>cos(x)</c> beside a
/// rational function of the three, closed by the ansatz <c>e^h P/Q</c>:
/// <c>e^sin(x) sec(x)^2 (x cos(x)^3 - sin(x))</c> is <c>(e^sin(x) (x - sec(x)))'</c>, though
/// neither of its two terms has an elementary antiderivative. Rubi's 4.7.7.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AnExponentialOfATrigonometricPolynomialIntegralTest
{
[Theory]
[InlineData("e^sin(x)*sec(x)^2*(x*cos(x)^3 - sin(x))", 100)]
[InlineData("e^cos(x)*(x*sin(x) - 1)", 100)]
[InlineData("e^sin(x)*(x*cos(x) + 1)", 100)]
public void DifferentiatesBack(string integrand, int atMost)
{
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}");
var derivative = integral.Substitute("C", 0).Differentiate("x");
var original = integrand.ToEntity();
foreach (var at in new[] { "0.35", "1.1", "2.3", "4.4" })
{
var want = original.Substitute("x", at.ToEntity()).EvalNumerical();
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)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}
}
}
Loading