core API
The root package Luna-Flow/luna-complex defines Complex[T], the complex
numbers over any scalar type T, with construction, in-place
mutation, conjugation, the arithmetic operators and the luna-generic
structure traits. It contains no analytic functions; those are in
float_backend. The algebra behind the instances is in
the core design.
Source: src/complex.mbt,
src/complex_traits.mbt and
src/extends.mbt.
Importing
import {
"Luna-Flow/luna-complex" @complex,
}
The examples use the alias @complex.
The type
Complex
A complex number with a real part re and an imaginary part im.
pub(all) struct Complex[T] {
mut re : T
mut im : T
} derive(Eq, @debug.Debug)
The value is with . Both fields
are public and mutable, and the struct is pub(all), so code outside the
package can read, write and construct it with a record literal.
Complex[T] is a reference type: two bindings to the same value see each
other’s updates.
derive(Eq) compares both parts with T’s equality (for Double,
0.0 == -0.0 and NaN is unequal to itself). derive(Debug) prints the
record form { re: …, im: … }.
Construction and mutation
Complex::new
Builds .
pub fn[T] Complex::new(T, T) -> Complex[T]
Complex::set
Overwrites both parts in place.
pub fn[T] Complex::set(Complex[T], T, T) -> Unit
Complex::set_re
Overwrites the real part in place.
pub fn[T] Complex::set_re(Complex[T], T) -> Unit
Complex::set_im
Overwrites the imaginary part in place.
pub fn[T] Complex::set_im(Complex[T], T) -> Unit
test "construct and mutate" {
let z = @complex.Complex::new(1.0, 2.0)
assert_eq(z.re, 1.0)
z.set(3.0, 4.0)
z.set_im(-1.0)
assert_eq(z, @complex.Complex::new(3.0, -1.0))
}
Identities
Complex::zero
Returns .
pub fn[T : @luna-generic.Zero] Complex::zero() -> Complex[T]
Complex::one
Returns .
pub fn[T : @luna-generic.One + @luna-generic.Zero] Complex::one() -> Complex[T]
Both are the promoted methods of the Zero and One instances.
Arithmetic
Each operator is available as an operator and as a promoted method.
| Item | Operator | Result | Bound on T |
|---|---|---|---|
Complex::add | z + w | Add | |
Complex::sub | z - w | Sub | |
Complex::neg | -z | Neg | |
Complex::mul | z * w | Mul + Add + Sub | |
Complex::div | z / w | Field |
Here and .
Complex::add
Adds componentwise.
pub fn[T : Add] Complex::add(Complex[T], Complex[T]) -> Complex[T]
pub impl[T : Add] Add for Complex[T]
Complex::sub
Subtracts componentwise.
pub fn[T : Sub] Complex::sub(Complex[T], Complex[T]) -> Complex[T]
pub impl[T : Sub] Sub for Complex[T]
Complex::neg
Negates both parts.
pub fn[T : Neg] Complex::neg(Complex[T]) -> Complex[T]
pub impl[T : Neg] Neg for Complex[T]
Complex::mul
Multiplies with , using four multiplications.
pub fn[T : Mul + Add + Sub] Complex::mul(Complex[T], Complex[T]) -> Complex[T]
pub impl[T : Mul + Add + Sub] Mul for Complex[T]
Complex::div
Divides by multiplying with the inverse of the squared modulus.
pub fn[T : @luna-generic.Field] Complex::div(Complex[T], Complex[T]) -> Complex[T]
pub impl[T : @luna-generic.Field] Div for Complex[T]
It computes , then Inverse::inv(n), and multiplies both
parts of by it. The formula is not scaled: for Double,
overflows for and underflows for , and Inverse::inv of Double aborts with
Double::inv: division by zero when is zero, including after underflow.
Use @fb.div for robust floating-point division.
test "complex arithmetic" {
let z = @complex.Complex::new(1.0, 2.0)
let w = @complex.Complex::new(3.0, -1.0)
assert_eq(z + w, @complex.Complex::new(4.0, 1.0))
assert_eq(z - w, @complex.Complex::new(-2.0, 3.0))
assert_eq(z * w, @complex.Complex::new(5.0, 5.0))
assert_eq(z * w / w, z)
assert_eq(-z, @complex.Complex::new(-1.0, -2.0))
}
Conjugate and inverse
Complex::conjugate
Returns .
pub fn[T : Neg] Complex::conjugate(Complex[T]) -> Complex[T]
pub impl[T : Neg] @luna-generic.Conjugate for Complex[T]
Complex::inv
Returns .
pub fn[T : @luna-generic.Field] Complex::inv(Complex[T]) -> Complex[T]
pub impl[T : @luna-generic.Field] @luna-generic.Inverse for Complex[T]
It has the same unscaled formula and the same abort on a zero modulus as
Complex::div.
test "conjugate and inverse" {
let z = @complex.Complex::new(3.0, 4.0)
assert_eq(z.conjugate(), @complex.Complex::new(3.0, -4.0))
assert_eq(z * z.conjugate(), @complex.Complex::new(25.0, 0.0))
assert_eq(z.inv(), @complex.Complex::new(0.12, -0.16))
}
Equality and text
Complex::equal
Compares both parts; the promoted method of the derived Eq. Prefer ==.
pub fn[T : Eq] Complex::equal(Complex[T], Complex[T]) -> Bool
Complex::to_string
Formats as "<re> + <im>i" using T’s Show.
pub fn[T : Show] Complex::to_string(Complex[T]) -> String
pub impl[T : Show] Show for Complex[T]
The sign of the imaginary part is printed by T, so prints as
1 + -2i. String interpolation "\{z}" uses the same text.
test "text form" {
inspect(@complex.Complex::new(1.5, -2.0), content="1.5 + -2i")
debug_inspect(@complex.Complex::new(1, 2), content="{ re: 1, im: 2 }")
}
Structure instances
Complex[T] implements the luna-generic structure traits under the
bounds below. The core design derives each law.
| Instance | Bound on T |
|---|---|
Zero | Zero |
One | One + Zero |
AddMonoid | Add + Zero |
AddGroup | Add + Zero + Neg + Sub |
MulMonoid | Ring |
Semiring | Ring |
Ring | Ring |
Inverse | Field |
MulGroup | Field |
Field | Field |
Conjugate | Neg |
fn[T : @lg.Field] average(a : T, b : T) -> T {
let two = @lg.One::one() + @lg.One::one()
(a + b) / two
}
test "generic field code accepts complex numbers" {
let m = average(@complex.Complex::new(1.0, 2.0), @complex.Complex::new(3.0, 0.0))
assert_eq(m, @complex.Complex::new(2.0, 1.0))
}
Deprecated
These method forms come from the trait instances and are hidden from the interface file; they warn when used outside the package.
| Deprecated method | Replacement |
|---|---|
z.not_equal(w) | z != w |
z.output(logger) | z.to_string() or "\{z}" |
z.to_repr() | Repr(z) or @debug.Debug::to_repr(z) |