Skip to content

Commit c23b2d0

Browse files
committed
Add log to quaternion
1 parent 2354e68 commit c23b2d0

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

Sources/QuaternionModule/ElementaryFunctions.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,23 @@ extension Quaternion/*: ElementaryFunctions */ {
164164
return sin(q) / cos(q)
165165
}
166166

167+
// MARK: - log-like functions
168+
@inlinable
169+
public static func log(_ q: Quaternion) -> Quaternion {
170+
// If q is zero or infinite, the phase is undefined, so the result is
171+
// the single exceptional value.
172+
guard q.isFinite && !q.isZero else { return .infinity }
173+
174+
let vectorLength = q.imaginary.length
175+
let scale = q.halfAngle / vectorLength
176+
177+
// We deliberatly choose log(length) over the (faster)
178+
// log(lengthSquared) / 2 which is used for complex numbers; as
179+
// the squared length of quaternions is more prone to overflows than the
180+
// squared length of complex numbers.
181+
return Quaternion(real: .log(q.length), imaginary: q.imaginary * scale)
182+
}
183+
167184
// MARK: - pow-like functions
168185
@inlinable
169186
public static func pow(_ q: Quaternion, _ p: Quaternion) -> Quaternion {

Tests/QuaternionTests/ElementaryFunctionTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,23 @@ final class ElementaryFunctionTests: XCTestCase {
209209
}
210210
}
211211

212+
func testLog<T: Real & FixedWidthFloatingPoint & SIMDScalar>(_ type: T.Type) {
213+
// log(0) = undefined/infinity
214+
XCTAssertFalse(Quaternion<T>.log(Quaternion(real: 0, imaginary: 0, 0, 0)).isFinite)
215+
XCTAssertFalse(Quaternion<T>.log(Quaternion(real:-0, imaginary: 0, 0, 0)).isFinite)
216+
XCTAssertFalse(Quaternion<T>.log(Quaternion(real:-0, imaginary:-0,-0,-0)).isFinite)
217+
XCTAssertFalse(Quaternion<T>.log(Quaternion(real: 0, imaginary:-0,-0,-0)).isFinite)
218+
219+
var g = SystemRandomNumberGenerator()
220+
let values: [Quaternion<T>] = (0..<100).map { _ in
221+
Quaternion(
222+
real: T.random(in: -1 ... 1, using: &g),
223+
imaginary: SIMD3(repeating: T.random(in: -.pi ... .pi, using: &g) / 3))
224+
}
225+
for q in values {
226+
XCTAssertTrue(q.isApproximatelyEqual(to: .log(.exp(q))))
227+
}
228+
}
212229

213230
func testCos<T: Real & FixedWidthFloatingPoint & SIMDScalar>(_ type: T.Type) {
214231
var g = SystemRandomNumberGenerator()
@@ -266,6 +283,8 @@ final class ElementaryFunctionTests: XCTestCase {
266283
testSinh(Float32.self)
267284
testCos(Float32.self)
268285
testSin(Float32.self)
286+
287+
testLog(Float32.self)
269288
}
270289

271290
func testDouble() {
@@ -275,5 +294,7 @@ final class ElementaryFunctionTests: XCTestCase {
275294
testSinh(Float64.self)
276295
testCos(Float64.self)
277296
testSin(Float64.self)
297+
298+
testLog(Float64.self)
278299
}
279300
}

0 commit comments

Comments
 (0)