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
13 changes: 13 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,19 @@ after it
|---|---|---|
| `"(a*c + b*c*x)^(-3-2*p)*(f + g*x)*(a^2 + 2*a*b*x + b^2*x^2)^p".ToEntity().Integrate("x")` | `integral(...)`; an answer with no value on the unreleased master | the antiderivative |

### `a + i a tan` of a shifted linear below the bar is integrated in the linear

**Answers where there were none.** `sqrt(a + i a tan(g + f x)) (A + B tan(g + f x))/sqrt(c - i c tan(g + f x))`
ran past thirty seconds, where with `x` for the argument it is answered in one: the rule that writes
`a + i a tan(z)` as an exponential expanded the exponential of `i (g + f x)` and its phase into a search.
Where every trigonometric function in the integrand is of one linear other than `x`, it integrates in
`u = g + f x` first ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sqrt(a + i*a*tan(g + f*x))*(A + B*tan(g + f*x))/sqrt(c - i*c*tan(g + f*x))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 448 characters |
| `"(A + B*tan(g + f*x))/(sqrt(a + i*a*tan(g + f*x))*(c - i*c*tan(g + f*x))^(3/2))".ToEntity().Integrate("x")` | `integral(...)`; past thirty seconds on the unreleased master | 597 characters |

### A value substituted under a binder is not captured by the name it binds

**Wrong answers fixed.** An integral, a sum, a product, a derivative, a limit, a set builder and the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26805,6 +26805,49 @@ 2 when NotWhole(sums[0].Base) != NotWhole(sums[1].Base) => NotWhole(sums[0].Base
/// handed on is the integrand in another spelling.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
/// <summary>
/// <paramref name="expr"/> integrated in <c>u = g + f x</c> where x stands only in trigonometric
/// functions of that one linear, other than x itself, and a root of something in x stands: <c>dx = du/f</c>. Null
/// otherwise.
/// </summary>
private static Entity? InItsOneShiftedLinear(Entity expr, Entity.Variable x, bool integrateByParts)
{
// Beside a root only: whole powers, `(a + i a tan(z))^3 (A + B tan(z))/(c - i c tan(z))^3`, the
// exponential's spelling answers sooner of the linear than of x.
if (!expr.Nodes.Any(node => node is Powf(var @base, Number.Rational power) && power is not Number.Integer && @base.ContainsNode(x)))
return null;
Entity? linear = null;
foreach (var node in expr.Nodes)
{
if (node is not (Tanf or Cotanf or Sinf or Cosf or Secantf or Cosecantf) || !node.ContainsNode(x))
continue;
var argument = node.DirectChildren.First();
if (linear is null)
linear = argument;
else if (linear != argument)
return null;
}
if (linear is null || !TreeAnalyzer.TryGetPolyLinear(linear, x, out var slope, out var offset)
|| slope.ContainsNode(x) || offset.ContainsNode(x) || TreeAnalyzer.IsZero(slope) || linear == x)
return null;
var u = Variable.CreateUnique(expr, "u_shifted");
var inU = expr.Replace(node => node switch
{
Tanf(var a) when a == linear => MathS.Tan(u),
Cotanf(var a) when a == linear => MathS.Cotan(u),
Sinf(var a) when a == linear => MathS.Sin(u),
Cosf(var a) when a == linear => MathS.Cos(u),
Secantf(var a) when a == linear => MathS.Sec(u),
Cosecantf(var a) when a == linear => new Cosecantf(u),
_ => node,
});
if (inU.ContainsNode(x)
|| Integration.ComputeAsAQuestionOfItsOwn((inU / slope).InnerSimplified, u, integrateByParts) is not { } answer
|| answer.Nodes.Any(node => node == MathS.NaN))
return null;
return answer.Substitute(u, linear);
}

internal static Entity? SolveByWritingAnImaginaryTangentAsAnExponential(Entity expr, Entity.Variable x, bool integrateByParts)
{
// Below the bar only: `(A + i A tan(z))^3` above it is a polynomial in the tangent,
Expand Down Expand Up @@ -26872,6 +26915,12 @@ Entity AsAnExponential(Entity node)
var rewritten = below.Replace(AsAnExponential);
if (rewritten == below)
return null;
// One linear other than x for every argument, `g + f x`, is integrated in it: the rewriting
// below expands an exponential of `i (g + f x)` and its phase into a search that ran past
// thirty seconds for `sqrt(a + i a tan(g + f x)) (A + B tan(g + f x))/sqrt(c - i c tan(g + f x))`,
// which in `u = g + f x` is answered in a second.
if (InItsOneShiftedLinear(expr, x, integrateByParts) is { } inTheLinear)
return inTheLinear;
// A root of the tangent or the cotangent of that argument, or of a constant times one, is
// read as written by the substitution `t = tan(z)`, where it is a root of `t`, and by
// nothing in the exponential's spelling: `sqrt(tan(c + d x))/(a + i a tan(c + d x))`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// 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>
/// <c>a + i a tan(g + f x)</c> below the bar, beside other functions of the same shifted linear,
/// integrated in <c>u = g + f x</c>. Rubi's 4.3.3.1. The integrands are complex for a real <c>x</c>
/// and are compared as complex numbers.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AnImaginaryTangentSumOfAShiftedLinearIntegralTest
{
[Theory]
[InlineData("sqrt(a + i*a*tan(g + f*x))*(A + B*tan(g + f*x))/sqrt(c - i*c*tan(g + f*x))")]
[InlineData("(A + B*tan(g + f*x))/(sqrt(a + i*a*tan(g + f*x))*(c - i*c*tan(g + f*x))^(3/2))")]
public void InTheLinear(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("c", 0.7).Substitute("g", 0.4).Substitute("f", 1.1).Substitute("A", 0.9).Substitute("B", 1.7);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -1.2, -0.7, 0.3, 0.8, 1.3, 2.9 })
{
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}");
}
}
}
Loading