Skip to content

Commit 27cbb44

Browse files
authored
Merge pull request #949 from zig-lang/complex-math
Add initial complex-number support
2 parents 13076d5 + 84391af commit 27cbb44

24 files changed

+1416
-0
lines changed

CMakeLists.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,28 @@ set(ZIG_STD_FILES
498498
"math/tan.zig"
499499
"math/tanh.zig"
500500
"math/trunc.zig"
501+
"math/complex/abs.zig"
502+
"math/complex/acosh.zig"
503+
"math/complex/acos.zig"
504+
"math/complex/arg.zig"
505+
"math/complex/asinh.zig"
506+
"math/complex/asin.zig"
507+
"math/complex/atanh.zig"
508+
"math/complex/atan.zig"
509+
"math/complex/conj.zig"
510+
"math/complex/cosh.zig"
511+
"math/complex/cos.zig"
512+
"math/complex/exp.zig"
513+
"math/complex/index.zig"
514+
"math/complex/ldexp.zig"
515+
"math/complex/log.zig"
516+
"math/complex/pow.zig"
517+
"math/complex/proj.zig"
518+
"math/complex/sinh.zig"
519+
"math/complex/sin.zig"
520+
"math/complex/sqrt.zig"
521+
"math/complex/tanh.zig"
522+
"math/complex/tan.zig"
501523
"mem.zig"
502524
"net.zig"
503525
"os/child_process.zig"

std/math/complex/abs.zig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn abs(z: var) @typeOf(z.re) {
8+
const T = @typeOf(z.re);
9+
return math.hypot(T, z.re, z.im);
10+
}
11+
12+
const epsilon = 0.0001;
13+
14+
test "complex.cabs" {
15+
const a = Complex(f32).new(5, 3);
16+
const c = abs(a);
17+
debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
18+
}

std/math/complex/acos.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn acos(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
const q = cmath.asin(z);
10+
return Complex(T).new(T(math.pi) / 2 - q.re, -q.im);
11+
}
12+
13+
const epsilon = 0.0001;
14+
15+
test "complex.cacos" {
16+
const a = Complex(f32).new(5, 3);
17+
const c = acos(a);
18+
19+
debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon));
20+
debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon));
21+
}

std/math/complex/acosh.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn acosh(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
const q = cmath.acos(z);
10+
return Complex(T).new(-q.im, q.re);
11+
}
12+
13+
const epsilon = 0.0001;
14+
15+
test "complex.cacosh" {
16+
const a = Complex(f32).new(5, 3);
17+
const c = acosh(a);
18+
19+
debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon));
20+
debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon));
21+
}

std/math/complex/arg.zig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn arg(z: var) @typeOf(z.re) {
8+
const T = @typeOf(z.re);
9+
return math.atan2(T, z.im, z.re);
10+
}
11+
12+
const epsilon = 0.0001;
13+
14+
test "complex.carg" {
15+
const a = Complex(f32).new(5, 3);
16+
const c = arg(a);
17+
debug.assert(math.approxEq(f32, c, 0.540420, epsilon));
18+
}

std/math/complex/asin.zig

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn asin(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
const x = z.re;
10+
const y = z.im;
11+
12+
const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y);
13+
const q = Complex(T).new(-y, x);
14+
const r = cmath.log(q.add(cmath.sqrt(p)));
15+
16+
return Complex(T).new(r.im, -r.re);
17+
}
18+
19+
const epsilon = 0.0001;
20+
21+
test "complex.casin" {
22+
const a = Complex(f32).new(5, 3);
23+
const c = asin(a);
24+
25+
debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon));
26+
debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon));
27+
}

std/math/complex/asinh.zig

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn asinh(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
const q = Complex(T).new(-z.im, z.re);
10+
const r = cmath.asin(q);
11+
return Complex(T).new(r.im, -r.re);
12+
}
13+
14+
const epsilon = 0.0001;
15+
16+
test "complex.casinh" {
17+
const a = Complex(f32).new(5, 3);
18+
const c = asinh(a);
19+
20+
debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon));
21+
debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon));
22+
}

