@@ -202,136 +202,6 @@ extension Quaternion/*: ElementaryFunctions */ {
202202 let p = Quaternion ( imaginary: â)
203203 return - p * tanh( q * p)
204204 }
205-
206- // MARK: - log-like functions
207- @inlinable
208- public static func log( _ q: Quaternion ) -> Quaternion {
209- // If q is zero or infinite, the phase is undefined, so the result is
210- // the single exceptional value.
211- guard q. isFinite && !q. isZero else { return . infinity }
212-
213- let argument = q. imaginary. length
214- let axis = q. imaginary / argument
215-
216- // We deliberatly choose log(length) over the (faster)
217- // log(lengthSquared) / 2 which is used for complex numbers; as
218- // the squared length of quaternions is more prone to overflows than the
219- // squared length of complex numbers.
220- return Quaternion ( real: . log( q. length) , imaginary: axis * q. halfAngle)
221- }
222-
223- @inlinable
224- public static func log( onePlus q: Quaternion ) -> Quaternion {
225- // If either |r| or ||v||₁ is bounded away from the origin, we don't need
226- // any extra precision, and can just literally compute log(1+z). Note
227- // that this includes part of the sphere |1+q| = 1 where log(onePlus:)
228- // vanishes (where r <= -0.5), but on this portion of the sphere 1+r
229- // is always exact by Sterbenz' lemma, so as long as log( ) produces
230- // a good result, log(1+q) will too.
231- guard 2 * q. real. magnitude < 1 && q. imaginary. oneNorm < 1 else {
232- return log ( . one + q)
233- }
234- // q is in (±0.5, ±1), so we need to evaluate more carefully.
235- // The imaginary part is straightforward:
236- let argument = ( . one + q) . halfAngle
237- let ( â, _) = q. imaginary. unitAxisAndLength
238- let imaginary = â * argument
239- // For the real part, we _could_ use the same approach that we do for
240- // log( ), but we'd need an extra-precise (1+r)², which can potentially
241- // be quite painful to calculate. Instead, we can use an approach that
242- // NevinBR suggested on the Swift forums for complex numbers:
243- //
244- // Re(log 1+q) = (log 1+q + log 1+q̅)/2
245- // = log((1+q)(1+q̅)/2
246- // = log(1 + q + q̅ + qq̅)/2
247- // = log1p((2+r)r + x² + y² + z²)/2
248- //
249- // So now we need to evaluate (2+r)r + x² + y² + z² accurately. To do this,
250- // we employ augmented arithmetic;
251- // (2+r)r + x² + y² + z²
252- // --↓--
253- let rp2 = Augmented . fastTwoSum ( 2 , q. real) // Known that 2 > |r|
254- var ( head, δ) = Augmented . twoProdFMA ( q. real, rp2. head)
255- var tail = δ
256- // head + x² + y² + z²
257- // ----↓----
258- let x ² = Augmented . twoProdFMA ( q. imaginary. x, q. imaginary. x)
259- ( head, δ) = Augmented . twoSum ( head, x ². head)
260- tail += ( δ + x ². tail)
261- // head + y² + z²
262- // ----↓----
263- let y ² = Augmented . twoProdFMA ( q. imaginary. y, q. imaginary. y)
264- ( head, δ) = Augmented . twoSum ( head, y ². head)
265- tail += ( δ + y ². tail)
266- // head + z²
267- // ----↓----
268- let z ² = Augmented . twoProdFMA ( q. imaginary. z, q. imaginary. z)
269- ( head, δ) = Augmented . twoSum ( head, z ². head)
270- tail += ( δ + z ². tail)
271-
272- let s = ( head + tail) . addingProduct ( q. real, rp2. tail)
273- return Quaternion ( real: . log( onePlus: s) / 2 , imaginary: imaginary)
274- }
275-
276- //
277- // MARK: - pow-like functions
278-
279- @inlinable
280- public static func pow( _ q: Quaternion , _ p: Quaternion ) -> Quaternion {
281- // Mathematically, this operation can be expanded in terms of the
282- // quaternionic `exp` and `log` operations as follows:
283- //
284- // ```
285- // pow(q, p) = exp(log(pow(q, p)))
286- // = exp(p * log(q))
287- // ```
288- exp ( p * log( q) )
289- }
290-
291- @inlinable
292- public static func pow( _ q: Quaternion , _ n: Int ) -> Quaternion {
293- // Mathematically, this operation can be expanded in terms of the
294- // quaternionic `exp` and `log` operations as follows:
295- //
296- // ```
297- // pow(q, n) = exp(log(pow(q, n)))
298- // = exp(log(q) * n)
299- // ```
300- guard !q. isZero else { return . zero }
301- // TODO: this implementation is not quite correct, because n may be
302- // rounded in conversion to RealType. This only effects very extreme
303- // cases, so we'll leave it alone for now.
304- return exp ( log ( q) . multiplied ( by: RealType ( n) ) )
305- }
306-
307- @inlinable
308- public static func sqrt( _ q: Quaternion ) -> Quaternion < RealType > {
309- // Mathematically, this operation can be expanded in terms of the
310- // quaternionic `exp` and `log` operations as follows:
311- //
312- // ```
313- // sqrt(q) = q^(1/2) = exp(log(q^(1/2)))
314- // = exp(log(q) * (1/2))
315- // ```
316- guard !q. isZero else { return . zero }
317- return exp ( log ( q) . divided ( by: 2 ) )
318- }
319-
320- @inlinable
321- public static func root( _ q: Quaternion , _ n: Int ) -> Quaternion {
322- // Mathematically, this operation can be expanded in terms of the
323- // quaternionic `exp` and `log` operations as follows:
324- //
325- // ```
326- // root(q, n) = exp(log(root(q, n)))
327- // = exp(log(q) / n)
328- // ```
329- guard !q. isZero else { return . zero }
330- // TODO: this implementation is not quite correct, because n may be
331- // rounded in conversion to RealType. This only effects very extreme
332- // cases, so we'll leave it alone for now.
333- return exp ( log ( q) . divided ( by: RealType ( n) ) )
334- }
335205}
336206
337207extension SIMD3 where Scalar: FloatingPoint {
@@ -349,18 +219,3 @@ extension SIMD3 where Scalar: FloatingPoint {
349219 return ( self / length, length)
350220 }
351221}
352-
353- extension Augmented {
354-
355- // TODO: Move to Augmented.swift
356- @usableFromInline @_transparent
357- internal static func twoSum< T: Real > ( _ a: T , _ b: T ) -> ( head: T , tail: T ) {
358- let head = a + b
359- let x = head - b
360- let y = head - x
361- let ax = a - x
362- let by = b - y
363- let tail = ax + by
364- return ( head, tail)
365- }
366- }
0 commit comments