Skip to content

Commit

Permalink
Refactor Complex module for finer granularity.
Browse files Browse the repository at this point in the history
  • Loading branch information
stephentyrone committed Dec 11, 2021
1 parent 1939f34 commit ba65b0f
Show file tree
Hide file tree
Showing 14 changed files with 495 additions and 370 deletions.
42 changes: 42 additions & 0 deletions Sources/ComplexModule/Complex+AdditiveArithmetic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===--- Complex+AdditiveArithmetic.swift ---------------------*- swift -*-===//
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import RealModule

extension Complex: AdditiveArithmetic {
/// The additive identity, with real and imaginary parts both zero.
///
/// See also: `one`, `i`, `infinity`
@_transparent
public static var zero: Complex {
Complex(0, 0)
}

@_transparent
public static func +(z: Complex, w: Complex) -> Complex {
return Complex(z.x + w.x, z.y + w.y)
}

@_transparent
public static func -(z: Complex, w: Complex) -> Complex {
return Complex(z.x - w.x, z.y - w.y)
}

@_transparent
public static func +=(z: inout Complex, w: Complex) {
z = z + w
}

@_transparent
public static func -=(z: inout Complex, w: Complex) {
z = z - w
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//===--- Arithmetic.swift -------------------------------------*- swift -*-===//
//===--- Complex+AlgebraicField.swift -------------------------*- swift -*-===//
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors
// Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand All @@ -11,81 +11,19 @@

import RealModule

// MARK: - Additive structure
extension Complex: AdditiveArithmetic {
@_transparent
public static func +(z: Complex, w: Complex) -> Complex {
return Complex(z.x + w.x, z.y + w.y)
}

@_transparent
public static func -(z: Complex, w: Complex) -> Complex {
return Complex(z.x - w.x, z.y - w.y)
}

@_transparent
public static func +=(z: inout Complex, w: Complex) {
z = z + w
}

extension Complex: AlgebraicField {
/// The multiplicative identity `1 + 0i`.
@_transparent
public static func -=(z: inout Complex, w: Complex) {
z = z - w
}
}

// MARK: - Vector space structure
//
// Policy: deliberately not using the * and / operators for these at the
// moment, because then there's an ambiguity in expressions like 2*z; is
// that Complex(2) * z or is it RealType(2) * z? This is especially
// problematic in type inference: suppose we have:
//
// let a: RealType = 1
// let b = 2*a
//
// what is the type of b? If we don't have a type context, it's ambiguous.
// If we have a Complex type context, then b will be inferred to have type
// Complex! Obviously, that doesn't help anyone.
//
// TODO: figure out if there's some way to avoid these surprising results
// and turn these into operators if/when we have it.
// (https://github.com/apple/swift-numerics/issues/12)
extension Complex {
/// `self` scaled by `a`.
@usableFromInline @_transparent
internal func multiplied(by a: RealType) -> Complex {
// This can be viewed in two different ways, which are mathematically
// equivalent: either we are computing `self * Complex(a)` (i.e.
// converting `a` to be a complex value, and then using the complex
// multiplication) or we are using the scalar product of the vector
// space structure: `Complex(a*real, a*imaginary)`.
//
// Although these two interpretations are _mathematically_ equivalent,
// they will generate different representations of the point at
// infinity in general. For example, suppose `self` is represented by
// `(infinity, 0)`. Then `self * Complex(1)` would evaluate as
// `(1*infinity - 0*0, 0*infinity + 1*0) = (infinity, nan)`, but
// the vector space interpretation produces `(infinity, 0)`. This does
// not matter much, because these are two representations of the same
// semantic value, but note that one requires four multiplies and two
// additions, while the one we use requires only two real multiplications.
Complex(x*a, y*a)
public static var one: Complex {
Complex(1, 0)
}

/// `self` unscaled by `a`.
@usableFromInline @_transparent
internal func divided(by a: RealType) -> Complex {
// See implementation notes for `multiplied` above.
Complex(x/a, y/a)
}
}

// MARK: - Multiplicative structure
extension Complex: AlgebraicField {
/// The [complex conjugate][conj] of this value.
///
/// [conj]: https://en.wikipedia.org/wiki/Complex_conjugate
@_transparent
public static func *(z: Complex, w: Complex) -> Complex {
return Complex(z.x*w.x - z.y*w.y, z.x*w.y + z.y*w.x)
public var conjugate: Complex {
Complex(x, -y)
}

@_transparent
Expand All @@ -98,11 +36,6 @@ extension Complex: AlgebraicField {
return z * (w.conjugate.divided(by: lenSq))
}

@_transparent
public static func *=(z: inout Complex, w: Complex) {
z = z * w
}

@_transparent
public static func /=(z: inout Complex, w: Complex) {
z = z / w
Expand Down
30 changes: 30 additions & 0 deletions Sources/ComplexModule/Complex+Codable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===--- Complex+Codable.swift --------------------------------*- swift -*-===//
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import RealModule

// FloatingPoint does not refine Codable, so this is a conditional conformance.
extension Complex: Decodable where RealType: Decodable {
public init(from decoder: Decoder) throws {
var unkeyedContainer = try decoder.unkeyedContainer()
let x = try unkeyedContainer.decode(RealType.self)
let y = try unkeyedContainer.decode(RealType.self)
self.init(x, y)
}
}

extension Complex: Encodable where RealType: Encodable {
public func encode(to encoder: Encoder) throws {
var unkeyedContainer = encoder.unkeyedContainer()
try unkeyedContainer.encode(x)
try unkeyedContainer.encode(y)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019 - 2020 Apple Inc. and the Swift Numerics project authors
// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down
25 changes: 25 additions & 0 deletions Sources/ComplexModule/Complex+ElFnsF16.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===--- Complex+ElFnsF16.swift -------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019-2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/*
import RealModule

#if swift(>=5.4) && !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64))
@available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)
extension Complex {
@_specialize(exported: true, target: exp(_:), where RealType == Float16)
public static func _expF16(_ z: Self) -> Self {
_expImpl(z)
}
}
#endif
*/
24 changes: 24 additions & 0 deletions Sources/ComplexModule/Complex+ElFnsF80.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===--- Complex+ElFnsF80.swift -------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019-2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/*
import RealModule

#if (arch(i386) || arch(x86_64)) && !(os(Windows) || os(Android))
extension Complex {
@_specialize(exported: true, target: exp(_:), where RealType == Float80)
public static func _expF80(_ z: Self) -> Self {
_expImpl(z)
}
}
#endif
*/
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- ElementaryFunctions.swift ----------------------------*- swift -*-===//
//===--- Complex+ElementaryFunctions.swift --------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
Expand Down
42 changes: 42 additions & 0 deletions Sources/ComplexModule/Complex+Hashable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===--- Complex+Hashable.swift -------------------------------*- swift -*-===//
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import RealModule

extension Complex: Hashable {
@_transparent
public static func ==(a: Complex, b: Complex) -> Bool {
// Identify all numbers with either component non-finite as a single
// "point at infinity".
guard a.isFinite || b.isFinite else { return true }
// For finite numbers, equality is defined componentwise. Cases where
// only one of a or b is infinite fall through to here as well, but this
// expression correctly returns false for them so we don't need to handle
// them explicitly.
return a.x == b.x && a.y == b.y
}

@_transparent
public func hash(into hasher: inout Hasher) {
// There are two equivalence classes to which we owe special attention:
// All zeros should hash to the same value, regardless of sign, and all
// non-finite numbers should hash to the same value, regardless of
// representation. The correct behavior for zero falls out for free from
// the hash behavior of floating-point, but we need to use a
// representative member for any non-finite values.
if isFinite {
hasher.combine(x)
hasher.combine(y)
} else {
hasher.combine(RealType.infinity)
}
}
}
19 changes: 19 additions & 0 deletions Sources/ComplexModule/Complex+IntegerLiteral.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//===--- Complex+IntegerLiteral.swift -------------------------*- swift -*-===//
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

extension Complex: ExpressibleByIntegerLiteral {
public typealias IntegerLiteralType = RealType.IntegerLiteralType

@inlinable
public init(integerLiteral value: IntegerLiteralType) {
self.init(RealType(integerLiteral: value))
}
}
58 changes: 58 additions & 0 deletions Sources/ComplexModule/Complex+Numeric.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//===--- Complex+Numeric.swift --------------------------------*- swift -*-===//
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

extension Complex: Numeric {

@_transparent
public static func *(z: Complex, w: Complex) -> Complex {
return Complex(z.x*w.x - z.y*w.y, z.x*w.y + z.y*w.x)
}

@_transparent
public static func *=(z: inout Complex, w: Complex) {
z = z * w
}

/// The complex number with specified real part and zero imaginary part.
///
/// Equivalent to `Complex(RealType(real), 0)`.
@inlinable
public init<Other: BinaryInteger>(_ real: Other) {
self.init(RealType(real), 0)
}

/// The complex number with specified real part and zero imaginary part,
/// if it can be constructed without rounding.
@inlinable
public init?<Other: BinaryInteger>(exactly real: Other) {
guard let real = RealType(exactly: real) else { return nil }
self.init(real, 0)
}

/// The ∞-norm of the value (`max(abs(real), abs(imaginary))`).
///
/// If you need the Euclidean norm (a.k.a. 2-norm) use the `length` or
/// `lengthSquared` properties instead.
///
/// Edge cases:
/// - If `z` is not finite, `z.magnitude` is `.infinity`.
/// - If `z` is zero, `z.magnitude` is `0`.
/// - Otherwise, `z.magnitude` is finite and non-zero.
///
/// See also:
/// - `.length`
/// - `.lengthSquared`
@_transparent
public var magnitude: RealType {
guard isFinite else { return .infinity }
return max(abs(x), abs(y))
}
}
23 changes: 23 additions & 0 deletions Sources/ComplexModule/Complex+StringConvertible.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===--- Complex+StringConvertible.swift ----------------------*- swift -*-===//
//
// This source file is part of the Swift Numerics open source project
//
// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

extension Complex: CustomStringConvertible {
public var description: String {
guard isFinite else { return "inf" }
return "(\(x), \(y))"
}
}

extension Complex: CustomDebugStringConvertible {
public var debugDescription: String {
"Complex<\(RealType.self)>(\(String(reflecting: x)), \(String(reflecting: y)))"
}
}
Loading

0 comments on commit ba65b0f

Please sign in to comment.