std/math/complex/atan.zig

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn atan(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
return switch (T) {
10+
f32 => atan32(z),
11+
f64 => atan64(z),
12+
else => @compileError("atan not implemented for " ++ @typeName(z)),
13+
};
14+
}
15+
16+
fn redupif32(x: f32) f32 {
17+
const DP1 = 3.140625;
18+
const DP2 = 9.67502593994140625e-4;
19+
const DP3 = 1.509957990978376432e-7;
20+
21+
var t = x / math.pi;
22+
if (t >= 0.0) {
23+
t += 0.5;
24+
} else {
25+
t -= 0.5;
26+
}
27+
28+
const u = f32(i32(t));
29+
return ((x - u * DP1) - u * DP2) - t * DP3;
30+
}
31+
32+
fn atan32(z: &const Complex(f32)) Complex(f32) {
33+
const maxnum = 1.0e38;
34+
35+
const x = z.re;
36+
const y = z.im;
37+
38+
if ((x == 0.0) and (y > 1.0)) {
39+
// overflow
40+
return Complex(f32).new(maxnum, maxnum);
41+
}
42+
43+
const x2 = x * x;
44+
var a = 1.0 - x2 - (y * y);
45+
if (a == 0.0) {
46+
// overflow
47+
return Complex(f32).new(maxnum, maxnum);
48+
}
49+
50+
var t = 0.5 * math.atan2(f32, 2.0 * x, a);
51+
var w = redupif32(t);
52+
53+
t = y - 1.0;
54+
a = x2 + t * t;
55+
if (a == 0.0) {
56+
// overflow
57+
return Complex(f32).new(maxnum, maxnum);
58+
}
59+
60+
t = y + 1.0;
61+
a = (x2 + (t * t)) / a;
62+
return Complex(f32).new(w, 0.25 * math.ln(a));
63+
}
64+
65+
fn redupif64(x: f64) f64 {
66+
const DP1 = 3.14159265160560607910;
67+
const DP2 = 1.98418714791870343106e-9;
68+
const DP3 = 1.14423774522196636802e-17;
69+
70+
var t = x / math.pi;
71+
if (t >= 0.0) {
72+
t += 0.5;
73+
} else {
74+
t -= 0.5;
75+
}
76+
77+
const u = f64(i64(t));
78+
return ((x - u * DP1) - u * DP2) - t * DP3;
79+
}
80+
81+
fn atan64(z: &const Complex(f64)) Complex(f64) {
82+
const maxnum = 1.0e308;
83+
84+
const x = z.re;
85+
const y = z.im;
86+
87+
if ((x == 0.0) and (y > 1.0)) {
88+
// overflow
89+
return Complex(f64).new(maxnum, maxnum);
90+
}
91+
92+
const x2 = x * x;
93+
var a = 1.0 - x2 - (y * y);
94+
if (a == 0.0) {
95+
// overflow
96+
return Complex(f64).new(maxnum, maxnum);
97+
}
98+
99+
var t = 0.5 * math.atan2(f64, 2.0 * x, a);
100+
var w = redupif64(t);
101+
102+
t = y - 1.0;
103+
a = x2 + t * t;
104+
if (a == 0.0) {
105+
// overflow
106+
return Complex(f64).new(maxnum, maxnum);
107+
}
108+
109+
t = y + 1.0;
110+
a = (x2 + (t * t)) / a;
111+
return Complex(f64).new(w, 0.25 * math.ln(a));
112+
}
113+
114+
const epsilon = 0.0001;
115+
116+
test "complex.catan32" {
117+
const a = Complex(f32).new(5, 3);
118+
const c = atan(a);
119+
120+
debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon));
121+
debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon));
122+
}
123+
124+
test "complex.catan64" {
125+
const a = Complex(f64).new(5, 3);
126+
const c = atan(a);
127+
128+
debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon));
129+
debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon));
130+
}

std/math/complex/atanh.zig

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn atanh(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
const q = Complex(T).new(-z.im, z.re);
10+
const r = cmath.atan(q);
11+
return Complex(T).new(r.im, -r.re);
12+
}
13+
14+
const epsilon = 0.0001;
15+
16+
test "complex.catanh" {
17+
const a = Complex(f32).new(5, 3);
18+
const c = atanh(a);
19+
20+
debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon));
21+
debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon));
22+
}

std/math/complex/conj.zig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn conj(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
return Complex(T).new(z.re, -z.im);
10+
}
11+
12+
test "complex.conj" {
13+
const a = Complex(f32).new(5, 3);
14+
const c = a.conjugate();
15+
16+
debug.assert(c.re == 5 and c.im == -3);
17+
}

std/math/complex/cos.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const std = @import("../../index.zig");
2+
const debug = std.debug;
3+
const math = std.math;
4+
const cmath = math.complex;
5+
const Complex = cmath.Complex;
6+
7+
pub fn cos(z: var) Complex(@typeOf(z.re)) {
8+
const T = @typeOf(z.re);
9+
const p = Complex(T).new(-z.im, z.re);
10+
return cmath.cosh(p);
11+
}
12+
13+
const epsilon = 0.0001;
14+
15+
test "complex.ccos" {
16+
const a = Complex(f32).new(5, 3);
17+
const c = cos(a);
18+
19+
debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon));
20+
debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon));
21+
}

0 commit comments

Comments
 (0)