tur/complex
Complex numbers as a plain re/im pair of doubles.
A Complex is a two-float record, and every operation on it is written in ordinary Turmeric. Neither _Complex nor <complex.h> appears anywhere in the emitted C, deliberately:
* `c2mir` (the JIT front end) implements a C11 subset that does not include the optional `_Complex` feature; and * even on a C compiler that supports it, `a * b` and `a / b` on `double _Complex` do not compile to arithmetic -- they compile to calls into `__muldc3` / `__divdc3` in the compiler runtime, which would become external symbols the JIT must resolve.
So the arithmetic is hand-written here, division uses Smith's algorithm (scale by the larger-magnitude denominator component before dividing), and the overflow behaviour is ours, identical on every engine, and testable.
Complex numbers are not ordered, so there is deliberately NO Ord instance. (lt? c1 c2) is a type error, and that is the right answer -- do not add a lexicographic one to fill the table.
Since: Phase N2 (numeric-tower-rational-complex-plan)
Complex
(defstruct Complex :copy [re :float im :float])
a re/im pair of doubles.
:copy -- two plain doubles, no owned resource, so a Complex is freely copyable and never interacts with the rc/ref/borrow machinery.
Since: Phase N2
complex/of
(complex/of [re :float im :float] :Complex)
build the complex number re + im*i.
| re | real part | |
| im | imaginary part |
The Complex re + im*i. This is what the #cx{re im} literal expands to.
(complex/re (complex/of 3.25 -1.5)) ; => 3.25
Since: Phase N2
complex/from-float
(complex/from-float [x :float] :Complex)
embed a real number in the complex plane.
| x | the real value |
x + 0i.
(complex/im (complex/from-float 2.5)) ; => 0
Since: Phase N2
complex/zero
(complex/zero :Complex)
the complex number 0.
0 + 0i.
(complex/re (complex/zero)) ; => 0
Since: Phase N2
complex/one
(complex/one :Complex)
the complex number 1.
1 + 0i.
(complex/re (complex/one)) ; => 1
Since: Phase N2
complex/i
(complex/i :Complex)
the imaginary unit.
0 + 1i.
(complex/im (complex/i)) ; => 1
Since: Phase N2
complex/re
(complex/re [z :Complex] :float)
the real part.
| z | a Complex |
The real component.
(complex/re (complex/of 3.25 -1.5)) ; => 3.25
Since: Phase N2
complex/im
(complex/im [z :Complex] :float)
the imaginary part.
| z | a Complex |
The imaginary component.
(complex/im (complex/of 3.25 -1.5)) ; => -1.5
Since: Phase N2
complex/real?
(complex/real? [z :Complex] :bool)
does this value lie on the real axis?
| z | a Complex |
true when the imaginary part is exactly 0.
(complex/real? (complex/from-float 2.5)) ; => true
Since: Phase N2
complex/add
(complex/add [x :Complex y :Complex] :Complex)
sum of two complex numbers.
| x | left addend | |
| y | right addend |
(a+bi) + (c+di) = (a+c) + (b+d)i.
(complex/re (complex/add (complex/of 3.25 1.5) (complex/of 0.75 0.5))) ; => 4
Since: Phase N2
complex/sub
(complex/sub [x :Complex y :Complex] :Complex)
difference of two complex numbers.
| x | minuend | |
| y | subtrahend |
(a+bi) - (c+di) = (a-c) + (b-d)i.
(complex/im (complex/sub (complex/of 3.25 1.5) (complex/of 0.75 0.5))) ; => 1
Since: Phase N2
complex/neg
(complex/neg [x :Complex] :Complex)
negate a complex number.
| x | a Complex |
-(a+bi) = -a - bi.
(complex/re (complex/neg (complex/of 3.25 -1.5))) ; => -3.25
Since: Phase N2
complex/conj
(complex/conj [x :Complex] :Complex)
complex conjugate.
| x | a Complex |
conj(a+bi) = a - bi.
(complex/im (complex/conj (complex/of 3.25 -1.5))) ; => 1.5
Since: Phase N2
complex/mul
(complex/mul [x :Complex y :Complex] :Complex)
product of two complex numbers.
| x | left factor | |
| y | right factor |
(a+bi)(c+di) = (ac - bd) + (ad + bc)i, written out as arithmetic rather
than deferred to a __muldc3-style compiler-runtime helper.
(complex/re (complex/mul (complex/of 3.25 1.5) (complex/of 0.5 2.0))) ; => -1.375
Since: Phase N2
complex/scale
(complex/scale [x :Complex k :float] :Complex)
multiply a complex number by a real scalar.
| x | a Complex | |
| k | the real scale factor |
k*(a+bi) = ka + kb*i.
(complex/re (complex/scale (complex/of 3.25 -1.5) 2.0)) ; => 6.5
Since: Phase N2
complex/div
(complex/div [x :Complex y :Complex] :Complex)
quotient of two complex numbers (Smith's algorithm).
| x | dividend | |
| y | divisor |
x/y. The naive (ac+bd)/(c^2+d^2) form overflows whenever c or d is
large enough that its square leaves the double range, even when the true
quotient is perfectly ordinary. Smith's algorithm divides through by the
larger-magnitude denominator component first, so the intermediates stay
near the magnitude of the result:
|c| >= |d|: r = d/c, den = c + d*r, x/y = ((a + b*r) + (b - a*r)i)/den
|c| < |d|: r = c/d, den = c*r + d, x/y = ((a*r + b) + (b*r - a)i)/den
Dividing by the zero complex number yields the IEEE infinity/NaN result
float division already produces; it is not an error here.
(complex/re (complex/div (complex/of 3.25 1.5) (complex/of 0.5 0.0))) ; => 6.5
Since: Phase N2
complex/abs2
(complex/abs2 [z :Complex] :float)
squared magnitude |z|^2.
| z | a Complex |
re^2 + im^2, with no square root. Prefer this to complex/abs when only
comparing magnitudes -- it is exact where the square root is not.
(complex/abs2 (complex/of 3.0 4.0)) ; => 25
Since: Phase N2
complex/abs
(complex/abs [z :Complex] :float)
magnitude |z|.
| z | a Complex |
sqrt(re^2 + im^2), computed by scaling out the larger component first
(|a| * sqrt(1 + (b/a)^2)) so a value whose square would overflow still
reports its true magnitude.
(complex/abs (complex/of 3.0 4.0)) ; => 5
Since: Phase N2
complex/arg
(complex/arg [z :Complex] :float)
principal argument (phase angle) of z, in radians.
| z | a Complex |
atan2(im, re), in (-pi, pi].
(complex/arg (complex/of 0.0 1.5)) ; => 1.5708
Since: Phase N2
complex/exp
(complex/exp [z :Complex] :Complex)
e raised to a complex power.
| z | a Complex |
e^(a+bi) = e^a * (cos b + i sin b), built from the real libm wrappers in stdlib/math.tur rather than from a complex-valued runtime helper.
(complex/re (complex/exp (complex/of 0.0 0.0))) ; => 1
Since: Phase N2
complex/eq?
(complex/eq? [x :Complex y :Complex] :bool)
componentwise equality.
| x | left value | |
| y | right value |
true when both the real and the imaginary parts compare equal. This is IEEE float equality, with all the usual caveats.
(complex/eq? (complex/of 3.25 -1.5) (complex/of 3.25 -1.5)) ; => true
Since: Phase N2
complex->string
(complex->string [z :Complex] :String)
render a Complex as "3.25+4.5i" / "3.25-4.5i".
| z | a Complex |
A fresh owned String. The sign of the imaginary part is ALWAYS explicit, so the output is unambiguous and never depends on how the imaginary part happens to format.
(complex->string (complex/of 3.25 -1.5)) ; => String "3.25-1.5i"
Since: Phase N2
Num[Complex]
(definstance Num [Complex])
hand-written complex arithmetic behind + - * /.
Eq[Complex]
(definstance Eq [Complex])
componentwise IEEE equality.
Show[Complex]
(definstance Show [Complex])
"3.25+4.5i" with an always-explicit imaginary sign.