dankogai/swift-complex
Complex numbers in Swift and Swift Package Manager.
Synopsis
import Complex
let z0 = 1.0 + 1.0.i // (1.0+1.0.i)
let z1 = 1.0 - 1.0.i // (1.0-1.0.i)
z0.conj // (1.0-1.0.i)
z0.i // (-1.0+1.0.i)
z0.norm // 2
z0 + z1 // (2.0+0.0.i)
z0 - z1 // (0.0+2.0.i)
z0 * z1 // (2.0+0.0.i)
z0 / z1 // (0.0+1.0.i)Description
complex.swift implements all the functionality of [std::complex in c++11], arguably more intuitively.
[std::complex in c++11]: http://www.cplusplus.com/reference/complex/
like C++11
- Protocol-Oriented
Complex numbers are Complex<R> where R is the type of .real and .imag that conforms to FloatingPoint. Math functions become available when R also conforms to RMath, aka RealElementaryFunctions. Gaussian integers are GaussianInt<I> where I conforms to the GaussianIntElement protocol, that is, SignedInteger. In addition to basic arithmetic operations like +, -, , / and abs(), Complex<R> gets libm functions like exp(), log(), sin(), cos().
unlike C++11
- Instead of defining the constant
i,DoubleandComplexhave a property.iwhich returnsself * Complex(0,1)so it does not pollute the identifieri, too popularly used for iteration to make it a constant. - Following functions are provided as computed properties:
z.abs for abs(z) z.arg for arg(z) z.norm for norm(z) z.conj for conj(z) * z.proj for proj(z)
- Construct a complex number via polar notation as:
* Complex(abs:magnitude, arg:argument)
RMath and CMath
What you get out of Complex<R> depends on what R can do.
R: FloatingPointis all the struct itself asks. Construction,+-*/,conj,norm,.i,description, andCodable(whenRis) — everything that is plain arithmetic works for any element.R: RMathis where the math comes from. WhenRconforms,Complex<R>conforms toCMath(typealiasComplexElementaryFunctions) and gainsexp,log,sqrtand friends,abs/argand polar construction, andtoString(_:radix:).
RMath (typealias RealElementaryFunctions; deliberately not named ElementaryFunctions, which would collide with [apple/swift-numerics] and friends) asks of the element:
init(_:Double)andtoDouble()— the two conversions no protocol can guess;static var precision:Int— the bit width results are computed to;- the math functions,
exp(_:)throughatan2(y:x:), in plain form and withprecision:debug:flags. The full forms are requirements, not conveniences, so that aprecision:you pass dispatches to the element instead of being silently dropped; fixed-precision elements accept and ignore the flags. (cbrt,expm1, andlog1pcome with defaults built from the other requirements.)
Out of the box only Double is predefined, by way of RMathViaDouble — a sub-protocol that implements every math requirement by round-tripping through Double; adopt it and toDouble() is all your type owes. Every other element is a conformance you, or a sibling package, declare:
- SwiftNumericsExample fills
Float's deliberately vacant slot with [apple/swift-numerics]'RealModule. - SwiftBigNumExample adopts
BigRatandBigFloatof [dankogai/swift-bignum] with an empty extension each, for arbitrary precision —Complex<BigFloat>.sqrt(z, precision:256)really is 256 bits, andComplex<BigRat>arithmetic is exact.
CMath also has a settable precision, defaulting to 128: the default precision: handed down to the element, likewise ignored by elements whose precision is fixed.
[dankogai/swift-bignum]: https://github.com/dankogai/swift-bignum
ComplexOperators
** — pow(base, exponent) as an operator — lives in a separate module so that plain import Complex does not add operators to your namespace:
import ComplexOperators // @_exported imports Complex, too
2.0 ** 3.0 // 8.0
(1.0+1.0.i) ** 2.0 // (0.0+2.0.i)
2.0 ** 3.0 ** 2.0 // 512.0 -- binds tighter than *, associates rightUsage
build
$ git clone https://github.com/dankogai/swift-complex.git
$ cd swift-complex # the following assumes your $PWD is here
$ swift buildtest
The suites are written in [Swift Testing], the modern successor of XCTest. The root package tests everything that needs no dependency — still fetching nothing:
$ swift testThe element conformances have suites of their own, in the sibling example packages:
$ (cd SwiftNumericsExample && swift test)$ (cd SwiftBigNumExample && swift test)[Swift Testing]: https://developer.apple.com/documentation/testing/
REPL
Simply
$ swift run --repland in your repl,
Welcome to Swift! Type :help for assistance.
1> import Complex
2> Complex.sqrt(1.i)
$R0: Complex.Complex<Double> = {
real = 0.70710678118654757
imag = 0.70710678118654757
}Xcode
Just open the package directory — Xcode natively supports Swift Package Manager:
$ open ./Package.swiftFrom Your SwiftPM-Managed Projects
Add the following to the dependencies section:
.package(
url: "https://github.com/dankogai/swift-complex.git", from: "6.3.0"
)and the following to the .target argument:
.target(
name: "YourSwiftyPackage",
dependencies: ["Complex"])Now all you have to do is:
import Complexin your code. Enjoy!
Prerequisite
Swift 6 or better, macOS or Linux to build.
Swift Numerics vs. this module
This section used to be a CAVEAT that began "You should consider using ComplexModule of Numerics instead of this." No longer. With [apple/swift-numerics] complex number support on Swift is [official at last] — and as of 6.3 this module is fully resurrected, for the parts officialdom does not cover.
- The element is open.
Complex<R>asks onlyFloatingPointofR; the math functions arrive whenRconforms toRMath, whose slot is deliberately left for you to fill.ComplexModulerequiresRealType: Real, its own hierarchy. Here the element's math is whatever you choose — SwiftNumericsExample fills the slot with swift-numerics' ownRealModule, SwiftBigNumExample with [dankogai/swift-bignum], an emptyextensioneach. This module does not compete with swift-numerics; it runs happily on top of it. - Arbitrary precision, all the way down. Every math function comes in a
precision:debug:form, as in swift-bignum, andComplexhands the flag to every element call underneath —Complex<BigFloat>.sqrt(z, precision:256)really is 256 bits. Exact types stay exact:(1+2i)/(3+4i)overComplex<BigRat>is(11+2i)/25, not a rounding of it.ComplexModulehas no such channel. - No [point at infinity].
ComplexModuleadopts it; while mathematically more correct, it may technically cause unexpected results because real operations on complex numbers are no longer isomorphic to real operations on real numbers:Complex(-1.0, 0.0) / Complex(0.0, 0.0)isComplex(+infinity, 0.0)there, notComplex(-infinity, nan)like many other platforms. This module keeps the componentwise semantics of C++'sstd::complexand friends. - Gaussian integers.
GaussianInt<I>for anySignedInteger,BigIntincluded. swift-numerics has no counterpart. - Ergonomics.
1.0 + 2.0.iliterals;abs,arg,magnitudeandargumentthat are settable, not just readable; polar construction;toString(_:radix:)down to hexfloat;**viaimport ComplexOperators, opt-in so it never sneaks into your namespace. - Nothing to fetch. The library depends on nothing but the standard library. The examples that do depend on things are packages of their own, so
swift buildhere fetches exactly nothing.
[apple/swift-numerics]: https://github.com/apple/swift-numerics [official at last]: https://swift.org/blog/numerics/ [point at infinity]: https://en.wikipedia.org/wiki/Point_at_infinity
Package Metadata
Repository: dankogai/swift-complex
Default branch: main
README: README.md