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
16 changes: 16 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,22 @@ in Rubi's 1.1.3.4 come out shorter as well, and those that took two seconds take
| `"1/(x^8*(a + b*x^3)*(c + d*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,217 characters |
| `"1/(x^6*(a + b*x^4)*(c + d*x^4))".ToEntity().Integrate("x")` | `integral(...)` | 795 characters |

### A function of an inverse tangent times its derivative up to a constant factor is integrated by substitution

**Answers where there were none.** `atan(c x/sqrt(a - c^2 x^2))^m/sqrt(d - c^2 d x^2/a)` was declined, and so were
eleven more of Rubi's 5.3.7. The derivative of `u = atan(c x/sqrt(a - c^2 x^2))` is `c/sqrt(a - c^2 x^2)`, and beside
it `1/sqrt(d - c^2 d x^2/a)` is that times a factor constant wherever it is real, though written with `x` in it. The
integrand over `u'` is now split into a function of `u` and such a factor, and the integral is the factor times the
integral of the function ([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"atan(c*x/sqrt(a-c^2*x^2))^m/sqrt(d-c^2*d*x^2/a)".ToEntity().Integrate("x")` | `integral(...)` | 176 characters |
| `"atan(c*x/sqrt(a-c^2*x^2))/sqrt(d-c^2*d*x^2/a)".ToEntity().Integrate("x")` | `integral(...)` | 164 characters |
| `"1/(atan(c*x/sqrt(a-c^2*x^2))*sqrt(d-c^2*d*x^2/a))".ToEntity().Integrate("x")` | `integral(...)` | 160 characters |
| `"atan(k*x/sqrt(-a*k^2/b-k^2*x^2))^m/sqrt(a+b*x^2)".ToEntity().Integrate("x")` | `integral(...)` | 320 characters |
| `"1/(atan(k*x/sqrt(-a*k^2/b-k^2*x^2))^3*sqrt(a+b*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 314 characters |

### Half an odd power of an inverse hyperbolic cosine of `c + d x^2` is integrated in error functions

**Answers where there were none.** `(a + b acosh(1 + d x^2))^(1/2)` was declined, and so were thirteen more of Rubi's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29128,6 +29128,62 @@ static bool Through(Entity node, Entity.Variable x)
}
}

/// <summary>
/// A function of an inverse sine, cosine or tangent <c>u</c> of something not linear, times
/// <c>u'</c> up to a factor that is constant though written with <c>x</c> in it:
/// <c>integral G(u) K u' dx = K integral G(t) dt</c> at <c>t = u</c>.
/// <c>atan(c x/sqrt(a - c^2 x^2))</c> has the derivative <c>c/sqrt(a - c^2 x^2)</c>, and beside it
/// <c>1/sqrt(d - c^2 d x^2/a)</c> is that over <c>c</c> times <c>sqrt(a - c^2 x^2)/sqrt(d - c^2 d x^2/a)</c>,
/// which is constant wherever it is real.
/// </summary>
/// <remarks>
/// The factor is taken as constant only where its derivative is zero as a value. Rubi's 5.3.7
/// <c>atan(c x/sqrt(a - c^2 x^2))^m/sqrt(d - c^2 d x^2/a)</c> and the like were declined.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveAFunctionOfAnInverseTimesItsDerivativeUpToAConstant(Entity expr, Entity.Variable x, bool integrateByParts)
{
if (!Integration.AnsweringTheQuestionAsked)
return null;
var inverses = expr.Nodes.Where(node => node is Arctanf or Arcsinf or Arccosf && node.ContainsNode(x)
&& !TreeAnalyzer.TryGetPolyLinear(((Function)node).DirectChildren.First(), x, out _, out _)).Distinct().ToList();
foreach (var inverse in inverses)
{
var t = Variable.CreateUnique(expr, "t_inv");
var inT = expr.Substitute(inverse, t);
if (!inT.ContainsNode(t))
continue;
var derivative = inverse.Differentiate(x).Simplify();
if (Functions.PartialFractions.IsZeroAsAValue(derivative))
continue;
var (top, bottom) = Functions.SingleQuotient.Of((inT / derivative).InnerSimplified);
Entity inTheVariable = Number.Integer.One, constant = Number.Integer.One;
foreach (var (side, below) in new[] { (top, false), (bottom, true) })
foreach (var factor in Mulf.LinearChildren(side))
{
if (factor.ContainsNode(t))
inTheVariable = below ? inTheVariable / factor : inTheVariable * factor;
else
constant = below ? constant / factor : constant * factor;
}
if (inTheVariable.ContainsNode(x) || !constant.ContainsNode(x) || !HoldsItsValue(constant))
continue;
if (Integration.ComputeAsAQuestionOfItsOwn(inTheVariable, t, integrateByParts) is not { } inU)
continue;
var answer = (constant * inU.Substitute(t, inverse)).InnerSimplified;
if (!Functions.PartialFractions.DerivativeDiffersWhereReal(answer, expr, x))
return answer;
}
return null;

// Constant where it takes its value at x = 3/10 at the sampled points, every other symbol
// pinned: asking Simplify whether the derivative of the factor vanished took four seconds
// on `sqrt(-a e^2/b - e^2 x^2)/(e sqrt(a + b x^2))`, and its square stayed a power of a
// quotient of roots. A point it cannot tell at counts against it; the answer is checked too.
bool HoldsItsValue(Entity factor)
=> Functions.PartialFractions.HoldsAtSampledPoints(factor, factor.Substitute(x, Number.Rational.Create(3, 10)), x);
}

