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
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,21 @@ no numeric root finder applies, and its discriminant vanishes identically. Such
| `"1/(x^3-3*b*x+2*b^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 122 characters |
| `"1/(-9*b*x+9*x^3+2*b^(3/2)*sqrt(3))".ToEntity().Integrate("x")` | `integral(...)` | 176 characters |

### An exponential of i arctan below the bar is read as a negative multiple

**Answers where there were none.** `x^2/(e^(i arctan(a x)) (c + a^2 c x^2)^(3/2))` was left unevaluated: the exponential
below the bar was read as `n = 1`. It is taken above the bar with its exponent negated, and for a negative whole `n`,
`e^(n i arctan(L))` is written over the conjugate, `(1 - i L)^(-n) (1 + L^2)^(n/2)`, which puts the linear with `i` in
it above the bar where the rational rules read it
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"x^2/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 211 characters |
| `"x/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 109 characters |
| `"1/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 132 characters |
| `"x^2/(e^(5*i*atan(a*x))*(c+a^2*c*x^2)^(27/2))".ToEntity().Integrate("x")` | `integral(...)` | 189 characters |

### A symbol shared by a numerator sum's coefficients is taken out before the partial fractions

**Answers where there were none.** `(b d + 2c d x)^9/(a + b x + c x^2)^3` ran past a minute, where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9778,6 +9778,21 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer &&
/// </summary>
internal static Entity? SolveByWritingAnExponentialOfAnInverseAlgebraically(Entity expr, Entity.Variable x, bool integrateByParts)
{
// An exponential below the bar taken above it with its exponent negated, so that a
// negative multiple is read as one: `x^2/(e^(i arctan(a x)) (c + a^2 c x^2)^(3/2))` is
// `x^2 e^(-i arctan(a x))/(c + a^2 c x^2)^(3/2)`, which is written over the conjugate.
if (expr is Divf(var overIt, var underIt) && Mulf.LinearChildren(underIt).Any(IsAnExponentialOfAnInverse))
{
Entity kept = Number.Integer.One, raised = Number.Integer.One;
foreach (var factor in Mulf.LinearChildren(underIt))
if (IsAnExponentialOfAnInverse(factor) && factor is Powf(_, var power))
raised *= MathS.Pow(MathS.e, -power);
else
kept *= factor;
expr = overIt * raised / kept;
}
bool IsAnExponentialOfAnInverse(Entity node) => node is Powf(var e0, var power0) && e0 == MathS.e && power0.ContainsNode(x)
&& power0.Nodes.Any(inner => inner is Arctanf or Arcsinf or Arccosf);
var rewrote = false;
// A power of a quadratic in x outside the exponentials, which `(1 + L^2)^(-n/2)` meets.
var quadraticElsewhere = expr.Nodes.Any(node => node is Powf(var q, _) && q.ContainsNode(x) && !q.Nodes.Any(inner => inner is Arctanf or Arcsinf or Arccosf)
Expand Down Expand Up @@ -9846,6 +9861,16 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer &&
var halfPower = Number.Rational.Create(n.ERational.Negate().Divide(2));
if (n is not Number.Integer)
return MathS.Pow((1 + MathS.i * argument) / (1 - MathS.i * argument), Number.Rational.Create(n.ERational.Divide(2)));
// A negative n over the conjugate, `(1 + i L)^(-1)` being `(1 - i L)/(1 + L^2)`:
// the linear with `i` in it is then above the bar, where the rational rules read
// it, and `x^2/((1 + i a x)(1 + a^2 x^2))` beside it was left unevaluated where
// `x^2 (1 - i a x)/(1 + a^2 x^2)^2` is answered.
if (n is Number.Integer { IsNegative: true })
{
var conjugatePower = Number.Integer.Create(((Number.Integer)n).EInteger.Negate());
return (conjugatePower == Number.Integer.One ? 1 - MathS.i * argument : MathS.Pow(1 - MathS.i * argument, conjugatePower))
* MathS.Pow(1 + MathS.Sqr(argument), Number.Rational.Create(n.ERational.Divide(2)));
}
return (n == Number.Integer.One ? 1 + MathS.i * argument : MathS.Pow(1 + MathS.i * argument, n)) * MathS.Pow(1 + MathS.Sqr(argument), halfPower);
}
Entity unit = inverse is Arcsinf
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// 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 <c>i arctan(a x)</c> below the bar, taken above it as a negative multiple and
/// written over the conjugate: Rubi's 5.3.6 <c>x^2/(e^(i arctan(a x)) (c + a^2 c x^2)^(3/2))</c>,
/// which was left unevaluated.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AnExponentialOfAnArctangentBelowTheBarIntegralTest
{
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());

var pinned = integral.Substitute("C", 0);
Entity original = integrand.ToEntity();
foreach (var (name, value) in pins)
{
pinned = pinned.Substitute(name, value);
original = original.Substitute(name, value);
}
var derivative = pinned.Differentiate("x");
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = original.Substitute("x", at).EvalNumerical();
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
Assert.True(difference / scale < 1e-8,
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}

[Theory]
[InlineData("x^2/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))")]
[InlineData("x/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))")]
[InlineData("1/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("a", 1.3), ("c", 0.7));
DifferentiatesBack(integrand, ("a", -0.5), ("c", 2.5));
}
}
}
Loading