Skip to content

Commit 4d1f3b7

Browse files
The arcsecant beside a root of x^2 - 1 is integrated right below -1 too (#1722)
Under x = sec(u), sqrt(x^2 - 1) is |tan(u)|, and on the arcsecant's range the tangent has the sign of x. It was written tan(u), so x arcsec(x)/sqrt(x^2 - 1) and its kin were answered right for x > 1 and with the integrand's negative for a derivative at every x < -1. The root carries the sign now, as the cosecant's and the cotangent's do, and a first power of the arcsecant beside it is left to parts as theirs is. The sign is written back as the argument's, which is the sign of x only for a positive slope and no constant. Part of #718. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 51cb049 commit 4d1f3b7

3 files changed

Lines changed: 60 additions & 14 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3100,6 +3100,24 @@ leaves are read as quotients of the sine and the cosine
31003100
| `"1/(a+b*asin(c*x))".Integrate("x")` | `integral(1 / (a + b * arcsin(c * x)), x)` | `Si` and `Ci` of `(a + b arcsin(c x))/b` |
31013101
| `"x/((c+a^2*c*x^2)^2*atan(a*x))".Integrate("x")` | `integral(x / ((c + a ^ 2 * c * x ^ 2) ^ 2 * arctan(a * x)), x)` | `1/2 * Si(2 * arctan(a * x)) / (a ^ 2 * c ^ 2) + C` |
31023102

3103+
### The arcsecant beside a root of `x^2 - 1` is integrated right below `-1` too
3104+
3105+
**Answers where there were none.** Under `x = sec(u)`, the substitution that undoes the arcsecant,
3106+
`sqrt(x^2 - 1)` is `|tan(u)|`, and on the arcsecant's range the tangent has the sign of `x`. It was
3107+
written `tan(u)`, so that on the unreleased master `x arcsec(x)/sqrt(x^2 - 1)` and its kin were
3108+
answered right for `x > 1` and with the integrand's negative for a derivative at every `x < -1`,
3109+
where they are as real; 2.5.0 declined them. The root is `tan(u) sgn(x)` now, as the cosecant's and
3110+
the cotangent's carry their signs, and a first power of the arcsecant beside it is by parts. The sign
3111+
written back is the argument's, which is that of `x` only for a positive slope and no constant:
3112+
`arcsec(x + 3)` has the sign of `x + 3` ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
3113+
3114+
| Input | Was (2.5.0) | Now |
3115+
|---|---|---|
3116+
| `"x*asec(x)/sqrt(-1 + x^2)".ToEntity().Integrate("x")` | `integral(...)` | right for `x > 1` and for `x < -1` |
3117+
| `"asec(x)/(x^2*sqrt(-1 + x^2))".ToEntity().Integrate("x")` | `integral(...)` | the same |
3118+
| `"asec(x)^2/(x*sqrt(x^2 - 1))".ToEntity().Integrate("x")` | `integral(...)` | the same |
3119+
| `"asec(x + 3)^2/((x + 3)*sqrt((x + 3)^2 - 1))".ToEntity().Integrate("x")` | `integral(...)` | right for `x > -2` and for `x < -4` |
3120+
31033121
### A perfect square in a power of the variable is read as one
31043122

31053123
`sqrt(a^2 + 2 a b x^2 + b^2 x^4) sqrt(c + e x + d x^2)` was left unevaluated. The radicand is

‎Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs‎

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7157,10 +7157,12 @@ private static bool TheInverseIsBelowTheBar(Entity expr, Entity inverse, Entity.
71577157
/// and the cosine is not negative; substituting and simplifying instead would leave
71587158
/// <c>sqrt(1 - sin(u)^2)</c>, which the simplifier is right not to call <c>cos(u)</c>.
71597159
/// So <c>(1 - x^2)^(k/2)</c> becomes <c>cos(u)^k</c> directly, and likewise
7160-
/// <c>(1 + x^2)^(k/2)</c> into <c>sec(u)^k</c> for the tangent and <c>(x^2 - 1)^(k/2)</c>
7161-
/// into <c>tan(u)^k</c> for the secant, each on its principal branch. That branch is the
7162-
/// one the inverse function is defined on, so the answer holds wherever the integrand
7163-
/// is read through it.
7160+
/// <c>(1 + x^2)^(k/2)</c> into <c>sec(u)^k</c> for the tangent, each on its principal
7161+
/// branch. That branch is the one the inverse function is defined on, so the answer holds
7162+
/// wherever the integrand is read through it. For the secant <c>(x^2 - 1)^(k/2)</c> is
7163+
/// <c>|tan(u)|^k</c>, and on the arcsecant's range the tangent has the sign of the
7164+
/// argument, so it becomes <c>tan(u)^k</c> times that sign to the <c>k</c>, as the
7165+
/// cosecant's and the cotangent's roots carry theirs.
71647166
/// </para>
71657167
/// <para>
71667168
/// Exactly one inverse function, of the variable or of a linear <c>c x + d</c> in it,
@@ -7213,9 +7215,10 @@ private static bool TheInverseIsBelowTheBar(Entity expr, Entity inverse, Entity.
72137215
}
72147216

72157217
var u = Variable.CreateUnique(expr, "u_inv");
7216-
// The sign of u, where the root's sign is that: a constant on each half of the
7217-
// range, carried through the integration as a symbol and written back as the sign
7218-
// of x, which it is for the cosecant and the cotangent.
7218+
// The sign the root has, where it has one: a constant on each half of the range,
7219+
// carried through the integration as a symbol and written back as the sign of the
7220+
// argument, which it is -- the sign of u for the cosecant and the cotangent, and of
7221+
// tan(u) for the secant.
72197222
var signOfU = Variable.CreateUnique(expr, "s_inv");
72207223
// x in terms of u, dx/du, the quadratic whose root goes by construction and what it
72217224
// becomes, and the way back.
@@ -7245,7 +7248,11 @@ private static bool TheInverseIsBelowTheBar(Entity expr, Entity inverse, Entity.
72457248
(xInU, dxdu, radicandBase, root) = (MathS.Cosec(u), -MathS.Cosec(u) * MathS.Cotan(u), MathS.Sqr(argument) - 1, MathS.Cotan(u) * signOfU);
72467249
break;
72477250
default:
7248-
(xInU, dxdu, radicandBase, root) = (MathS.Sec(u), MathS.Sec(u) * MathS.Tan(u), MathS.Sqr(argument) - 1, MathS.Tan(u));
7251+
// arcsec has the range [0, pi/2) ∪ (pi/2, pi], where the tangent has the sign
7252+
// of the argument: `sqrt(x^2 - 1)` is `tan(u) sgn(x)`. Taken as `tan(u)`, the
7253+
// answer was right for x > 1 and its derivative the integrand's negative for
7254+
// every x < -1, where the integrand is as real: `x arcsec(x)/sqrt(x^2 - 1)`.
7255+
(xInU, dxdu, radicandBase, root) = (MathS.Sec(u), MathS.Sec(u) * MathS.Tan(u), MathS.Sqr(argument) - 1, MathS.Tan(u) * signOfU);
72497256
break;
72507257
}
72517258
if (linear)
@@ -7318,10 +7325,10 @@ node is Powf(var @base, Number.Integer power) && power.EInteger.CompareTo(EInteg
73187325
{
73197326
if (radicalsRemoved == 0 && !exponentialOfTheInverse && !powerOfTheInverse)
73207327
return null;
7321-
// The cosecant and the cotangent carry the sign of u into the root, and a first
7322-
// power of either beside a root is parts' -- `arccot(x)/(1 + x^2)^(3/2)` is
7323-
// `x arccot(x)/sqrt(1 + x^2) + 1/sqrt(1 + x^2)` there, with no sign in it.
7324-
if (inverse is Arccosecantf or Arccotanf && !exponentialOfTheInverse && !powerOfTheInverse)
7328+
// The cosecant, the cotangent and the secant carry a sign into the root, and a
7329+
// first power of any of them beside a root is parts' -- `arccot(x)/(1 + x^2)^(3/2)`
7330+
// is `x arccot(x)/sqrt(1 + x^2) + 1/sqrt(1 + x^2)` there, with no sign in it.
7331+
if (inverse is Arccosecantf or Arccotanf or Arcsecantf && !exponentialOfTheInverse && !powerOfTheInverse)
73257332
return null;
73267333
}
73277334
// And nothing else of `x` under a root, which the construction did not reach:
@@ -7374,10 +7381,11 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer &&
73747381
if (result is null)
73757382
return null;
73767383
if (result.ContainsNode(signOfU))
7377-
// The sign squared is one, and the sign itself is the sign of x.
7384+
// The sign squared is one, and the sign itself is the argument's: `arccsc(c x + d)`
7385+
// has the sign of `c x + d`, which is the sign of x only for a positive c and no d.
73787386
result = result
73797387
.Replace(node => node is Powf(var b, Number.Integer k) && b == signOfU ? (k.EInteger.IsEven ? Number.Integer.One : signOfU) : node)
7380-
.Substitute(signOfU, MathS.Signum(x));
7388+
.Substitute(signOfU, MathS.Signum(argument));
73817389
return TrigonometryOfTheInverseInX(result.Substitute(u, inverse), inverse, argument);
73827390
}
73837391

‎Sources/Tests/UnitTests/Calculus/InverseSecantIntegralTest.cs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,26 @@ public void TheFourThatWereAlreadyThere(string integrand, double[] points)
109109
[InlineData("x^3*asec(x)/sqrt(x^4 - 1)", new[] { 1.4, 2.6, 5.1, -1.9, -3.7 })]
110110
public void ByPartsAgainstARootOfAQuartic(string integrand, double[] points) => DifferentiatesBack(integrand, points);
111111

112+
/// <summary>
113+
/// The arcsecant beside a root of <c>x^2 - 1</c>, under <c>x = sec(u)</c>: the root is
114+
/// <c>tan(u) sgn(x)</c> on the arcsecant's range, and taken as <c>tan(u)</c> the answers
115+
/// were right for x &gt; 1 and their derivatives the integrands' negatives for every
116+
/// x &lt; -1, where the integrands are as real. Timofeev's and Charlwood's. With a linear
117+
/// argument the sign is the argument's: at <c>x = -1.5</c> the sign of <c>x + 3</c> is
118+
/// not the sign of <c>x</c>.
119+
/// </summary>
120+
[Theory]
121+
[InlineData("x*asec(x)/sqrt(-1 + x^2)", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
122+
[InlineData("asec(x)*sqrt(-1 + x^2)/x^4", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
123+
[InlineData("asec(x)/(-1 + x^2)^(5/2)", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
124+
[InlineData("x^3*asec(x)/(-1 + x^2)^(5/2)", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
125+
[InlineData("asec(x)/(x^2*sqrt(-1 + x^2))", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
126+
[InlineData("(-1 + x^2)^(3/2)*asec(x)^2/x^5", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
127+
[InlineData("asec(x)^3*sqrt(-1 + x^2)/x^4", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
128+
[InlineData("asec(x)^2/(x*sqrt(x^2 - 1))", new[] { 1.3, 2.6, -1.3, -1.9, -2.7 })]
129+
[InlineData("asec(x + 3)^2/((x + 3)*sqrt((x + 3)^2 - 1))", new[] { 0.5, -1.5, -0.5, -4.5, -6.0 })]
130+
public void BesideARootOnBothIntervals(string integrand, double[] points) => DifferentiatesBack(integrand, points);
131+
112132
/// <summary>
113133
/// A non-linear argument is not this row's: <c>arcsec(x^2)</c> would want the chain rule
114134
/// undone first, and the table reads a linear argument only.

0 commit comments

Comments
 (0)