@@ -21,23 +21,16 @@ extension Quaternion/*: ElementaryFunctions */ {
2121
2222 // MARK: - exp-like functions
2323
24- // Mathematically, this operation can be expanded in terms of the `Real`
25- // operations `exp`, `cos` and `sin` as follows:
26- // ```
2724 // exp(r + xi + yj + zk) = exp(r + v) = exp(r) exp(v)
2825 // = 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.
3428 @inlinable
3529 public static func exp( _ q: Quaternion ) -> Quaternion {
3630 guard q. isFinite else { return q }
3731 // 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)
4134 // If real < log(greatestFiniteMagnitude), then exp(q.real) does not overflow.
4235 // To protect ourselves against sketchy log or exp implementations in
4336 // an unknown host library, or slight rounding disagreements between
@@ -55,11 +48,10 @@ extension Quaternion/*: ElementaryFunctions */ {
5548 // Note that the imaginary part is just the usual exp(r) sin(argument);
5649 // the only trick is computing the real part, which allows us to borrow
5750 // 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.
5952 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)
6355 // If exp(q) is close to the overflow boundary, we don't need to
6456 // worry about the "MinusOne" part of this function; we're just
6557 // computing exp(q). (Even when q.y is near a multiple of π/2,
@@ -78,28 +70,26 @@ extension Quaternion/*: ElementaryFunctions */ {
7870 }
7971
8072 // 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||)
8274 //
8375 // See cosh on complex numbers for algorithm details.
8476 @inlinable
8577 public static func cosh( _ q: Quaternion ) -> Quaternion {
8678 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)
9081 return cosh ( q. real, argument, axis: axis)
9182 }
9283
9384 // 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||)
9586 //
96- // See cosh on complex numbers for algorithm details.
87+ // See sinh on complex numbers for algorithm details.
9788 @inlinable
9889 public static func sinh( _ q: Quaternion ) -> Quaternion {
9990 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)
10393 guard q. real. magnitude < - RealType. log ( . ulpOfOne) else {
10494 let rotation = Quaternion ( halfAngle: argument, unitAxis: axis)
10595 let firstScale = RealType . exp ( q. real. magnitude/ 2 )
@@ -133,25 +123,26 @@ extension Quaternion/*: ElementaryFunctions */ {
133123 }
134124
135125 // 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||)
137127 //
138128 // See cosh for algorithm details.
139129 @inlinable
140130 public static func cos( _ q: Quaternion ) -> Quaternion {
141131 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)
145134 return cosh ( - argument, q. real, axis: axis)
146135 }
147136
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.
149141 @inlinable
150142 public static func sin( _ q: Quaternion ) -> Quaternion {
151143 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)
155146 let ( x, y) = sinh ( - argument, q. real)
156147 return Quaternion ( real: y, imaginary: axis * - x)
157148 }
@@ -182,42 +173,36 @@ extension Quaternion/*: ElementaryFunctions */ {
182173 }
183174
184175 // 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.
185180 @inlinable
186181 public static func pow( _ q: Quaternion , _ p: Quaternion ) -> Quaternion {
187- // pow(q, p) = exp(log(q^p)) = exp(p * log(q))
188182 return exp ( p * log( q) )
189183 }
190184
185+ // pow(q, n) = exp(log(q) * n)
186+ //
187+ // See pow on complex numbers for algorithm details.
191188 @inlinable
192189 public static func pow( _ q: Quaternion , _ n: Int ) -> Quaternion {
193190 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.
201191 return exp ( log ( q) . multiplied ( by: RealType ( n) ) )
202192 }
203193
204194 @inlinable
205195 public static func sqrt( _ q: Quaternion ) -> Quaternion < RealType > {
206196 if q. isZero { return . zero }
207- // TODO: This is not the fastest implementation available
208197 return exp ( log ( q) . divided ( by: 2 ) )
209198 }
210199
200+ // root(q, n) = exp(log(q) / n)
201+ //
202+ // See root on complex numbers for algorithm details.
211203 @inlinable
212204 public static func root( _ q: Quaternion , _ n: Int ) -> Quaternion {
213205 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.
221206 return exp ( log ( q) . divided ( by: RealType ( n) ) )
222207 }
223208}
@@ -244,7 +229,7 @@ extension Quaternion {
244229 )
245230 }
246231
247- // See sinh of complex numbers for algorithm details.
232+ // See sinh of complex numbers for algorithm details.
248233 @usableFromInline @_transparent
249234 internal static func sinh(
250235 _ x: RealType ,
0 commit comments