Skip to content

Refactor complex #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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