diff --git a/Sources/ComplexModule/Complex+AdditiveArithmetic.swift b/Sources/ComplexModule/Complex+AdditiveArithmetic.swift new file mode 100644 index 00000000..58f59fe1 --- /dev/null +++ b/Sources/ComplexModule/Complex+AdditiveArithmetic.swift @@ -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 + } +} diff --git a/Sources/ComplexModule/Arithmetic.swift b/Sources/ComplexModule/Complex+AlgebraicField.swift similarity index 57% rename from Sources/ComplexModule/Arithmetic.swift rename to Sources/ComplexModule/Complex+AlgebraicField.swift index a90c8578..d7935359 100644 --- a/Sources/ComplexModule/Arithmetic.swift +++ b/Sources/ComplexModule/Complex+AlgebraicField.swift @@ -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 @@ -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 @@ -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 diff --git a/Sources/ComplexModule/Complex+Codable.swift b/Sources/ComplexModule/Complex+Codable.swift new file mode 100644 index 00000000..9be3e7d9 --- /dev/null +++ b/Sources/ComplexModule/Complex+Codable.swift @@ -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) + } +} diff --git a/Sources/ComplexModule/Differentiable.swift b/Sources/ComplexModule/Complex+Differentiable.swift similarity index 97% rename from Sources/ComplexModule/Differentiable.swift rename to Sources/ComplexModule/Complex+Differentiable.swift index 6775b79d..c38d4503 100644 --- a/Sources/ComplexModule/Differentiable.swift +++ b/Sources/ComplexModule/Complex+Differentiable.swift @@ -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 diff --git a/Sources/ComplexModule/ElementaryFunctions.swift b/Sources/ComplexModule/Complex+ElementaryFunctions.swift similarity index 99% rename from Sources/ComplexModule/ElementaryFunctions.swift rename to Sources/ComplexModule/Complex+ElementaryFunctions.swift index 955ce838..efa685b4 100644 --- a/Sources/ComplexModule/ElementaryFunctions.swift +++ b/Sources/ComplexModule/Complex+ElementaryFunctions.swift @@ -1,4 +1,4 @@ -//===--- ElementaryFunctions.swift ----------------------------*- swift -*-===// +//===--- Complex+ElementaryFunctions.swift --------------------*- swift -*-===// // // This source file is part of the Swift.org open source project // diff --git a/Sources/ComplexModule/Complex+Hashable.swift b/Sources/ComplexModule/Complex+Hashable.swift new file mode 100644 index 00000000..f2daf56a --- /dev/null +++ b/Sources/ComplexModule/Complex+Hashable.swift @@ -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) + } + } +} diff --git a/Sources/ComplexModule/Complex+IntegerLiteral.swift b/Sources/ComplexModule/Complex+IntegerLiteral.swift new file mode 100644 index 00000000..fadb8fc6 --- /dev/null +++ b/Sources/ComplexModule/Complex+IntegerLiteral.swift @@ -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)) + } +} diff --git a/Sources/ComplexModule/Complex+Numeric.swift b/Sources/ComplexModule/Complex+Numeric.swift new file mode 100644 index 00000000..631eed3b --- /dev/null +++ b/Sources/ComplexModule/Complex+Numeric.swift @@ -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(_ 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?(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)) + } +} diff --git a/Sources/ComplexModule/Complex+StringConvertible.swift b/Sources/ComplexModule/Complex+StringConvertible.swift new file mode 100644 index 00000000..d1a26f07 --- /dev/null +++ b/Sources/ComplexModule/Complex+StringConvertible.swift @@ -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)))" + } +} diff --git a/Sources/ComplexModule/Complex.swift b/Sources/ComplexModule/Complex.swift index 8aeabc83..35bd3916 100644 --- a/Sources/ComplexModule/Complex.swift +++ b/Sources/ComplexModule/Complex.swift @@ -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 @@ -93,30 +93,16 @@ extension Complex { set { y = newValue } } - /// The additive identity, with real and imaginary parts both zero. - /// - /// See also: - /// - - /// - .one - /// - .i - /// - .infinity + /// The raw representation of the real part of this value. @_transparent - public static var zero: Complex { - Complex(0, 0) - } + public var _rawX: RealType { x } - /// The multiplicative identity, with real part one and imaginary part zero. - /// - /// See also: - /// - - /// - .zero - /// - .i - /// - .infinity + /// The raw representation of the imaginary part of this value. @_transparent - public static var one: Complex { - Complex(1, 0) - } + public var _rawY: RealType { y } +} +extension Complex { /// The imaginary unit. /// /// See also: @@ -141,12 +127,6 @@ extension Complex { Complex(.infinity, 0) } - /// The complex conjugate of this value. - @_transparent - public var conjugate: Complex { - Complex(x, -y) - } - /// True if this value is finite. /// /// A complex value is finite if neither component is an infinity or nan. @@ -169,7 +149,6 @@ extension Complex { /// representation. /// /// See also: - /// - /// - `.isFinite` /// - `.isSubnormal` /// - `.isZero` @@ -185,7 +164,6 @@ extension Complex { /// the result generally does not have full precision. /// /// See also: - /// - /// - `.isFinite` /// - `.isNormal` /// - `.isZero` @@ -209,27 +187,6 @@ extension Complex { x == 0 && y == 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)) - } - /// A "canonical" representation of the value. /// /// For normal complex numbers with a RealType conforming to @@ -270,29 +227,6 @@ extension Complex { public init(imaginary: RealType) { self.init(0, imaginary) } - - /// The complex number with specified real part and zero imaginary part. - /// - /// Equivalent to `Complex(RealType(real), 0)`. - @inlinable - public init(_ 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?(exactly real: Other) { - guard let real = RealType(exactly: real) else { return nil } - self.init(real, 0) - } - - public typealias IntegerLiteralType = Int - - @inlinable - public init(integerLiteral value: Int) { - self.init(RealType(value)) - } } extension Complex where RealType: BinaryFloatingPoint { @@ -310,220 +244,3 @@ extension Complex where RealType: BinaryFloatingPoint { self.init(x, y) } } - -// MARK: - Conformance to Hashable and Equatable -// -// The Complex type identifies all non-finite points (waving hands slightly, -// we identify all NaNs and infinites as the point at infinity on the Riemann -// sphere). -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) - } - } -} - -// MARK: - Conformance to Codable -// 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) - } -} - -// MARK: - Formatting -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)))" - } -} - -// MARK: - Operations for working with polar form -extension Complex { - - /// The Euclidean norm (a.k.a. 2-norm, `sqrt(real*real + imaginary*imaginary)`). - /// - /// This property takes care to avoid spurious over- or underflow in - /// this computation. For example: - /// - /// let x: Float = 3.0e+20 - /// let x: Float = 4.0e+20 - /// let naive = sqrt(x*x + y*y) // +Inf - /// let careful = Complex(x, y).length // 5.0e+20 - /// - /// Note that it *is* still possible for this property to overflow, - /// because the length can be as much as sqrt(2) times larger than - /// either component, and thus may not be representable in the real type. - /// - /// For most use cases, you can use the cheaper `.magnitude` - /// property (which computes the ∞-norm) instead, which always produces - /// a representable result. - /// - /// Edge cases: - /// - - /// If a complex value is not finite, its `.length` is `infinity`. - /// - /// See also: - /// - - /// - `.magnitude` - /// - `.lengthSquared` - /// - `.phase` - /// - `.polar` - /// - `init(r:θ:)` - @_transparent - public var length: RealType { - let naive = lengthSquared - guard naive.isNormal else { return carefulLength } - return .sqrt(naive) - } - - // Internal implementation detail of `length`, moving slow path off - // of the inline function. Note that even `carefulLength` can overflow - // for finite inputs, but only when the result is outside the range - // of representable values. - @usableFromInline - internal var carefulLength: RealType { - guard isFinite else { return .infinity } - return .hypot(x, y) - } - - /// The squared length `(real*real + imaginary*imaginary)`. - /// - /// This property is more efficient to compute than `length`, but is - /// highly prone to overflow or underflow; for finite values that are - /// not well-scaled, `lengthSquared` is often either zero or - /// infinity, even when `length` is a finite number. Use this property - /// only when you are certain that this value is well-scaled. - /// - /// For many cases, `.magnitude` can be used instead, which is similarly - /// cheap to compute and always returns a representable value. - /// - /// See also: - /// - - /// - `.length` - /// - `.magnitude` - @_transparent - public var lengthSquared: RealType { - x*x + y*y - } - - @available(*, unavailable, renamed: "lengthSquared") - public var unsafeLengthSquared: RealType { lengthSquared } - - /// The phase (angle, or "argument"). - /// - /// Returns the angle (measured above the real axis) in radians. If - /// the complex value is zero or infinity, the phase is not defined, - /// and `nan` is returned. - /// - /// Edge cases: - /// - - /// If the complex value is zero or non-finite, phase is `nan`. - /// - /// See also: - /// - - /// - `.length` - /// - `.polar` - /// - `init(r:θ:)` - @inlinable - public var phase: RealType { - guard isFinite && !isZero else { return .nan } - return .atan2(y: y, x: x) - } - - /// The length and phase (or polar coordinates) of this value. - /// - /// Edge cases: - /// - - /// If the complex value is zero or non-finite, phase is `.nan`. - /// If the complex value is non-finite, length is `.infinity`. - /// - /// See also: - /// - - /// - `.length` - /// - `.phase` - /// - `init(r:θ:)` - public var polar: (length: RealType, phase: RealType) { - (length, phase) - } - - /// Creates a complex value specified with polar coordinates. - /// - /// Edge cases: - /// - - /// - Negative lengths are interpreted as reflecting the point through the - /// origin, i.e.: - /// ``` - /// Complex(length: -r, phase: θ) == -Complex(length: r, phase: θ) - /// ``` - /// - For any `θ`, even `.infinity` or `.nan`: - /// ``` - /// Complex(length: .zero, phase: θ) == .zero - /// ``` - /// - For any `θ`, even `.infinity` or `.nan`, if `r` is infinite then: - /// ``` - /// Complex(length: r, phase: θ) == .infinity - /// ``` - /// - Otherwise, `θ` must be finite, or a precondition failure occurs. - /// - /// See also: - /// - - /// - `.length` - /// - `.phase` - /// - `.polar` - @inlinable - public init(length: RealType, phase: RealType) { - if phase.isFinite { - self = Complex(.cos(phase), .sin(phase)).multiplied(by: length) - } else { - precondition( - length.isZero || length.isInfinite, - "Either phase must be finite, or length must be zero or infinite." - ) - self = Complex(length) - } - } -} diff --git a/Sources/ComplexModule/Polar.swift b/Sources/ComplexModule/Polar.swift new file mode 100644 index 00000000..c9b40df0 --- /dev/null +++ b/Sources/ComplexModule/Polar.swift @@ -0,0 +1,157 @@ +//===--- Polar.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 { + /// The Euclidean norm (a.k.a. 2-norm, `sqrt(real*real + imaginary*imaginary)`). + /// + /// This property takes care to avoid spurious over- or underflow in + /// this computation. For example: + /// + /// let x: Float = 3.0e+20 + /// let x: Float = 4.0e+20 + /// let naive = sqrt(x*x + y*y) // +Inf + /// let careful = Complex(x, y).length // 5.0e+20 + /// + /// Note that it *is* still possible for this property to overflow, + /// because the length can be as much as sqrt(2) times larger than + /// either component, and thus may not be representable in the real type. + /// + /// For most use cases, you can use the cheaper `.magnitude` + /// property (which computes the ∞-norm) instead, which always produces + /// a representable result. + /// + /// Edge cases: + /// - + /// If a complex value is not finite, its `.length` is `infinity`. + /// + /// See also: + /// - + /// - `.magnitude` + /// - `.lengthSquared` + /// - `.phase` + /// - `.polar` + /// - `init(r:θ:)` + @_transparent + public var length: RealType { + let naive = lengthSquared + guard naive.isNormal else { return carefulLength } + return .sqrt(naive) + } + + // Internal implementation detail of `length`, moving slow path off + // of the inline function. Note that even `carefulLength` can overflow + // for finite inputs, but only when the result is outside the range + // of representable values. + @usableFromInline + internal var carefulLength: RealType { + guard isFinite else { return .infinity } + return .hypot(x, y) + } + + /// The squared length `(real*real + imaginary*imaginary)`. + /// + /// This property is more efficient to compute than `length`, but is + /// highly prone to overflow or underflow; for finite values that are + /// not well-scaled, `lengthSquared` is often either zero or + /// infinity, even when `length` is a finite number. Use this property + /// only when you are certain that this value is well-scaled. + /// + /// For many cases, `.magnitude` can be used instead, which is similarly + /// cheap to compute and always returns a representable value. + /// + /// See also: + /// - + /// - `.length` + /// - `.magnitude` + @_transparent + public var lengthSquared: RealType { + x*x + y*y + } + + @available(*, unavailable, renamed: "lengthSquared") + public var unsafeLengthSquared: RealType { lengthSquared } + + /// The phase (angle, or "argument"). + /// + /// Returns the angle (measured above the real axis) in radians. If + /// the complex value is zero or infinity, the phase is not defined, + /// and `nan` is returned. + /// + /// Edge cases: + /// - + /// If the complex value is zero or non-finite, phase is `nan`. + /// + /// See also: + /// - + /// - `.length` + /// - `.polar` + /// - `init(r:θ:)` + @inlinable + public var phase: RealType { + guard isFinite && !isZero else { return .nan } + return .atan2(y: y, x: x) + } + + /// The length and phase (or polar coordinates) of this value. + /// + /// Edge cases: + /// - + /// If the complex value is zero or non-finite, phase is `.nan`. + /// If the complex value is non-finite, length is `.infinity`. + /// + /// See also: + /// - + /// - `.length` + /// - `.phase` + /// - `init(r:θ:)` + public var polar: (length: RealType, phase: RealType) { + (length, phase) + } + + /// Creates a complex value specified with polar coordinates. + /// + /// Edge cases: + /// - + /// - Negative lengths are interpreted as reflecting the point through the + /// origin, i.e.: + /// ``` + /// Complex(length: -r, phase: θ) == -Complex(length: r, phase: θ) + /// ``` + /// - For any `θ`, even `.infinity` or `.nan`: + /// ``` + /// Complex(length: .zero, phase: θ) == .zero + /// ``` + /// - For any `θ`, even `.infinity` or `.nan`, if `r` is infinite then: + /// ``` + /// Complex(length: r, phase: θ) == .infinity + /// ``` + /// - Otherwise, `θ` must be finite, or a precondition failure occurs. + /// + /// See also: + /// - + /// - `.length` + /// - `.phase` + /// - `.polar` + @inlinable + public init(length: RealType, phase: RealType) { + if phase.isFinite { + self = Complex(.cos(phase), .sin(phase)).multiplied(by: length) + } else { + precondition( + length.isZero || length.isInfinite, + "Either phase must be finite, or length must be zero or infinite." + ) + self = Complex(length) + } + } +} diff --git a/Sources/ComplexModule/Scale.swift b/Sources/ComplexModule/Scale.swift new file mode 100644 index 00000000..d678038d --- /dev/null +++ b/Sources/ComplexModule/Scale.swift @@ -0,0 +1,55 @@ +//===--- 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 +// +//===----------------------------------------------------------------------===// + +// 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) + } + + /// `self` unscaled by `a`. + @usableFromInline @_transparent + internal func divided(by a: RealType) -> Complex { + // See implementation notes for `multiplied` above. + Complex(x/a, y/a) + } +}