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
21 changes: 21 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,27 @@ whose denominator in the tangent is a product of linears
| `"sec(c + d*x)^3/(a*cos(c + d*x) + b*sin(c + d*x))^4".ToEntity().Integrate("x")` | `integral(...)` | answered, in 9,322 characters |
| `"1/(a*sec(x) + b*tan(x))^4".ToEntity().Integrate("x")` | `integral(...)` | answered, in 6,548 characters |

### An odd power of a sine or a cosine of a logarithm is integrated, and a resonance on a sign is declined

**Answers where there were none.** `x^m sin(a + b ln(k x^n))^3` and the cosine's cube were declined, though the first
and second powers were answered (Rubi's 4.7.5). Under `t = ln(k x^n)` each one is an exponential times a power of a
sine, `e^((m + 1) t/n) sin(a + b t)^3`. The rule that closes that answers only the question asked, and the
substitution asked it one level down. A logarithm's substitution now asks that integrand as the question
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

**No answer where there was a wrong one.** `e^(x/n) sin(x sqrt(-1/n^2))` is resonant: `1/n + i sqrt(-1/n^2)` is zero
for a positive `n`, and `1/n - i sqrt(-1/n^2)` is zero for a negative one. The answer divided by one of them, and had
no value at any point. It is declined now ([#1852](https://github.com/asc-community/AngouriMath/issues/1852)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sin(ln(x))^3".ToEntity().Integrate("x")` | `integral(...)` | 130 characters |
| `"sin(a + b*ln(x))^3".ToEntity().Integrate("x")` | `integral(...)` | 909 characters |
| `"x^2*sin(a + b*ln(k*x^n))^3".ToEntity().Integrate("x")` | `integral(...)` | 1,605 characters |
| `"x^m*cos(a + b*ln(k*x^n))^3".ToEntity().Integrate("x")` | `integral(...)` | 1,907 characters |
| `"sin(a + b*ln(k*x^n))^4/x^2".ToEntity().Integrate("x")` | `integral(...)` | 2,083 characters |
| `"exp(x/n)*sin(x*sqrt((-1)/n^2))".ToEntity().Integrate("x")` | an answer with no value anywhere, divided by `1/n^2 + (-1/n^2)` | `integral(...)` |

### An odd power of a quadratic's derivative over a power of the quadratic is integrated in the quadratic

**Shorter answers, and answers where there were none.** `(b + 2c x)^9/(a + b x + c x^2)^3` was answered through the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8146,12 +8146,24 @@ private static (Entity Elementary, Entity Si, Entity Ci) APowerTimesASineOrACosi
// `e^(i arctan(a x))/sqrt(c + a^2 c x^2)` under `x = tan(u)/a` is `e^(i u) cos(u)`.
var plus = ((onTheCosine - MathS.i * onTheSine) / 2).InnerSimplified;
var minus = ((onTheCosine + MathS.i * onTheSine) / 2).InnerSimplified;
var towardsPlus = (rate + MathS.i * frequency).InnerSimplified;
var towardsMinus = (rate - MathS.i * frequency).InnerSimplified;
static bool Vanishes(Entity exponent) => exponent.Evaled is Number.Complex { IsZero: true }
|| exponent.Vars.Any() && Functions.PartialFractions.Bare(exponent.Simplify()).Evaled is Number.Complex { IsZero: true };
var plusVanishes = Vanishes(towardsPlus);
var minusVanishes = Vanishes(towardsMinus);
// One of the two vanishes, since their product is the scale, and which one can rest on
// a sign: with `a = 1/n` and `b = sqrt(-1/n^2)`, `a + i b` is zero for a positive `n`
// and `a - i b` for a negative one. Neither seen to, the term over it divides by zero
// on one side of the sign, and the answer had no value anywhere.
if (!plusVanishes && !minusVanishes)
return null;
Entity resonant = Number.Integer.Zero;
foreach (var (coefficient, exponent) in new[] { (plus, (rate + MathS.i * frequency).InnerSimplified), (minus, (rate - MathS.i * frequency).InnerSimplified) })
foreach (var (coefficient, exponent, vanishes) in new[] { (plus, towardsPlus, plusVanishes), (minus, towardsMinus, minusVanishes) })
{
if (coefficient.Evaled is Number.Complex { IsZero: true })
continue;
var term = exponent.Evaled is Number.Complex { IsZero: true } ? polynomial : polynomial * MathS.Pow(MathS.e, exponent * x);
var term = vanishes ? polynomial : polynomial * MathS.Pow(MathS.e, exponent * x);
if (Integration.ComputeIndefiniteIntegral(term.InnerSimplified, x, integrateByParts: false) is not { } termIntegral || termIntegral.Nodes.Any(node => node == MathS.NaN))
return null;
resonant += coefficient * termIntegral;
Expand Down Expand Up @@ -9820,12 +9832,83 @@ node is Logf(var @base, var argument) && !@base.ContainsNode(x) && @base != Math
if (integrand is Providedf(var inner, _))
integrand = inner;

if (Integration.ComputeIndefiniteIntegral(integrand, u, integrateByParts) is not { } result)
if (TheIntegrandInTheLogarithm(integrand, u, integrateByParts) is not { } result)
return null;
var back = result.Substitute(u, MathS.Ln(x));
return IsARadicalFunctionOfWholePowersOfTheExponential(inU, u) ? back.Provided(x > Number.Integer.Zero) : back;
}

/// <summary>
/// The integral of what a logarithm's substitution makes of the integrand, in the logarithm
/// <paramref name="t"/>; an exponential times whole powers of sines and cosines asked at the top.
/// </summary>
/// <remarks>
/// <c>x^m sin(a + b ln(c x^n))^3</c> is <c>e^((m + 1) t/n) sin(a + b t)^3</c> in <c>t</c>, which the
/// closed rule for an exponential times a trigonometric answers only when asked, and as the
/// substitution's integrand it is one level down: the cube was declined where the first and
/// second powers, which other rules answer, were not. The substitution is exact, so its integrand
/// is the question asked in another variable. Once: an integrand asked so does not lift its own
/// substitution again. Rubi's 4.7.5.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
private static Entity? TheIntegrandInTheLogarithm(Entity integrand, Entity.Variable t, bool integrateByParts)
{
if (!Integration.AnsweringTheQuestionAsked || !IsAnExponentialTimesPowersOfSinesAndCosines(integrand, t))
return Integration.ComputeIndefiniteIntegral(integrand, t, integrateByParts);
if (askingTheIntegrandInTheLogarithm)
{
var answer = Integration.ComputeIndefiniteIntegral(integrand, t, integrateByParts);
if (answer is null)
Integration.DeclinedForItsScope();
return answer;
}
askingTheIntegrandInTheLogarithm = true;
try
{
return Integration.ComputeAsAQuestionOfItsOwn(integrand, t, integrateByParts);
}
finally
{
askingTheIntegrandInTheLogarithm = false;
}
}

/// <summary>Whether <see cref="TheIntegrandInTheLogarithm"/> is asking at the top already.</summary>
[System.ThreadStatic] private static bool askingTheIntegrandInTheLogarithm;

/// <summary>
/// Whether <paramref name="expr"/> is a constant times an exponential of a linear in
/// <paramref name="t"/> times whole positive powers of sines and cosines of linears in it.
/// </summary>
private static bool IsAnExponentialTimesPowersOfSinesAndCosines(Entity expr, Entity.Variable t)
{
if (expr is Divf(var above, var below))
{
if (below.ContainsNode(t))
return false;
expr = above;
}
var exponential = false;
var trigonometric = false;
foreach (var factor in Mulf.LinearChildren(expr))
{
if (!factor.ContainsNode(t))
continue;
if (factor is Powf(var @base, var exponent) && !@base.ContainsNode(t))
{
if (!TreeAnalyzer.TryGetPolyLinear(exponent, t, out _, out _))
return false;
exponential = true;
continue;
}
var function = factor is Powf(var raised, Number.Integer power) && power.EInteger.Sign > 0 ? raised : factor;
if (function is not (Sinf or Cosf) || !TreeAnalyzer.TryGetPolyLinear(function.DirectChildren.First(), t, out _, out _))
return false;
trigonometric = true;
}
return exponential && trigonometric;
}

/// <summary>
/// Whether <paramref name="expr"/> holds <paramref name="t"/> only in exponentials
/// <c>e^(k t + a)</c> with a whole <c>k</c>, some of them under a power that is not
Expand Down Expand Up @@ -9910,7 +9993,7 @@ node is Powf(var @base, var exponent) && @base == MathS.e && exponent.ContainsNo
var integrand = Functions.SingleQuotient.Combine(inT * MathS.Pow(MathS.e, (mPlusOne / n).InnerSimplified * t)).InnerSimplified;
if (integrand is Providedf(var bare, _))
integrand = bare;
if (Integration.ComputeIndefiniteIntegral(integrand, t, integrateByParts) is not { } inTIntegral)
if (TheIntegrandInTheLogarithm(integrand, t, integrateByParts) is not { } inTIntegral)
return null;
// Of ln(x) itself, K is 1, and written as x^(m + 1) x^(-(m + 1)) it is not seen to be.
var k = logarithms[0].Antilogarithm == x ? Number.Integer.One
Expand Down
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 power of <c>x</c> times a whole power of a sine or a cosine of <c>a + b ln(k x^n)</c>, which
/// in the logarithm is an exponential times a power of a sine or a cosine. Rubi's 4.7.5.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AnOddPowerOfATrigonometricOfALogarithmIntegralTest
{
[Theory]
[InlineData("sin(ln(x))^3", 500)]
[InlineData("sin(a + b*ln(x))^3", 2000)]
[InlineData("x^2*sin(a + b*ln(k*x^n))^3", 3000)]
[InlineData("sin(a + b*ln(k*x^n))^3/x^3", 3000)]
[InlineData("x^m*cos(a + b*ln(k*x^n))^3", 3000)]
[InlineData("sin(a + b*ln(k*x^n))^4/x^2", 4000)]
public void DifferentiatesBack(string integrand, int atMostCharacters)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Assert.True(integral.Stringize().Length <= atMostCharacters, $"{integral.Stringize().Length} characters");
Entity Pinned(Entity e) => e.Substitute("a", "1/4".ToEntity()).Substitute("b", "3/2".ToEntity())
.Substitute("k", 2).Substitute("n", 3).Substitute("m", "5/2".ToEntity());
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
foreach (var at in new[] { "0.35", "0.6", "1.1", "1.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}");
}
}

/// <summary>
/// <c>a + i b</c> or <c>a - i b</c> is zero, which one by the sign of <c>n</c>, and an answer
/// dividing by either had no value anywhere.
/// <a href="https://github.com/asc-community/AngouriMath/issues/1852">#1852</a>
/// </summary>
[Theory]
[InlineData("exp(x/n)*sin(x*sqrt((-1)/n^2))")]
[InlineData("exp(x/n)*sin(a + x*sqrt((-1)/n^2))")]
[InlineData("sin(a + ln(k*x^n)*sqrt((-1)/n^2))")]
public void AResonanceOnASignIsDeclined(string integrand) =>
Assert.Contains("integral(", integrand.ToEntity().Integrate("x").Stringize());
}
}
Loading