Skip to content

Commit 09602a1

Browse files
committed
Fix a Swift 5.6-related warning. Make complex numbers involving infinity and NaN work.
1 parent e8c1cb9 commit 09602a1

File tree

3 files changed

+63
-10
lines changed

3 files changed

+63
-10
lines changed

Sources/NumberKit/Complex.swift

+35-9
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import Foundation
3030
/// the complex type implementation is based on.
3131
/// - Todo: Implement the `Arithmetic` protocol. This requires that complex numbers are
3232
/// mutable.
33-
public protocol ComplexNumber: Equatable {
33+
public protocol ComplexNumber: Hashable,
34+
CustomStringConvertible {
3435

3536
/// The floating point number type on which this complex number is based.
3637
associatedtype Float: FloatingPoint
@@ -153,14 +154,29 @@ public struct Complex<T: FloatingPointNumber>: ComplexNumber,
153154

154155
/// Creates a complex number with the given real and imaginary parts.
155156
public init(_ re: T, _ im: T) {
156-
self.re = re
157-
self.im = im
157+
// Normalize complex numbers involving NaN
158+
if re.isNaN {
159+
self.re = re
160+
self.im = T(0)
161+
} else if im.isNaN {
162+
self.re = im
163+
self.im = T(0)
164+
// Normalize infinite complex numbers
165+
} else if re.isInfinite {
166+
self.re = re
167+
self.im = T(0)
168+
} else if im.isInfinite { // imaginary parts are never infinity
169+
self.re = .nan
170+
self.im = T(0)
171+
} else {
172+
self.re = re
173+
self.im = im
174+
}
158175
}
159176

160177
/// Creates a complex number from polar coordinates
161178
public init(abs: T, arg: T) {
162-
self.re = abs * arg.cos
163-
self.im = abs * arg.sin
179+
self.init(abs * arg.cos, abs * arg.sin)
164180
}
165181

166182
/// Creates a real number initialized to integer `value`.
@@ -175,7 +191,7 @@ public struct Complex<T: FloatingPointNumber>: ComplexNumber,
175191

176192
/// Returns a textual representation of this complex number
177193
public var description: String {
178-
if im.isZero {
194+
if im.isZero || re.isNaN || re.isInfinite {
179195
return String(describing: re)
180196
} else if re.isZero {
181197
return String(describing: im) + "i"
@@ -224,7 +240,9 @@ public struct Complex<T: FloatingPointNumber>: ComplexNumber,
224240
/// Returns the ∞-norm of this complex number. Use `norm` if the Euclidean norm
225241
/// is needed.
226242
public var magnitude: T {
227-
if self.isFinite {
243+
if self.isNaN {
244+
return .nan
245+
} else if self.isFinite {
228246
return max(self.re.abs, self.im.abs)
229247
} else {
230248
return .infinity
@@ -258,8 +276,16 @@ public struct Complex<T: FloatingPointNumber>: ComplexNumber,
258276

259277
/// Returns the reciprocal of this complex number.
260278
public var reciprocal: Complex<T> {
261-
let s = re * re + im * im
262-
return Complex(re / s, -im / s)
279+
if self.isNaN {
280+
return self
281+
} else if self.isZero {
282+
return .infinity
283+
} else if self.isInfinite {
284+
return .zero
285+
} else {
286+
let s = re * re + im * im
287+
return Complex(re / s, -im / s)
288+
}
263289
}
264290

265291
/// Returns the norm of this complex number.

Sources/NumberKit/Rational.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import Foundation
2727
/// not be zero.
2828
public protocol RationalNumber: SignedNumeric,
2929
Comparable,
30-
Hashable {
30+
Hashable,
31+
CustomStringConvertible {
3132

3233
/// The integer type on which this rational number is based.
3334
associatedtype Integer: IntegerNumber

Tests/NumberKitTests/ComplexTests.swift

+26
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,34 @@ class ComplexTests: XCTestCase {
3737
XCTAssertEqual(c * c, -1)
3838
}
3939

40+
func testSpecialCases() {
41+
let c1: Complex<Double> = Complex(.infinity, 12.0)
42+
XCTAssertTrue(c1.isInfinite)
43+
XCTAssertFalse(c1.isNaN)
44+
let c2: Complex<Double> = Complex(.infinity, -7.3)
45+
XCTAssertEqual(c1, c2)
46+
let c3: Complex<Double> = Complex(.nan, -1.2)
47+
XCTAssertTrue(c3.isNaN)
48+
XCTAssertFalse(c3.isInfinite)
49+
XCTAssertEqual(c3.im, 0.0)
50+
let c4: Complex<Double> = Complex(3.4, .nan)
51+
XCTAssertTrue(c4.isNaN)
52+
XCTAssertFalse(c4.isInfinite)
53+
XCTAssertEqual(c4.im, 0.0)
54+
XCTAssertNotEqual(c3, c4)
55+
XCTAssertTrue(c4.abs.isNaN)
56+
let c5: Complex<Double> = Complex(1.234, .infinity)
57+
XCTAssertTrue(c5.isNaN)
58+
XCTAssertFalse(c5.isInfinite)
59+
XCTAssertTrue(c5.magnitude.isNaN)
60+
let c6: Complex<Double> = .zero
61+
XCTAssertTrue(c6.reciprocal.isInfinite)
62+
XCTAssertTrue(c1.reciprocal.isZero)
63+
}
64+
4065
static let allTests = [
4166
("testConstructors", testConstructors),
4267
("testImaginaryInvariant", testImaginaryInvariant),
68+
("testSpecialCases", testSpecialCases),
4369
]
4470
}

0 commit comments

Comments
 (0)