@@ -21,23 +21,16 @@ extension Quaternion/*: ElementaryFunctions */ {
21
21
22
22
// MARK: - exp-like functions
23
23
24
- // Mathematically, this operation can be expanded in terms of the `Real`
25
- // operations `exp`, `cos` and `sin` as follows:
26
- // ```
27
24
// exp(r + xi + yj + zk) = exp(r + v) = exp(r) exp(v)
28
25
// = exp(r) (cos(||v||) + (v/||v||) sin(||v||))
29
- // ```
30
- // Note that naive evaluation of this expression in floating-point would be
31
- // prone to premature overflow, since `cos` and `sin` both have magnitude
32
- // less than 1 for most inputs (i.e. `exp(r)` may be infinity when
33
- // `exp(r) cos(arg)` would not be).
26
+ //
27
+ // See exp on complex numbers for algorithm details.
34
28
@inlinable
35
29
public static func exp( _ q: Quaternion ) -> Quaternion {
36
30
guard q. isFinite else { return q }
37
31
// For real quaternions we can skip phase and axis calculations
38
- // TODO: Replace q.imaginary == .zero with `q.isReal`
39
- let argument = q. imaginary == . zero ? . zero : q. imaginary. length
40
- let axis = q. imaginary == . zero ? . zero : ( q. imaginary / argument)
32
+ let argument = q. isReal ? . zero : q. imaginary. length
33
+ let axis = q. isReal ? . zero : ( q. imaginary / argument)
41
34
// If real < log(greatestFiniteMagnitude), then exp(q.real) does not overflow.
42
35
// To protect ourselves against sketchy log or exp implementations in
43
36
// an unknown host library, or slight rounding disagreements between
@@ -55,11 +48,10 @@ extension Quaternion/*: ElementaryFunctions */ {
55
48
// Note that the imaginary part is just the usual exp(r) sin(argument);
56
49
// the only trick is computing the real part, which allows us to borrow
57
50
// the derivative of real part for this function from complex numbers.
58
- // See `expMinusOne` in the ComplexModule for implementation details.
51
+ // See `expMinusOne` in the ComplexModule for implementation details.
59
52
guard q. isFinite else { return q }
60
- // TODO: Replace q.imaginary == .zero with `q.isReal`
61
- let argument = q. imaginary == . zero ? . zero : q. imaginary. length
62
- let axis = q. imaginary == . zero ? . zero : ( q. imaginary / argument)
53
+ let argument = q. isReal ? . zero : q. imaginary. length
54
+ let axis = q. isReal ? . zero : ( q. imaginary / argument)
63
55
// If exp(q) is close to the overflow boundary, we don't need to
64
56
// worry about the "MinusOne" part of this function; we're just
65
57
// computing exp(q). (Even when q.y is near a multiple of π/2,
@@ -78,28 +70,26 @@ extension Quaternion/*: ElementaryFunctions */ {
78
70
}
79
71
80
72
// cosh(r + xi + yj + zk) = cosh(r + v)
81
- // cosh(r + v) = cosh(r) cos(||v||) + (v/||v||) sinh(r) sin(||v||).
73
+ // = cosh(r) cos(||v||) + (v/||v||) sinh(r) sin(||v||)
82
74
//
83
75
// See cosh on complex numbers for algorithm details.
84
76
@inlinable
85
77
public static func cosh( _ q: Quaternion ) -> Quaternion {
86
78
guard q. isFinite else { return q }
87
- // TODO: Replace q.imaginary == .zero with `q.isReal`
88
- let argument = q. imaginary == . zero ? . zero : q. imaginary. length
89
- let axis = q. imaginary == . zero ? . zero : ( q. imaginary / argument)
79
+ let argument = q. isReal ? . zero : q. imaginary. length
80
+ let axis = q. isReal ? . zero : ( q. imaginary / argument)
90
81
return cosh ( q. real, argument, axis: axis)
91
82
}
92
83
93
84
// sinh(r + xi + yj + zk) = sinh(r + v)
94
- // sinh(r + v) = sinh(r) cos(||v||) + (v/||v||) cosh(r) sin(||v||)
85
+ // = sinh(r) cos(||v||) + (v/||v||) cosh(r) sin(||v||)
95
86
//
96
- // See cosh on complex numbers for algorithm details.
87
+ // See sinh on complex numbers for algorithm details.
97
88
@inlinable
98
89
public static func sinh( _ q: Quaternion ) -> Quaternion {
99
90
guard q. isFinite else { return q }
100
- // TODO: Replace q.imaginary == .zero with `q.isReal`
101
- let argument = q. imaginary == . zero ? . zero : q. imaginary. length
102
- let axis = q. imaginary == . zero ? . zero : ( q. imaginary / argument)
91
+ let argument = q. isReal ? . zero : q. imaginary. length
92
+ let axis = q. isReal ? . zero : ( q. imaginary / argument)
103
93
guard q. real. magnitude < - RealType. log ( . ulpOfOne) else {
104
94
let rotation = Quaternion ( halfAngle: argument, unitAxis: axis)
105
95
let firstScale = RealType . exp ( q. real. magnitude/ 2 )
@@ -133,25 +123,26 @@ extension Quaternion/*: ElementaryFunctions */ {
133
123
}
134
124
135
125
// cos(r + xi + yj + zk) = cos(r + v)
136
- // cos(r + v) = cos(r) cosh(||v||) - (v/||v||) sin(r) sinh(||v||).
126
+ // = cos(r) cosh(||v||) - (v/||v||) sin(r) sinh(||v||)
137
127
//
138
128
// See cosh for algorithm details.
139
129
@inlinable
140
130
public static func cos( _ q: Quaternion ) -> Quaternion {
141
131
guard q. isFinite else { return q }
142
- // TODO: Replace q.imaginary == .zero with `q.isReal`
143
- let argument = q. imaginary == . zero ? . zero : q. imaginary. length
144
- let axis = q. imaginary == . zero ? . zero : ( q. imaginary / argument)
132
+ let argument = q. isReal ? . zero : q. imaginary. length
133
+ let axis = q. isReal ? . zero : ( q. imaginary / argument)
145
134
return cosh ( - argument, q. real, axis: axis)
146
135
}
147
136
148
- // See sinh on complex numbers for algorithm details.
137
+ // sin(r + xi + yj + zk) = sin(r + v)
138
+ // = sin(r) cosh(-||v||) - (v/||v||) cos(r) sinh(-||v||)
139
+ //
140
+ // See sinh for algorithm details.
149
141
@inlinable
150
142
public static func sin( _ q: Quaternion ) -> Quaternion {
151
143
guard q. isFinite else { return q }
152
- // TODO: Replace q.imaginary == .zero with `q.isReal`
153
- let argument = q. imaginary == . zero ? . zero : q. imaginary. length
154
- let axis = q. imaginary == . zero ? . zero : ( q. imaginary / argument)
144
+ let argument = q. isReal ? . zero : q. imaginary. length
145
+ let axis = q. isReal ? . zero : ( q. imaginary / argument)
155
146
let ( x, y) = sinh ( - argument, q. real)
156
147
return Quaternion ( real: y, imaginary: axis * - x)
157
148
}
@@ -182,42 +173,36 @@ extension Quaternion/*: ElementaryFunctions */ {
182
173
}
183
174
184
175
// MARK: - pow-like functions
176
+
177
+ // pow(q, p) = exp(log(pow(q, p))) = exp(p * log(q))
178
+ //
179
+ // See pow on complex numbers for algorithm details.
185
180
@inlinable
186
181
public static func pow( _ q: Quaternion , _ p: Quaternion ) -> Quaternion {
187
- // pow(q, p) = exp(log(q^p)) = exp(p * log(q))
188
182
return exp ( p * log( q) )
189
183
}
190
184
185
+ // pow(q, n) = exp(log(q) * n)
186
+ //
187
+ // See pow on complex numbers for algorithm details.
191
188
@inlinable
192
189
public static func pow( _ q: Quaternion , _ n: Int ) -> Quaternion {
193
190
if q. isZero { return . zero }
194
- // TODO: this implementation is not quite correct, because n may be
195
- // rounded in conversion to RealType. This only effects very extreme
196
- // cases, so we'll leave it alone for now.
197
- //
198
- // Note that this does not have the same problems that a similar
199
- // implementation for a real type would have, because there's no
200
- // parity/sign interaction in the complex plane.
201
191
return exp ( log ( q) . multiplied ( by: RealType ( n) ) )
202
192
}
203
193
204
194
@inlinable
205
195
public static func sqrt( _ q: Quaternion ) -> Quaternion < RealType > {
206
196
if q. isZero { return . zero }
207
- // TODO: This is not the fastest implementation available
208
197
return exp ( log ( q) . divided ( by: 2 ) )
209
198
}
210
199
200
+ // root(q, n) = exp(log(q) / n)
201
+ //
202
+ // See root on complex numbers for algorithm details.
211
203
@inlinable
212
204
public static func root( _ q: Quaternion , _ n: Int ) -> Quaternion {
213
205
if q. isZero { return . zero }
214
- // TODO: this implementation is not quite correct, because n may be
215
- // rounded in conversion to RealType. This only effects very extreme
216
- // cases, so we'll leave it alone for now.
217
- //
218
- // Note that this does not have the same problems that a similar
219
- // implementation for a real type would have, because there's no
220
- // parity/sign interaction in the complex plane.
221
206
return exp ( log ( q) . divided ( by: RealType ( n) ) )
222
207
}
223
208
}
@@ -244,7 +229,7 @@ extension Quaternion {
244
229
)
245
230
}
246
231
247
- // See sinh of complex numbers for algorithm details.
232
+ // See sinh of complex numbers for algorithm details.
248
233
@usableFromInline @_transparent
249
234
internal static func sinh(
250
235
_ x: RealType ,
0 commit comments