NumericAnnex/Sources/Complex.swift

1053 lines
35 KiB
Swift

//
// Complex.swift
// NumericAnnex
//
// Created by Xiaodi Wu on 3/25/17.
//
// Note
// ====
//
// For maximum consistency with corresponding functions in C/C++, checks for
// special values in `naturalExponential()`, `squareRoot()`, trigonometric
// functions, and hyperbolic functions are adapted from libc++.
//
// Code in libc++ is dual-licensed under the MIT and UIUC/NCSA licenses.
// Copyright © 2009-2017 contributors to the LLVM/libc++ project.
/// A type to represent a complex value in Cartesian form.
///
/// - Note: `Complex64` is a type alias for `Complex<Float>` and `Complex128` is
/// a type alias for `Complex<Double>`.
///
/// Create new instances of `Complex<T>` using integer or floating-point
/// literals and the imaginary unit `Complex<T>.i`. For example:
///
/// ```swift
/// let x = 2 + 4 * .i // `x` is of type `Complex<Double>`
/// let y = 3.5 + 7 * .i // `y` is of type `Complex<Double>`
///
/// let z: Complex64 = .e + .pi * .i // `z` is of type `Complex<Float>`
/// ```
///
/// Additional Considerations
/// -------------------------
///
/// Floating-point types have special values that represent infinity or NaN
/// ("not a number"). Complex functions in different languages may return
/// different results when working with special values.
///
/// Many complex functions have [branch cuts][dfn], which are curves in the
/// complex plane across which a function is discontinuous. Different languages
/// may adopt different branch cut structures for the same complex function.
///
/// Implementations in `Complex<T>` adhere to the [C standard][std] (Annex G) as
/// closely as possible with respect to special values and branch cuts.
///
/// To users unfamiliar with complex functions, the principal value returned by
/// some complex functions may be unexpected. For example,
/// `Double.cbrt(-8) == -2`, which is the __real root__, while
/// `Complex.cbrt(-8) == 2 * Complex.exp(.i * .pi / 3)`, which is the
/// __principal root__.
///
/// [dfn]: http://mathworld.wolfram.com/BranchCut.html
/// [std]: http://www.open-std.org/JTC1/SC22/WG14/www/standards.html#9899
@_fixed_layout
public struct Complex<T : Real> : Codable
where T : Codable & _ExpressibleByBuiltinFloatLiteral {
// ---------------------------------------------------------------------------
// MARK: Stored Properties
// ---------------------------------------------------------------------------
/// The real component of the complex value.
public var real: T
/// The imaginary component of the complex value.
public var imaginary: T
// ---------------------------------------------------------------------------
// MARK: Initializers
// ---------------------------------------------------------------------------
/// Creates a new value from the given real and imaginary components.
///
/// - Parameters:
/// - real: The new value's real component.
/// - imaginary: The new value's imaginary component.
@_transparent // @_inlineable
public init(real: T = 0, imaginary: T = 0) {
self.real = real
self.imaginary = imaginary
}
/// Creates a new value from the given polar coordinates `(r, theta)`.
///
/// - Parameters:
/// - r: The new value's radial coordinate.
/// - theta: The new value's angular coordinate.
@_transparent // @_inlineable
public init(r: T, theta: T) {
self.real = r * T.cos(theta)
self.imaginary = r * T.sin(theta)
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: Int) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: Int8) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: Int16) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: Int32) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: Int64) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: UInt) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: UInt8) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: UInt16) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: UInt32) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// If two representable values are equally close, the result is the value
/// with more trailing zeros in its significand bit pattern.
///
/// - Parameters:
/// - real: The integer to convert to a floating-point real component.
@_transparent // @_inlineable
init(_ real: UInt64) {
self.real = T(real)
self.imaginary = 0
}
// ---------------------------------------------------------------------------
// FIXME: If corresponding requirements are added to FloatingPoint
// add init<U : Integer>(_: U) as well as init?<U : Integer>(exactly: U).
// ---------------------------------------------------------------------------
}
extension Complex where T : BinaryFloatingPoint {
// ---------------------------------------------------------------------------
// MARK: Initializers (Constrained)
// ---------------------------------------------------------------------------
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// - Note: This initializer creates only instances of
/// `Complex<T> where T : BinaryFloatingPoint`.
///
/// - Parameters:
/// - real: The value to convert to a real component of type `T`.
@_transparent // @_inlineable
public init(_ real: Float) {
self.real = T(real)
self.imaginary = 0
}
/// Creates a new value from the given real component, rounded to the closest
/// possible representation.
///
/// - Note: This initializer creates only instances of
/// `Complex<T> where T : BinaryFloatingPoint`.
///
/// - Parameters:
/// - real: The value to convert to a real component of type `T`.
@_transparent // @_inlineable
public init(_ real: Double) {
self.real = T(real)
self.imaginary = 0
}
// ---------------------------------------------------------------------------
// FIXME: If corresponding requirements are added to BinaryFloatingPoint
// add init<U : BinaryFloatingPoint>(_: U) as well as
// init?<U : BinaryFloatingPoint>(exactly: U).
// ---------------------------------------------------------------------------
}
extension Complex {
// ---------------------------------------------------------------------------
// MARK: Static Properties
// ---------------------------------------------------------------------------
/// The imaginary unit _i_.
@_transparent // @_inlineable
public static var i: Complex {
return Complex(real: 0, imaginary: 1)
}
// ---------------------------------------------------------------------------
// MARK: Computed Properties
// ---------------------------------------------------------------------------
/// The principal argument (phase angle) of this value.
///
/// Special cases are handled as if calling `T.atan2(imaginary, real)`. The
/// result lies in the range [-π, π].
@_transparent // @_inlineable
public var argument: T {
return T.atan2(imaginary, real)
}
#if false
/// A Boolean value indicating whether the instance's representation is in
/// canonical form.
///
/// A complex value is represented in canonical form if the its real and
/// imaginary components are both represented in canonical form, as defined in
/// the [IEEE 754 specification][spec]. Every `Float` or `Double` value is
/// canonical.
///
/// [spec]: http://ieeexplore.ieee.org/servlet/opac?punumber=4610933
@_transparent // @_inlineable
public var isCanonical: Bool {
return real.isCanonical && imaginary.isCanonical
}
#endif
/// A Boolean value indicating whether the instance is finite.
///
/// A complex value is finite if its real and imaginary components are both
/// finite. A component is finite if it is not infinity or NaN.
@_transparent // @_inlineable
public var isFinite: Bool {
return real.isFinite && imaginary.isFinite
}
/// A Boolean value indicating whether the instance is infinite.
///
/// A complex value is infinite if at least one of its components (real or
/// imaginary) is infinite, even if the other component is NaN.
///
/// Note that `isFinite` and `isInfinite` do not form a dichotomy because NaN
/// is neither finite nor infinite.
@_transparent // @_inlineable
public var isInfinite: Bool {
return real.isInfinite || imaginary.isInfinite
}
/// A Boolean value indicating whether the instance is NaN ("not a number").
///
/// A complex value is NaN if at least one of its components (real or
/// imaginary) is NaN and the other component is not infinite.
///
/// Because NaN is not equal to any value, including NaN, use this property
/// instead of the equal-to operator (`==`) or not-equal-to operator (`!=`) to
/// test whether a value is or is not NaN.
///
/// This property is `true` for both quiet and signaling NaNs.
@_transparent // @_inlineable
public var isNaN: Bool {
return (real.isNaN && !imaginary.isInfinite) ||
(imaginary.isNaN && !real.isInfinite)
}
/// A Boolean value indicating whether the instance is a signaling NaN.
///
/// A complex value is a signaling NaN if at least one of its components (real
/// or imaginary) is a signaling NaN and the other component is not infinite.
///
/// Signaling NaNs typically raise the Invalid flag when used in general
/// computing operations.
@_transparent // @_inlineable
public var isSignalingNaN: Bool {
return (real.isSignalingNaN && !imaginary.isInfinite) ||
(imaginary.isSignalingNaN && !real.isInfinite)
}
/// A Boolean value indicating whether the instance is equal to zero.
///
/// A complex value is equal to zero if its real and imaginary components both
/// represent either `-0.0` or `+0.0`.
@_transparent // @_inlineable
public var isZero: Bool {
return real.isZero && imaginary.isZero
}
/// The magnitude (modulus, absolute value) of this value.
///
/// Special cases are handled as if calling `T.hypot(real, imaginary)`.
@_transparent // @_inlineable
public var magnitude: T {
return T.hypot(real, imaginary)
}
/// The squared magnitude (field norm, absolute square) of this value.
///
/// This is less costly to compute than `magnitude` and, in some cases, can be
/// used in its place. For example, if `a.magnitude > b.magnitude`, then
/// `a.squaredMagnitude > b.squaredMagnitude`.
@_transparent // @_inlineable
public var squaredMagnitude: T {
if real.isInfinite { return abs(real) }
if imaginary.isInfinite { return abs(imaginary) }
return real * real + imaginary * imaginary
}
// ---------------------------------------------------------------------------
// MARK: Methods
// ---------------------------------------------------------------------------
/// Returns the complex conjugate of this value, obtained by reversing the
/// sign of the imaginary component.
@_transparent // @_inlineable
public func conjugate() -> Complex {
return Complex(real: real, imaginary: -imaginary)
}
/// Returns the projection of this value onto the Riemann sphere.
///
/// For most values `z`, `z.projection() == z`. The projection of any complex
/// infinity is positive real infinity. The sign of the imaginary component is
/// preserved in the sign of zero; that is,
/// `z.projection().imaginary.sign == z.imaginary.sign`.
@_transparent // @_inlineable
public func projection() -> Complex {
if real.isInfinite || imaginary.isInfinite {
return Complex(
real: .infinity, imaginary: T(signOf: imaginary, magnitudeOf: 0)
)
}
return self
}
/// Returns the reciprocal (multiplicative inverse) of this value, or NaN
/// ("not a number") if this value is infinite, NaN, or zero.
@_transparent // @_inlineable
public func reciprocal() -> Complex {
let denominator = squaredMagnitude
return Complex(
real: real / denominator, imaginary: -imaginary / denominator
)
}
}
extension Complex : ExpressibleByFloatLiteral {
// ---------------------------------------------------------------------------
// MARK: ExpressibleByFloatLiteral
// ---------------------------------------------------------------------------
@_transparent // @_inlineable
public init(floatLiteral value: T) {
self.real = value
self.imaginary = 0
}
}
extension Complex : ExpressibleByIntegerLiteral {
// ---------------------------------------------------------------------------
// MARK: ExpressibleByIntegerLiteral
// ---------------------------------------------------------------------------
@_transparent // @_inlineable
public init(integerLiteral value: Int) {
self.real = T(value)
self.imaginary = 0
}
}
extension Complex : CustomStringConvertible {
// ---------------------------------------------------------------------------
// MARK: CustomStringConvertible
// ---------------------------------------------------------------------------
@_transparent // @_inlineable
public var description: String {
return real.isNaN && real.sign == .minus
// At present, -NaN is described as "nan", which is acceptable for real
// values. However, it is arguably misleading to describe -NaN - NaNi as
// "nan + nani" or "nan - nani". Therefore, handle this case separately.
? imaginary.sign == .minus
? "-\(-real) - \(-imaginary)i"
: "-\(-real) + \(imaginary)i"
: imaginary.sign == .minus
? "\(real) - \(-imaginary)i"
: "\(real) + \(imaginary)i"
}
}
extension Complex : Equatable {
// ---------------------------------------------------------------------------
// MARK: Equatable
// ---------------------------------------------------------------------------
@_transparent // @_inlineable
public static func == (lhs: Complex, rhs: Complex) -> Bool {
return lhs.real == rhs.real && lhs.imaginary == rhs.imaginary
}
}
extension Complex : Hashable {
// ---------------------------------------------------------------------------
// MARK: Hashable
// ---------------------------------------------------------------------------
// @_transparent // @_inlineable
public var hashValue: Int {
return _Hash._combine(real, imaginary)
}
}
extension Complex : Numeric {
// ---------------------------------------------------------------------------
// MARK: Numeric
// ---------------------------------------------------------------------------
// @_transparent // @_inlineable
public init?<U>(exactly source: U) where U : BinaryInteger {
guard let t = T(exactly: source) else { return nil }
self.real = t
self.imaginary = 0
}
@_transparent // @_inlineable
public static func + (lhs: Complex, rhs: Complex) -> Complex {
var lhs = lhs
lhs += rhs
return lhs
}
@_transparent // @_inlineable
public static func += (lhs: inout Complex, rhs: Complex) {
lhs.real += rhs.real
lhs.imaginary += rhs.imaginary
}
@_transparent // @_inlineable
public static func - (lhs: Complex, rhs: Complex) -> Complex {
var lhs = lhs
lhs -= rhs
return lhs
}
@_transparent // @_inlineable
public static func -= (lhs: inout Complex, rhs: Complex) {
lhs.real -= rhs.real
lhs.imaginary -= rhs.imaginary
}
@_transparent // @_inlineable
public static func * (lhs: Complex, rhs: Complex) -> Complex {
var a = lhs.real, b = lhs.imaginary, c = rhs.real, d = rhs.imaginary
let ac = a * c, bd = b * d, ad = a * d, bc = b * c
let x = ac - bd
let y = ad + bc
// Recover infinities that computed as NaN + iNaN.
// See C11 Annex G.
if x.isNaN && y.isNaN {
var recalculate = false
if a.isInfinite || b.isInfinite {
// "Box" the infinity and change NaNs in the other operand to 0.
a = T(signOf: a, magnitudeOf: a.isInfinite ? 1 : 0)
b = T(signOf: b, magnitudeOf: b.isInfinite ? 1 : 0)
if c.isNaN { c = T(signOf: c, magnitudeOf: 0) }
if d.isNaN { d = T(signOf: d, magnitudeOf: 0) }
recalculate = true
}
if c.isInfinite || d.isInfinite {
// "Box" the infinity and change NaNs in the other operand to 0.
if a.isNaN { a = T(signOf: a, magnitudeOf: 0) }
if b.isNaN { b = T(signOf: b, magnitudeOf: 0) }
c = T(signOf: c, magnitudeOf: c.isInfinite ? 1 : 0)
d = T(signOf: d, magnitudeOf: d.isInfinite ? 1 : 0)
recalculate = true
}
if !recalculate &&
(ac.isInfinite || bd.isInfinite || ad.isInfinite || bc.isInfinite) {
// Recover infinities from overflow by changing NaNs to 0.
if a.isNaN { a = T(signOf: a, magnitudeOf: 0) }
if b.isNaN { b = T(signOf: b, magnitudeOf: 0) }
if c.isNaN { c = T(signOf: c, magnitudeOf: 0) }
if d.isNaN { d = T(signOf: d, magnitudeOf: 0) }
recalculate = true
}
if recalculate {
return Complex(
real: .infinity * (a * c - b * d),
imaginary: .infinity * (a * d + b * c)
)
}
}
return Complex(real: x, imaginary: y)
/*
return Complex(
real: lhs.real * rhs.real - lhs.imaginary * rhs.imaginary,
imaginary: lhs.real * rhs.imaginary + lhs.imaginary * rhs.real
)
*/
}
@_transparent // @_inlineable
public static func *= (lhs: inout Complex, rhs: Complex) {
lhs = lhs * rhs
}
}
extension Complex : SignedNumeric {
// ---------------------------------------------------------------------------
// MARK: SignedNumeric
// ---------------------------------------------------------------------------
@_transparent // @_inlineable
public static prefix func - (operand: Complex) -> Complex {
return Complex(real: -operand.real, imaginary: -operand.imaginary)
}
@_transparent // @_inlineable
public mutating func negate() {
real.negate()
imaginary.negate()
}
}
extension Complex : Math {
// ---------------------------------------------------------------------------
// MARK: Math
// ---------------------------------------------------------------------------
@_transparent // @_inlineable
public static var pi: Complex {
return Complex(real: .pi)
}
@_transparent // @_inlineable
public static var e: Complex {
return Complex(real: .e)
}
@_transparent // @_inlineable
public static var phi: Complex {
return Complex(real: .phi)
}
@_transparent // @_inlineable
public static func / (lhs: Complex, rhs: Complex) -> Complex {
var a = lhs.real, b = lhs.imaginary, c = rhs.real, d = rhs.imaginary
var x: T
var y: T
// Prevent avoidable overflow; see Numerical Recipes.
if c.magnitude >= d.magnitude {
let ratio = d / c
let denominator = c + d * ratio
x = (a + b * ratio) / denominator
y = (b - a * ratio) / denominator
} else {
let ratio = c / d
let denominator = c * ratio + d
x = (a * ratio + b) / denominator
y = (b * ratio - a) / denominator
}
// Recover infinities and zeros that computed as NaN + iNaN.
// See C11 Annex G.
if x.isNaN && y.isNaN {
if c == 0 && d == 0 && (!a.isNaN || !b.isNaN) {
x = T(signOf: c, magnitudeOf: .infinity) * a
y = T(signOf: c /* sic */, magnitudeOf: .infinity) * b
} else if (a.isInfinite || b.isInfinite) && c.isFinite && d.isFinite {
a = T(signOf: a, magnitudeOf: a.isInfinite ? 1 : 0)
b = T(signOf: b, magnitudeOf: b.isInfinite ? 1 : 0)
x = .infinity * (a * c + b * d)
y = .infinity * (b * c - a * d)
} else if (c.isInfinite || d.isInfinite) && a.isFinite && b.isFinite {
c = T(signOf: c, magnitudeOf: c.isInfinite ? 1 : 0)
d = T(signOf: d, magnitudeOf: d.isInfinite ? 1 : 0)
x = 0 * (a * c + b * d)
y = 0 * (b * c - a * d)
}
}
return Complex(real: x, imaginary: y)
/*
let denominator = rhs.squaredMagnitude
return Complex(
real:
(lhs.real * rhs.real + lhs.imaginary * rhs.imaginary) / denominator,
imaginary:
(lhs.imaginary * rhs.real - lhs.real * rhs.imaginary) / denominator
)
*/
}
@_transparent // @_inlineable
public static func /= (lhs: inout Complex, rhs: Complex) {
lhs = lhs / rhs
}
@_transparent // @_inlineable
public static func ** (lhs: Complex, rhs: Complex) -> Complex {
return Complex.exp(rhs * Complex.log(lhs))
}
@_transparent // @_inlineable
public static func **= (lhs: inout Complex, rhs: Complex) {
lhs = lhs ** rhs
}
@_transparent // @_inlineable
public func naturalExponential() -> Complex {
if real.isNaN && imaginary == 0 { return self }
var im = imaginary
if real.isInfinite {
if real < 0 && !im.isFinite {
im = 1
} else if im == 0 || !im.isFinite {
if im.isInfinite { im = .nan }
return Complex(real: real, imaginary: im)
}
}
return Complex(r: T.exp(real), theta: im)
}
@_transparent // @_inlineable
public func naturalLogarithm() -> Complex {
return Complex(real: T.log(magnitude), imaginary: argument)
}
@_transparent // @_inlineable
public func commonLogarithm() -> Complex {
return Complex.log(self) / Complex.log(10 as Complex)
}
@_transparent // @_inlineable
public func squareRoot() -> Complex {
if imaginary.isInfinite {
return Complex(real: .infinity, imaginary: imaginary)
}
if real.isInfinite {
if real > 0 {
return Complex(
real: real,
imaginary: imaginary.isNaN ? imaginary :
T(signOf: imaginary, magnitudeOf: 0)
)
}
return Complex(
real: imaginary.isNaN ? imaginary : 0,
imaginary: T(signOf: imaginary, magnitudeOf: real)
)
}
// Guard intermediate results and enforce a branch cut; see Numerical
// Recipes.
if real == 0 && imaginary == 0 { return 0 }
let w: T
let x = abs(real), y = abs(imaginary)
if x >= y {
let r = y / x
w = T.sqrt(x) * T.sqrt((1 + T.sqrt(1 + r * r)) / 2)
} else {
let r = x / y
w = T.sqrt(y) * T.sqrt((r + T.sqrt(1 + r * r)) / 2)
}
if real >= 0 {
return Complex(real: w, imaginary: imaginary / (2 * w))
}
/* real < 0 */
return Complex(real: y / (2 * w), imaginary: imaginary >= 0 ? w : -w)
/*
return Complex(r: T.sqrt(magnitude), theta: argument / 2)
*/
}
@_transparent // @_inlineable
public func cubeRoot() -> Complex {
return Complex.exp(Complex.log(self) / 3)
/*
return Complex(r: T.cbrt(magnitude), theta: argument / 3)
*/
}
@_transparent // @_inlineable
public func sine() -> Complex {
let sinh = Complex.sinh(Complex(real: -imaginary, imaginary: real))
return Complex(real: sinh.imaginary, imaginary: -sinh.real)
}
@_transparent // @_inlineable
public func cosine() -> Complex {
return Complex.cosh(Complex(real: -imaginary, imaginary: real))
}
@_transparent // @_inlineable
public func tangent() -> Complex {
let tanh = Complex.tanh(Complex(real: -imaginary, imaginary: real))
return Complex(real: tanh.imaginary, imaginary: -tanh.real)
}
@_transparent // @_inlineable
public func inverseSine() -> Complex {
let asinh = Complex.asinh(Complex(real: -imaginary, imaginary: real))
return Complex(real: asinh.imaginary, imaginary: -asinh.real)
}
@_transparent // @_inlineable
public func inverseCosine() -> Complex {
if real.isNaN {
if imaginary.isInfinite {
return Complex(real: real, imaginary: -imaginary)
}
return Complex(real: real, imaginary: real)
}
if real.isInfinite {
if imaginary.isNaN {
return Complex(real: imaginary, imaginary: real)
}
if imaginary.isInfinite {
return Complex(
real: real < 0 ? .pi * 3 / 4 : .pi / 4,
imaginary: -imaginary
)
}
return real < 0
? Complex(real: .pi, imaginary: imaginary.sign == .minus ? -real : real)
: Complex(real: 0, imaginary: imaginary.sign == .minus ? real : -real)
}
if real == 0 && (imaginary.isNaN || imaginary == 0) {
return Complex(real: .pi / 2, imaginary: -imaginary)
}
if imaginary.isInfinite {
return Complex(real: .pi / 2, imaginary: -imaginary)
}
let a = self ** 2 - 1
let b = Complex.log(self + Complex.sqrt(a))
return Complex(
real: abs(b.imaginary),
imaginary: imaginary.sign == .minus ? abs(b.real) : -abs(b.real)
)
}
@_transparent // @_inlineable
public func inverseTangent() -> Complex {
let atanh = Complex.atanh(Complex(real: -imaginary, imaginary: real))
return Complex(real: atanh.imaginary, imaginary: -atanh.real)
}
@_transparent // @_inlineable
public func hyperbolicSine() -> Complex {
if (real.isInfinite || real == 0) && !imaginary.isFinite {
return Complex(real: real, imaginary: .nan)
}
if imaginary == 0 && !real.isFinite {
return self
}
return Complex(
real: T.sinh(real) * T.cos(imaginary),
imaginary: T.cosh(real) * T.sin(imaginary)
)
}
@_transparent // @_inlineable
public func hyperbolicCosine() -> Complex {
if real.isInfinite && !imaginary.isFinite {
return Complex(real: abs(real), imaginary: .nan)
}
if real == 0 && imaginary == 0 {
return Complex(real: 1, imaginary: imaginary)
}
if real == 0 && !imaginary.isFinite {
return Complex(real: .nan, imaginary: real)
}
if imaginary == 0 && !real.isFinite {
return Complex(real: abs(real), imaginary: imaginary)
}
return Complex(
real: T.cosh(real) * T.cos(imaginary),
imaginary: T.sinh(real) * T.sin(imaginary)
)
}
@_transparent // @_inlineable
public func hyperbolicTangent() -> Complex {
if real.isNaN && imaginary == 0 { return self }
if real.isInfinite {
if !imaginary.isFinite {
return Complex(real: T(signOf: real, magnitudeOf: 1), imaginary: 0)
}
return Complex(
real: T(signOf: real, magnitudeOf: 1),
imaginary: T(signOf: T.sin(2 * imaginary), magnitudeOf: 0)
)
}
if real == 0 {
// See C11 DR 471.
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471
return Complex(real: real, imaginary: T.tan(imaginary))
}
// See AMS55 4.5.51.
let twiceReal = 2 * real, twiceImaginary = 2 * imaginary
let sinhTwiceReal = T.sinh(twiceReal)
let denominator = T.cosh(twiceReal) + T.cos(twiceImaginary)
// If `sinh(2 * real)` is infinite, then `cosh(2 * real)` is infinite.
//
// Thus, `denominator` is either:
// - infinite, if `cos(2 * imaginary)` is not NaN; or
// - NaN, if `cos(2 * imaginary)` is NaN (i.e., if `2 * imaginary` is
// infinite or NaN).
//
// In either case, if `imaginary` is finite, the complex hyperbolic tangent
// should be 1 or -1.
if sinhTwiceReal.isInfinite && imaginary.isFinite {
return Complex(
real: T(signOf: sinhTwiceReal, magnitudeOf: 1),
imaginary: T(signOf: T.sin(twiceImaginary), magnitudeOf: 0)
)
}
return Complex(
real: sinhTwiceReal / denominator,
imaginary: T.sin(twiceImaginary) / denominator
)
}
@_transparent // @_inlineable
public func inverseHyperbolicSine() -> Complex {
if real.isNaN {
if imaginary.isInfinite {
return Complex(real: imaginary, imaginary: real)
}
if imaginary == 0 { return self }
return Complex(real: real, imaginary: real)
}
if real.isInfinite {
if imaginary.isNaN { return self }
if imaginary.isInfinite {
return Complex(
real: real, imaginary: T(signOf: imaginary, magnitudeOf: .pi / 4)
)
}
return Complex(
real: real, imaginary: T(signOf: imaginary, magnitudeOf: 0)
)
}
if imaginary.isInfinite {
return Complex(
real: T(signOf: real, magnitudeOf: imaginary),
imaginary: T(signOf: imaginary, magnitudeOf: .pi / 2)
)
}
let a = self ** 2 + 1
let b = Complex.log(self + Complex.sqrt(a))
return Complex(
real: T(signOf: real, magnitudeOf: b.real),
imaginary: T(signOf: imaginary, magnitudeOf: b.imaginary)
)
}
@_transparent // @_inlineable
public func inverseHyperbolicCosine() -> Complex {
if real.isNaN {
if imaginary.isInfinite {
return Complex(real: .infinity, imaginary: real)
}
return Complex(real: real, imaginary: real)
}
if real.isInfinite {
if imaginary.isNaN {
return Complex(real: .infinity, imaginary: imaginary)
}
if imaginary.isInfinite {
switch (real.sign, imaginary.sign) {
case (.plus, .plus):
return Complex(real: .infinity, imaginary: .pi / 4)
case (.plus, .minus):
return Complex(real: .infinity, imaginary: -.pi / 4)
case (.minus, .plus):
return Complex(real: .infinity, imaginary: .pi * 3 / 4)
case (.minus, .minus):
return Complex(real: .infinity, imaginary: -.pi * 3 / 4)
}
}
switch (real.sign, imaginary.sign) {
case (.plus, .plus):
return Complex(real: .infinity, imaginary: 0)
case (.plus, .minus):
return Complex(real: .infinity, imaginary: -(0 as T))
case (.minus, .plus):
return Complex(real: .infinity, imaginary: .pi)
case (.minus, .minus):
return Complex(real: .infinity, imaginary: -.pi)
}
}
if imaginary.isNaN {
if real == 0 {
// See C11 DR 471.
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471
return Complex(real: imaginary, imaginary: .pi / 2)
}
return Complex(real: imaginary, imaginary: imaginary)
}
if imaginary.isInfinite {
return Complex(
real: .infinity, imaginary: T(signOf: imaginary, magnitudeOf: .pi / 2)
)
}
let a = self ** 2 - 1
let b = Complex.log(self + Complex.sqrt(a))
return Complex(
real: T(signOf: 0, magnitudeOf: b.real),
imaginary: T(signOf: imaginary, magnitudeOf: b.imaginary)
)
}
@_transparent // @_inlineable
public func inverseHyperbolicTangent() -> Complex {
if imaginary.isNaN {
if real.isInfinite || real == 0 {
return Complex(
real: T(signOf: real, magnitudeOf: 0), imaginary: imaginary
)
}
return Complex(real: imaginary, imaginary: imaginary)
}
if imaginary.isInfinite {
return Complex(
real: T(signOf: real, magnitudeOf: 0),
imaginary: T(signOf: imaginary, magnitudeOf: .pi / 2)
)
}
if imaginary == 0 && abs(real) == 1 {
return Complex(
real: T(signOf: real, magnitudeOf: .infinity),
imaginary: T(signOf: imaginary, magnitudeOf: 0)
)
}
if real.isNaN {
return Complex(real: real, imaginary: real)
}
if real.isInfinite {
return Complex(
real: T(signOf: real, magnitudeOf: 0),
imaginary: T(signOf: imaginary, magnitudeOf: .pi / 2)
)
}
let a = Complex.log((1 + self) / (1 - self)) / 2
return Complex(
real: T(signOf: real, magnitudeOf: a.real),
imaginary: T(signOf: imaginary, magnitudeOf: a.imaginary)
)
}
}
/// Returns the absolute value (magnitude, modulus) of `z`.
@_transparent
public func abs<T>(_ z: Complex<T>) -> Complex<T> {
return Complex(real: z.magnitude)
}
/// Returns the square root of `z`.
@_transparent
public func sqrt<T>(_ z: Complex<T>) -> Complex<T> {
return z.squareRoot()
}
public typealias Complex64 = Complex<Float>
public typealias Complex128 = Complex<Double>