28
28
// project.
29
29
// 6. Give the best performance we can. We should care about performance,
30
30
// but with lower precedence than the other considerations.
31
+ //
32
+ // Except where derivations are given, the expressions used here are all
33
+ // adapted from Kahan's 1986 paper "Branch Cuts for Complex Elementary
34
+ // Functions; or: Much Ado About Nothing's Sign Bit".
31
35
32
36
import RealModule
33
37
34
38
// TODO: uncomment conformance once all implementations are provided.
35
- extension Complex /* : ElementaryFunctions */ {
39
+ extension Complex : ElementaryFunctions {
36
40
37
41
// MARK: - exp-like functions
38
42
@@ -47,7 +51,6 @@ extension Complex /*: ElementaryFunctions */ {
47
51
/// Note that naive evaluation of this expression in floating-point would be prone to premature
48
52
/// overflow, since `cos` and `sin` both have magnitude less than 1 for most inputs (i.e.
49
53
/// `exp(x)` may be infinity when `exp(x) cos(y)` would not be.
50
- @inlinable
51
54
public static func exp( _ z: Complex ) -> Complex {
52
55
guard z. isFinite else { return z }
53
56
// If x < log(greatestFiniteMagnitude), then exp(x) does not overflow.
@@ -62,7 +65,6 @@ extension Complex /*: ElementaryFunctions */ {
62
65
return Complex ( . cos( z. y) , . sin( z. y) ) . multiplied ( by: . exp( z. x) )
63
66
}
64
67
65
- @inlinable
66
68
public static func expMinusOne( _ z: Complex ) -> Complex {
67
69
// exp(x + iy) - 1 = (exp(x) cos(y) - 1) + i exp(x) sin(y)
68
70
// -------- u --------
@@ -132,7 +134,6 @@ extension Complex /*: ElementaryFunctions */ {
132
134
// This function and sinh should stay in sync; if you make a
133
135
// modification here, you should almost surely make a parallel
134
136
// modification to sinh below.
135
- @inlinable @inline ( __always)
136
137
public static func cosh( _ z: Complex ) -> Complex {
137
138
guard z. isFinite else { return z }
138
139
guard z. x. magnitude < - RealType. log ( . ulpOfOne) else {
@@ -162,7 +163,6 @@ extension Complex /*: ElementaryFunctions */ {
162
163
// sinh(x + iy) = sinh(x) cos(y) + i cosh(x) sinh(y)
163
164
//
164
165
// See cosh above for algorithm details.
165
- @inlinable @inline ( __always)
166
166
public static func sinh( _ z: Complex ) -> Complex {
167
167
guard z. isFinite else { return z }
168
168
guard z. x. magnitude < - RealType. log ( . ulpOfOne) else {
@@ -178,7 +178,6 @@ extension Complex /*: ElementaryFunctions */ {
178
178
}
179
179
180
180
// tanh(z) = sinh(z) / cosh(z)
181
- @inlinable
182
181
public static func tanh( _ z: Complex ) -> Complex {
183
182
guard z. isFinite else { return z }
184
183
// Note that when |x| is larger than -log(.ulpOfOne),
@@ -219,7 +218,6 @@ extension Complex /*: ElementaryFunctions */ {
219
218
}
220
219
221
220
// MARK: - log-like functions
222
- @inlinable
223
221
public static func log( _ z: Complex ) -> Complex {
224
222
// If z is zero or infinite, the phase is undefined, so the result is
225
223
// the single exceptional value.
@@ -319,7 +317,6 @@ extension Complex /*: ElementaryFunctions */ {
319
317
return Complex ( . log( onePlus: s) / 2 , θ)
320
318
}
321
319
322
- @inlinable
323
320
public static func log( onePlus z: Complex ) -> Complex {
324
321
// If either |x| or |y| is bounded away from the origin, we don't need
325
322
// any extra precision, and can just literally compute log(1+z). Note
@@ -330,7 +327,7 @@ extension Complex /*: ElementaryFunctions */ {
330
327
guard 2 * z. x. magnitude < 1 && z. y. magnitude < 1 else { return log ( 1 + z) }
331
328
// z is in (±0.5, ±1), so we need to evaluate more carefully.
332
329
// The imaginary part is straightforward:
333
- let θ = z . phase
330
+ let θ = ( 1 + z ) . phase
334
331
// For the real part, we _could_ use the same approach that we do for
335
332
// log( ), but we'd need an extra-precise (1+x)², which can potentially
336
333
// be quite painful to calculate. Instead, we can use an approach that
@@ -352,13 +349,17 @@ extension Complex /*: ElementaryFunctions */ {
352
349
}
353
350
354
351
public static func acos( _ z: Complex ) -> Complex {
355
- fatalError ( )
352
+ Complex (
353
+ 2 * RealType. atan2 ( y: sqrt ( 1 - z) . real, x: sqrt ( 1 + z) . real) ,
354
+ RealType . asinh ( ( sqrt ( 1 + z) . conjugate * sqrt( 1 - z) ) . imaginary)
355
+ )
356
356
}
357
357
358
- // asin(z) = -i*asinh(iz)
359
358
public static func asin( _ z: Complex ) -> Complex {
360
- let w = asinh ( Complex ( - z. y, z. x) )
361
- return Complex ( w. y, - w. x)
359
+ Complex (
360
+ RealType . atan2 ( y: z. x, x: ( sqrt ( 1 - z) * sqrt( 1 + z) ) . real) ,
361
+ RealType . asinh ( ( sqrt ( 1 - z) . conjugate * sqrt( 1 + z) ) . imaginary)
362
+ )
362
363
}
363
364
364
365
// atan(z) = -i*atanh(iz)
@@ -368,15 +369,29 @@ extension Complex /*: ElementaryFunctions */ {
368
369
}
369
370
370
371
public static func acosh( _ z: Complex ) -> Complex {
371
- fatalError ( )
372
+ Complex (
373
+ RealType . asinh ( ( sqrt ( z- 1 ) . conjugate * sqrt( z+ 1 ) ) . real) ,
374
+ 2 * RealType. atan2 ( y: sqrt ( z- 1 ) . imaginary, x: sqrt ( z+ 1 ) . real)
375
+ )
372
376
}
373
377
378
+ // asinh(z) = -i*asin(iz)
374
379
public static func asinh( _ z: Complex ) -> Complex {
375
- fatalError ( )
380
+ let w = asin ( Complex ( - z. y, z. x) )
381
+ return Complex ( w. y, - w. x)
376
382
}
377
383
378
384
public static func atanh( _ z: Complex ) -> Complex {
379
- fatalError ( )
385
+ // TODO: Kahan uses a much more complicated expression here; possibly
386
+ // simply because he didn't have a complex log(1+z) with good
387
+ // characteristics. Investigate tradeoffs further.
388
+ //
389
+ // Further TODO: decide policy for point at infinity / NaN. Unlike most
390
+ // of these functions, atanh doesn't have a pole at infinity; convention
391
+ // in C-family languages is use one value in the upper half plane, and
392
+ // another in the lower. Requires some thought about the most appropriate
393
+ // way to handle this case in Swift.
394
+ ( log ( onePlus: z) - log( onePlus: - z) ) / 2
380
395
}
381
396
382
397
// MARK: - pow-like functions
0 commit comments