/// <summary>
/// A power of a constant multiple of a radicand that a logarithm holds under a root,
/// written over that radicand: <c>acosh(c x)</c> is <c>ln(c x + sqrt(c^2 x^2 - 1))</c>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,11 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
// A sum free of the variable that comes to a number multiplied out, as that number,
// before anything reads a coefficient or an exponent in it: `2 + 2 a - 2 (1 + a)`.
if ((answer = IndefiniteIntegralSolver.SolveByFoldingConstantSumsToTheirNumbers(expr, x, integrateByParts)) is { }) return answer;
// A function of an inverse of something not linear times its derivative up to a factor
// constant though written with x: `atan(c x/sqrt(a - c^2 x^2))^2/sqrt(d - c^2 d x^2/a)`. Early,
// since it fires only where that factor holds x, and the rules before it spent 28 seconds on
// `atan(e x/sqrt(-a e^2/b - e^2 x^2))/sqrt(a + b x^2)` declining.
if ((answer = IndefiniteIntegralSolver.SolveAFunctionOfAnInverseTimesItsDerivativeUpToAConstant(expr, x, integrateByParts)) is { }) return answer;
// A whole power of a linear in the inverse sine, cosine or hyperbolic cosine of
// `c + d x^2` with `c^2 = 1`, by its recurrence, which closes: by parts it went round.
if ((answer = IndefiniteIntegralSolver.SolveAPowerOfAnInverseFunctionOfAQuadraticByTheRecurrence(expr, x)) is { }) return answer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// 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 function of an inverse tangent of something not linear times its derivative, up to a factor
/// constant though written with <c>x</c>: Rubi's 5.3.7 <c>atan(c x/sqrt(a - c^2 x^2))^m/sqrt(d - c^2 d x^2/a)</c>,
/// where <c>sqrt(a - c^2 x^2)/sqrt(d - c^2 d x^2/a)</c> is that factor. Pinned on the real domain.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AFunctionOfAnInverseTimesItsDerivativeIntegralTest
{
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}");
}
}

// d with the sign that keeps the argument real at every sampled x, and |d| below 2/2.3^2
// so that the inverse sine and cosine stay inside [-1, 1]; off it the exact checks hold too.
[Theory]
[InlineData("atan(c*x/sqrt(a-c^2*x^2))^m/sqrt(d-c^2*d*x^2/a)")]
[InlineData("atan(c*x/sqrt(a-c^2*x^2))^2/sqrt(d-c^2*d*x^2/a)")]
[InlineData("1/(atan(c*x/sqrt(a-c^2*x^2))*sqrt(d-c^2*d*x^2/a))")]
[InlineData("1/(atan(k*x/sqrt(-a*k^2/b-k^2*x^2))^3*sqrt(a+b*x^2))")]
[InlineData("atan(k*x/sqrt(-a*k^2/b-k^2*x^2))/sqrt(a+b*x^2)")]
public void IsAnsweredAndDifferentiatesBack(string integrand)
{
DifferentiatesBack(integrand, ("a", 9), ("b", -1.25), ("c", 0.7), ("d", 1.4), ("k", 0.75), ("m", 2.5));
DifferentiatesBack(integrand, ("a", 7), ("b", -2), ("c", -0.5), ("d", 0.6), ("k", -0.3), ("m", 1.0 / 3));
}
}
}
Loading