| author | |
| committer | |
| log | d5e99cc05ea05d701adffce55defc512d75e10d1 |
| tree | abaf18d1fd78162939aaa38c4811b205a47cb469 |
| parent | 15bf0c1541479870dff1f8d64a3746c337f901ef |
- Library type instead of builtin
- All C complex functions implemented
Partial WIP: Needs more tests for edge cases.24 files changed, 1484 insertions(+), 0 deletions(-)
CMakeLists.txt+22| ... | @@ -498,6 +498,28 @@ set(ZIG_STD_FILES | ... | @@ -498,6 +498,28 @@ set(ZIG_STD_FILES |
| 498 | "math/tan.zig" | 498 | "math/tan.zig" |
| 499 | "math/tanh.zig" | 499 | "math/tanh.zig" |
| 500 | "math/trunc.zig" | 500 | "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" | ||
| 501 | "mem.zig" | 523 | "mem.zig" |
| 502 | "net.zig" | 524 | "net.zig" |
| 503 | "os/child_process.zig" | 525 | "os/child_process.zig" |
std/math/complex/abs.zig created+18| ... | @@ -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 created+24| ... | @@ -0,0 +1,24 @@ | ||
| 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 | const re = c.re; | ||
| 20 | const im = c.im; | ||
| 21 | |||
| 22 | debug.assert(math.approxEq(f32, re, 0.546975, epsilon)); | ||
| 23 | debug.assert(math.approxEq(f32, im, -2.452914, epsilon)); | ||
| 24 | } | ||
std/math/complex/acosh.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 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 | const re = c.re; | ||
| 20 | const im = c.im; | ||
| 21 | |||
| 22 | debug.assert(math.approxEq(f32, re, 2.452914, epsilon)); | ||
| 23 | debug.assert(math.approxEq(f32, im, 0.546975, epsilon)); | ||
| 24 | } | ||
std/math/complex/arg.zig created+18| ... | @@ -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 created+30| ... | @@ -0,0 +1,30 @@ | ||
| 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 | const re = c.re; | ||
| 26 | const im = c.im; | ||
| 27 | |||
| 28 | debug.assert(math.approxEq(f32, re, 1.023822, epsilon)); | ||
| 29 | debug.assert(math.approxEq(f32, im, 2.452914, epsilon)); | ||
| 30 | } | ||
std/math/complex/asinh.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 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 | const re = c.re; | ||
| 21 | const im = c.im; | ||
| 22 | |||
| 23 | debug.assert(math.approxEq(f32, re, 2.459831, epsilon)); | ||
| 24 | debug.assert(math.approxEq(f32, im, 0.533999, epsilon)); | ||
| 25 | } | ||
std/math/complex/atan.zig created+136| ... | @@ -0,0 +1,136 @@ | ||
| 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.ctan32" { | ||
| 117 | const a = Complex(f32).new(5, 3); | ||
| 118 | const c = atan(a); | ||
| 119 | |||
| 120 | const re = c.re; | ||
| 121 | const im = c.im; | ||
| 122 | |||
| 123 | debug.assert(math.approxEq(f32, re, 1.423679, epsilon)); | ||
| 124 | debug.assert(math.approxEq(f32, im, 0.086569, epsilon)); | ||
| 125 | } | ||
| 126 | |||
| 127 | test "complex.ctan64" { | ||
| 128 | const a = Complex(f64).new(5, 3); | ||
| 129 | const c = atan(a); | ||
| 130 | |||
| 131 | const re = c.re; | ||
| 132 | const im = c.im; | ||
| 133 | |||
| 134 | debug.assert(math.approxEq(f64, re, 1.423679, epsilon)); | ||
| 135 | debug.assert(math.approxEq(f64, im, 0.086569, epsilon)); | ||
| 136 | } | ||
std/math/complex/atanh.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 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 | const re = c.re; | ||
| 21 | const im = c.im; | ||
| 22 | |||
| 23 | debug.assert(math.approxEq(f32, re, 0.146947, epsilon)); | ||
| 24 | debug.assert(math.approxEq(f32, im, 1.480870, epsilon)); | ||
| 25 | } | ||
std/math/complex/conj.zig created+17| ... | @@ -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 created+24| ... | @@ -0,0 +1,24 @@ | ||
| 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 | const re = c.re; | ||
| 20 | const im = c.im; | ||
| 21 | |||
| 22 | debug.assert(math.approxEq(f32, re, 2.855815, epsilon)); | ||
| 23 | debug.assert(math.approxEq(f32, im, 9.606383, epsilon)); | ||
| 24 | } | ||
std/math/complex/cosh.zig created+171| ... | @@ -0,0 +1,171 @@ | ||
| 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 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; | ||
| 8 | |||
| 9 | pub fn cosh(z: var) Complex(@typeOf(z.re)) { | ||
| 10 | const T = @typeOf(z.re); | ||
| 11 | return switch (T) { | ||
| 12 | f32 => cosh32(z), | ||
| 13 | f64 => cosh64(z), | ||
| 14 | else => @compileError("cosh not implemented for " ++ @typeName(z)), | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | fn cosh32(z: &const Complex(f32)) Complex(f32) { | ||
| 19 | const x = z.re; | ||
| 20 | const y = z.im; | ||
| 21 | |||
| 22 | const hx = @bitCast(u32, x); | ||
| 23 | const ix = hx & 0x7fffffff; | ||
| 24 | |||
| 25 | const hy = @bitCast(u32, y); | ||
| 26 | const iy = hy & 0x7fffffff; | ||
| 27 | |||
| 28 | if (ix < 0x7f800000 and iy < 0x7f800000) { | ||
| 29 | if (iy == 0) { | ||
| 30 | return Complex(f32).new(math.cosh(x), y); | ||
| 31 | } | ||
| 32 | // small x: normal case | ||
| 33 | if (ix < 0x41100000) { | ||
| 34 | return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); | ||
| 35 | } | ||
| 36 | |||
| 37 | // |x|>= 9, so cosh(x) ~= exp(|x|) | ||
| 38 | if (ix < 0x42b17218) { | ||
| 39 | // x < 88.7: exp(|x|) won't overflow | ||
| 40 | const h = math.exp(math.fabs(x)) * 0.5; | ||
| 41 | return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); | ||
| 42 | } | ||
| 43 | // x < 192.7: scale to avoid overflow | ||
| 44 | else if (ix < 0x4340b1e7) { | ||
| 45 | const v = Complex(f32).new(math.fabs(x), y); | ||
| 46 | const r = ldexp_cexp(v, -1); | ||
| 47 | return Complex(f32).new(x, y * math.copysign(f32, 1, x)); | ||
| 48 | } | ||
| 49 | // x >= 192.7: result always overflows | ||
| 50 | else { | ||
| 51 | const h = 0x1p127 * x; | ||
| 52 | return Complex(f32).new(h * h * math.cos(y), h * math.sin(y)); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | if (ix == 0 and iy >= 0x7f800000) { | ||
| 57 | return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y))); | ||
| 58 | } | ||
| 59 | |||
| 60 | if (iy == 0 and ix >= 0x7f800000) { | ||
| 61 | if (hx & 0x7fffff == 0) { | ||
| 62 | return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y); | ||
| 63 | } | ||
| 64 | return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y)); | ||
| 65 | } | ||
| 66 | |||
| 67 | if (ix < 0x7f800000 and iy >= 0x7f800000) { | ||
| 68 | return Complex(f32).new(y - y, x * (y - y)); | ||
| 69 | } | ||
| 70 | |||
| 71 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { | ||
| 72 | if (iy >= 0x7f800000) { | ||
| 73 | return Complex(f32).new(x * x, x * (y - y)); | ||
| 74 | } | ||
| 75 | return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y)); | ||
| 76 | } | ||
| 77 | |||
| 78 | return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); | ||
| 79 | } | ||
| 80 | |||
| 81 | fn cosh64(z: &const Complex(f64)) Complex(f64) { | ||
| 82 | const x = z.re; | ||
| 83 | const y = z.im; | ||
| 84 | |||
| 85 | const fx = @bitCast(u64, x); | ||
| 86 | const hx = u32(fx >> 32); | ||
| 87 | const lx = @truncate(u32, fx); | ||
| 88 | const ix = hx & 0x7fffffff; | ||
| 89 | |||
| 90 | const fy = @bitCast(u64, y); | ||
| 91 | const hy = u32(fy >> 32); | ||
| 92 | const ly = @truncate(u32, fy); | ||
| 93 | const iy = hy & 0x7fffffff; | ||
| 94 | |||
| 95 | // nearly non-exceptional case where x, y are finite | ||
| 96 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { | ||
| 97 | if (iy | ly == 0) { | ||
| 98 | return Complex(f64).new(math.cosh(x), x * y); | ||
| 99 | } | ||
| 100 | // small x: normal case | ||
| 101 | if (ix < 0x40360000) { | ||
| 102 | return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); | ||
| 103 | } | ||
| 104 | |||
| 105 | // |x|>= 22, so cosh(x) ~= exp(|x|) | ||
| 106 | if (ix < 0x40862e42) { | ||
| 107 | // x < 710: exp(|x|) won't overflow | ||
| 108 | const h = math.exp(math.fabs(x)) * 0.5; | ||
| 109 | return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y)); | ||
| 110 | } | ||
| 111 | // x < 1455: scale to avoid overflow | ||
| 112 | else if (ix < 0x4096bbaa) { | ||
| 113 | const v = Complex(f64).new(math.fabs(x), y); | ||
| 114 | const r = ldexp_cexp(v, -1); | ||
| 115 | return Complex(f64).new(x, y * math.copysign(f64, 1, x)); | ||
| 116 | } | ||
| 117 | // x >= 1455: result always overflows | ||
| 118 | else { | ||
| 119 | const h = 0x1p1023; | ||
| 120 | return Complex(f64).new(h * h * math.cos(y), h * math.sin(y)); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | if (ix | lx == 0 and iy >= 0x7ff00000) { | ||
| 125 | return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y))); | ||
| 126 | } | ||
| 127 | |||
| 128 | if (iy | ly == 0 and ix >= 0x7ff00000) { | ||
| 129 | if ((hx & 0xfffff) | lx == 0) { | ||
| 130 | return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y); | ||
| 131 | } | ||
| 132 | return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y)); | ||
| 133 | } | ||
| 134 | |||
| 135 | if (ix < 0x7ff00000 and iy >= 0x7ff00000) { | ||
| 136 | return Complex(f64).new(y - y, x * (y - y)); | ||
| 137 | } | ||
| 138 | |||
| 139 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { | ||
| 140 | if (iy >= 0x7ff00000) { | ||
| 141 | return Complex(f64).new(x * x, x * (y - y)); | ||
| 142 | } | ||
| 143 | return Complex(f64).new(x * x * math.cos(y), x * math.sin(y)); | ||
| 144 | } | ||
| 145 | |||
| 146 | return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y)); | ||
| 147 | } | ||
| 148 | |||
| 149 | const epsilon = 0.0001; | ||
| 150 | |||
| 151 | test "complex.ccosh32" { | ||
| 152 | const a = Complex(f32).new(5, 3); | ||
| 153 | const c = cosh(a); | ||
| 154 | |||
| 155 | const re = c.re; | ||
| 156 | const im = c.im; | ||
| 157 | |||
| 158 | debug.assert(math.approxEq(f32, re, -73.467300, epsilon)); | ||
| 159 | debug.assert(math.approxEq(f32, im, 10.471557, epsilon)); | ||
| 160 | } | ||
| 161 | |||
| 162 | test "complex.ccosh64" { | ||
| 163 | const a = Complex(f64).new(5, 3); | ||
| 164 | const c = cosh(a); | ||
| 165 | |||
| 166 | const re = c.re; | ||
| 167 | const im = c.im; | ||
| 168 | |||
| 169 | debug.assert(math.approxEq(f64, re, -73.467300, epsilon)); | ||
| 170 | debug.assert(math.approxEq(f64, im, 10.471557, epsilon)); | ||
| 171 | } | ||
std/math/complex/exp.zig created+146| ... | @@ -0,0 +1,146 @@ | ||
| 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 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; | ||
| 8 | |||
| 9 | pub fn exp(z: var) Complex(@typeOf(z.re)) { | ||
| 10 | const T = @typeOf(z.re); | ||
| 11 | |||
| 12 | return switch (T) { | ||
| 13 | f32 => exp32(z), | ||
| 14 | f64 => exp64(z), | ||
| 15 | else => @compileError("exp not implemented for " ++ @typeName(z)), | ||
| 16 | }; | ||
| 17 | } | ||
| 18 | |||
| 19 | fn exp32(z: &const Complex(f32)) Complex(f32) { | ||
| 20 | @setFloatMode(this, @import("builtin").FloatMode.Strict); | ||
| 21 | |||
| 22 | const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 | ||
| 23 | const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2 | ||
| 24 | |||
| 25 | const x = z.re; | ||
| 26 | const y = z.im; | ||
| 27 | |||
| 28 | const hy = @bitCast(u32, y) & 0x7fffffff; | ||
| 29 | // cexp(x + i0) = exp(x) + i0 | ||
| 30 | if (hy == 0) { | ||
| 31 | return Complex(f32).new(math.exp(x), y); | ||
| 32 | } | ||
| 33 | |||
| 34 | const hx = @bitCast(u32, x); | ||
| 35 | // cexp(0 + iy) = cos(y) + isin(y) | ||
| 36 | if ((hx & 0x7fffffff) == 0) { | ||
| 37 | return Complex(f32).new(math.cos(y), math.sin(y)); | ||
| 38 | } | ||
| 39 | |||
| 40 | if (hy >= 0x7f800000) { | ||
| 41 | // cexp(finite|nan +- i inf|nan) = nan + i nan | ||
| 42 | if ((hx & 0x7fffffff) != 0x7f800000) { | ||
| 43 | return Complex(f32).new(y - y, y - y); | ||
| 44 | } | ||
| 45 | // cexp(-inf +- i inf|nan) = 0 + i0 | ||
| 46 | else if (hx & 0x80000000 != 0) { | ||
| 47 | return Complex(f32).new(0, 0); | ||
| 48 | } | ||
| 49 | // cexp(+inf +- i inf|nan) = inf + i nan | ||
| 50 | else { | ||
| 51 | return Complex(f32).new(x, y - y); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | // 88.7 <= x <= 192 so must scale | ||
| 56 | if (hx >= exp_overflow and hx <= cexp_overflow) { | ||
| 57 | return ldexp_cexp(z, 0); | ||
| 58 | } | ||
| 59 | // - x < exp_overflow => exp(x) won't overflow (common) | ||
| 60 | // - x > cexp_overflow, so exp(x) * s overflows for s > 0 | ||
| 61 | // - x = +-inf | ||
| 62 | // - x = nan | ||
| 63 | else { | ||
| 64 | const exp_x = math.exp(x); | ||
| 65 | return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y)); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | fn exp64(z: &const Complex(f64)) Complex(f64) { | ||
| 70 | const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 | ||
| 71 | const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 | ||
| 72 | |||
| 73 | const x = z.re; | ||
| 74 | const y = z.im; | ||
| 75 | |||
| 76 | const fy = @bitCast(u64, y); | ||
| 77 | const hy = u32(fy >> 32) & 0x7fffffff; | ||
| 78 | const ly = @truncate(u32, fy); | ||
| 79 | |||
| 80 | // cexp(x + i0) = exp(x) + i0 | ||
| 81 | if (hy | ly == 0) { | ||
| 82 | return Complex(f64).new(math.exp(x), y); | ||
| 83 | } | ||
| 84 | |||
| 85 | const fx = @bitCast(u64, x); | ||
| 86 | const hx = u32(fx >> 32); | ||
| 87 | const lx = @truncate(u32, fx); | ||
| 88 | |||
| 89 | // cexp(0 + iy) = cos(y) + isin(y) | ||
| 90 | if ((hx & 0x7fffffff) | lx == 0) { | ||
| 91 | return Complex(f64).new(math.cos(y), math.sin(y)); | ||
| 92 | } | ||
| 93 | |||
| 94 | if (hy >= 0x7ff00000) { | ||
| 95 | // cexp(finite|nan +- i inf|nan) = nan + i nan | ||
| 96 | if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) { | ||
| 97 | return Complex(f64).new(y - y, y - y); | ||
| 98 | } | ||
| 99 | // cexp(-inf +- i inf|nan) = 0 + i0 | ||
| 100 | else if (hx & 0x80000000 != 0) { | ||
| 101 | return Complex(f64).new(0, 0); | ||
| 102 | } | ||
| 103 | // cexp(+inf +- i inf|nan) = inf + i nan | ||
| 104 | else { | ||
| 105 | return Complex(f64).new(x, y - y); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | // 709.7 <= x <= 1454.3 so must scale | ||
| 110 | if (hx >= exp_overflow and hx <= cexp_overflow) { | ||
| 111 | const r = ldexp_cexp(z, 0); | ||
| 112 | return *r; | ||
| 113 | } | ||
| 114 | // - x < exp_overflow => exp(x) won't overflow (common) | ||
| 115 | // - x > cexp_overflow, so exp(x) * s overflows for s > 0 | ||
| 116 | // - x = +-inf | ||
| 117 | // - x = nan | ||
| 118 | else { | ||
| 119 | const exp_x = math.exp(x); | ||
| 120 | return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y)); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | const epsilon = 0.0001; | ||
| 125 | |||
| 126 | test "complex.cexp32" { | ||
| 127 | const a = Complex(f32).new(5, 3); | ||
| 128 | const c = exp(a); | ||
| 129 | |||
| 130 | const re = c.re; | ||
| 131 | const im = c.im; | ||
| 132 | |||
| 133 | debug.assert(math.approxEq(f32, re, -146.927917, epsilon)); | ||
| 134 | debug.assert(math.approxEq(f32, im, 20.944065, epsilon)); | ||
| 135 | } | ||
| 136 | |||
| 137 | test "complex.cexp64" { | ||
| 138 | const a = Complex(f32).new(5, 3); | ||
| 139 | const c = exp(a); | ||
| 140 | |||
| 141 | const re = c.re; | ||
| 142 | const im = c.im; | ||
| 143 | |||
| 144 | debug.assert(math.approxEq(f64, re, -146.927917, epsilon)); | ||
| 145 | debug.assert(math.approxEq(f64, im, 20.944065, epsilon)); | ||
| 146 | } | ||
std/math/complex/index.zig created+172| ... | @@ -0,0 +1,172 @@ | ||
| 1 | const std = @import("../../index.zig"); | ||
| 2 | const debug = std.debug; | ||
| 3 | const math = std.math; | ||
| 4 | |||
| 5 | pub const abs = @import("abs.zig").abs; | ||
| 6 | pub const acosh = @import("acosh.zig").acosh; | ||
| 7 | pub const acos = @import("acos.zig").acos; | ||
| 8 | pub const arg = @import("arg.zig").arg; | ||
| 9 | pub const asinh = @import("asinh.zig").asinh; | ||
| 10 | pub const asin = @import("asin.zig").asin; | ||
| 11 | pub const atanh = @import("atanh.zig").atanh; | ||
| 12 | pub const atan = @import("atan.zig").atan; | ||
| 13 | pub const conj = @import("conj.zig").conj; | ||
| 14 | pub const cosh = @import("cosh.zig").cosh; | ||
| 15 | pub const cos = @import("cos.zig").cos; | ||
| 16 | pub const exp = @import("exp.zig").exp; | ||
| 17 | pub const log = @import("log.zig").log; | ||
| 18 | pub const pow = @import("pow.zig").pow; | ||
| 19 | pub const proj = @import("proj.zig").proj; | ||
| 20 | pub const sinh = @import("sinh.zig").sinh; | ||
| 21 | pub const sin = @import("sin.zig").sin; | ||
| 22 | pub const sqrt = @import("sqrt.zig").sqrt; | ||
| 23 | pub const tanh = @import("tanh.zig").tanh; | ||
| 24 | pub const tan = @import("tan.zig").tan; | ||
| 25 | |||
| 26 | // NOTE: Make this a builtin at some point? | ||
| 27 | pub fn Complex(comptime T: type) type { | ||
| 28 | return struct { | ||
| 29 | const Self = this; | ||
| 30 | |||
| 31 | re: T, | ||
| 32 | im: T, | ||
| 33 | |||
| 34 | pub fn new(re: T, im: T) Self { | ||
| 35 | return Self { | ||
| 36 | .re = re, | ||
| 37 | .im = im, | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn add(self: &const Self, other: &const Self) Self { | ||
| 42 | return Self { | ||
| 43 | .re = self.re + other.re, | ||
| 44 | .im = self.im + other.im, | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | |||
| 48 | pub fn sub(self: &const Self, other: &const Self) Self { | ||
| 49 | return Self { | ||
| 50 | .re = self.re - other.re, | ||
| 51 | .im = self.im - other.im, | ||
| 52 | }; | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn mul(self: &const Self, other: &const Self) Self { | ||
| 56 | return Self { | ||
| 57 | .re = self.re * other.re - self.im * other.im, | ||
| 58 | .im = self.im * other.re + self.re * other.im, | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | pub fn div(self: &const Self, other: &const Self) Self { | ||
| 63 | const re_num = self.re * other.re + self.im * other.im; | ||
| 64 | const im_num = self.im * other.re - self.re * other.im; | ||
| 65 | const den = other.re * other.re + other.im * other.im; | ||
| 66 | |||
| 67 | return Self { | ||
| 68 | .re = re_num / den, | ||
| 69 | .im = im_num / den, | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | pub fn conjugate(self: &const Self) Self { | ||
| 74 | return Self { | ||
| 75 | .re = self.re, | ||
| 76 | .im = -self.im, | ||
| 77 | }; | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn reciprocal(self: &const Self) Self { | ||
| 81 | const m = self.re * self.re + self.im * self.im; | ||
| 82 | return Self { | ||
| 83 | .re = self.re / m, | ||
| 84 | .im = -self.im / m, | ||
| 85 | }; | ||
| 86 | } | ||
| 87 | |||
| 88 | pub fn magnitude(self: &const Self) T { | ||
| 89 | return math.sqrt(self.re * self.re + self.im * self.im); | ||
| 90 | } | ||
| 91 | }; | ||
| 92 | } | ||
| 93 | |||
| 94 | const epsilon = 0.0001; | ||
| 95 | |||
| 96 | test "complex.add" { | ||
| 97 | const a = Complex(f32).new(5, 3); | ||
| 98 | const b = Complex(f32).new(2, 7); | ||
| 99 | const c = a.add(b); | ||
| 100 | |||
| 101 | debug.assert(c.re == 7 and c.im == 10); | ||
| 102 | } | ||
| 103 | |||
| 104 | test "complex.sub" { | ||
| 105 | const a = Complex(f32).new(5, 3); | ||
| 106 | const b = Complex(f32).new(2, 7); | ||
| 107 | const c = a.sub(b); | ||
| 108 | |||
| 109 | debug.assert(c.re == 3 and c.im == -4); | ||
| 110 | } | ||
| 111 | |||
| 112 | test "complex.mul" { | ||
| 113 | const a = Complex(f32).new(5, 3); | ||
| 114 | const b = Complex(f32).new(2, 7); | ||
| 115 | const c = a.mul(b); | ||
| 116 | |||
| 117 | debug.assert(c.re == -11 and c.im == 41); | ||
| 118 | } | ||
| 119 | |||
| 120 | test "complex.div" { | ||
| 121 | const a = Complex(f32).new(5, 3); | ||
| 122 | const b = Complex(f32).new(2, 7); | ||
| 123 | const c = a.div(b); | ||
| 124 | |||
| 125 | debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and | ||
| 126 | math.approxEq(f32, c.im, f32(-29)/53, epsilon)); | ||
| 127 | } | ||
| 128 | |||
| 129 | test "complex.conjugate" { | ||
| 130 | const a = Complex(f32).new(5, 3); | ||
| 131 | const c = a.conjugate(); | ||
| 132 | |||
| 133 | debug.assert(c.re == 5 and c.im == -3); | ||
| 134 | } | ||
| 135 | |||
| 136 | test "complex.reciprocal" { | ||
| 137 | const a = Complex(f32).new(5, 3); | ||
| 138 | const c = a.reciprocal(); | ||
| 139 | |||
| 140 | debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and | ||
| 141 | math.approxEq(f32, c.im, f32(-3)/34, epsilon)); | ||
| 142 | } | ||
| 143 | |||
| 144 | test "complex.magnitude" { | ||
| 145 | const a = Complex(f32).new(5, 3); | ||
| 146 | const c = a.magnitude(); | ||
| 147 | |||
| 148 | debug.assert(math.approxEq(f32, c, 5.83095, epsilon)); | ||
| 149 | } | ||
| 150 | |||
| 151 | test "complex.cmath" { | ||
| 152 | _ = @import("abs.zig"); | ||
| 153 | _ = @import("acosh.zig"); | ||
| 154 | _ = @import("acos.zig"); | ||
| 155 | _ = @import("arg.zig"); | ||
| 156 | _ = @import("asinh.zig"); | ||
| 157 | _ = @import("asin.zig"); | ||
| 158 | _ = @import("atanh.zig"); | ||
| 159 | _ = @import("atan.zig"); | ||
| 160 | _ = @import("conj.zig"); | ||
| 161 | _ = @import("cosh.zig"); | ||
| 162 | _ = @import("cos.zig"); | ||
| 163 | _ = @import("exp.zig"); | ||
| 164 | _ = @import("log.zig"); | ||
| 165 | _ = @import("pow.zig"); | ||
| 166 | _ = @import("proj.zig"); | ||
| 167 | _ = @import("sinh.zig"); | ||
| 168 | _ = @import("sin.zig"); | ||
| 169 | _ = @import("sqrt.zig"); | ||
| 170 | _ = @import("tanh.zig"); | ||
| 171 | _ = @import("tan.zig"); | ||
| 172 | } | ||
std/math/complex/ldexp.zig created+75| ... | @@ -0,0 +1,75 @@ | ||
| 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 ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) { | ||
| 8 | const T = @typeOf(z.re); | ||
| 9 | |||
| 10 | return switch (T) { | ||
| 11 | f32 => ldexp_cexp32(z, expt), | ||
| 12 | f64 => ldexp_cexp64(z, expt), | ||
| 13 | else => unreachable, | ||
| 14 | }; | ||
| 15 | } | ||
| 16 | |||
| 17 | fn frexp_exp32(x: f32, expt: &i32) f32 { | ||
| 18 | const k = 235; // reduction constant | ||
| 19 | const kln2 = 162.88958740; // k * ln2 | ||
| 20 | |||
| 21 | const exp_x = math.exp(x - kln2); | ||
| 22 | const hx = @bitCast(u32, exp_x); | ||
| 23 | *expt = i32(hx >> 23) - (0x7f + 127) + k; | ||
| 24 | return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23)); | ||
| 25 | } | ||
| 26 | |||
| 27 | fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) { | ||
| 28 | var ex_expt: i32 = undefined; | ||
| 29 | const exp_x = frexp_exp32(z.re, &ex_expt); | ||
| 30 | const exptf = expt + ex_expt; | ||
| 31 | |||
| 32 | const half_expt1 = @divTrunc(exptf, 2); | ||
| 33 | const scale1 = @bitCast(f32, (0x7f + half_expt1) << 23); | ||
| 34 | |||
| 35 | const half_expt2 = exptf - half_expt1; | ||
| 36 | const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23); | ||
| 37 | |||
| 38 | return Complex(f32).new( | ||
| 39 | math.cos(z.im) * exp_x * scale1 * scale2, | ||
| 40 | math.sin(z.im) * exp_x * scale1 * scale2, | ||
| 41 | ); | ||
| 42 | } | ||
| 43 | |||
| 44 | fn frexp_exp64(x: f64, expt: &i32) f64 { | ||
| 45 | const k = 1799; // reduction constant | ||
| 46 | const kln2 = 1246.97177782734161156; // k * ln2 | ||
| 47 | |||
| 48 | const exp_x = math.exp(x - kln2); | ||
| 49 | |||
| 50 | const fx = @bitCast(u64, x); | ||
| 51 | const hx = u32(fx >> 32); | ||
| 52 | const lx = @truncate(u32, fx); | ||
| 53 | |||
| 54 | *expt = i32(hx >> 20) - (0x3ff + 1023) + k; | ||
| 55 | |||
| 56 | const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20); | ||
| 57 | return @bitCast(f64, (u64(high_word) << 32) | lx); | ||
| 58 | } | ||
| 59 | |||
| 60 | fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) { | ||
| 61 | var ex_expt: i32 = undefined; | ||
| 62 | const exp_x = frexp_exp64(z.re, &ex_expt); | ||
| 63 | const exptf = i64(expt + ex_expt); | ||
| 64 | |||
| 65 | const half_expt1 = @divTrunc(exptf, 2); | ||
| 66 | const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20); | ||
| 67 | |||
| 68 | const half_expt2 = exptf - half_expt1; | ||
| 69 | const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20); | ||
| 70 | |||
| 71 | return Complex(f64).new( | ||
| 72 | math.cos(z.im) * exp_x * scale1 * scale2, | ||
| 73 | math.sin(z.im) * exp_x * scale1 * scale2, | ||
| 74 | ); | ||
| 75 | } | ||
std/math/complex/log.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 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 log(z: var) Complex(@typeOf(z.re)) { | ||
| 8 | const T = @typeOf(z.re); | ||
| 9 | const r = cmath.abs(z); | ||
| 10 | const phi = cmath.arg(z); | ||
| 11 | |||
| 12 | return Complex(T).new(math.ln(r), phi); | ||
| 13 | } | ||
| 14 | |||
| 15 | const epsilon = 0.0001; | ||
| 16 | |||
| 17 | test "complex.clog" { | ||
| 18 | const a = Complex(f32).new(5, 3); | ||
| 19 | const c = log(a); | ||
| 20 | |||
| 21 | const re = c.re; | ||
| 22 | const im = c.im; | ||
| 23 | |||
| 24 | debug.assert(math.approxEq(f32, re, 1.763180, epsilon)); | ||
| 25 | debug.assert(math.approxEq(f32, im, 0.540419, epsilon)); | ||
| 26 | } | ||
std/math/complex/pow.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 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 pow(comptime T: type, z: &const T, c: &const T) T { | ||
| 8 | const p = cmath.log(z); | ||
| 9 | const q = c.mul(p); | ||
| 10 | return cmath.exp(q); | ||
| 11 | } | ||
| 12 | |||
| 13 | const epsilon = 0.0001; | ||
| 14 | |||
| 15 | test "complex.cpow" { | ||
| 16 | const a = Complex(f32).new(5, 3); | ||
| 17 | const b = Complex(f32).new(2.3, -1.3); | ||
| 18 | const c = pow(Complex(f32), a, b); | ||
| 19 | |||
| 20 | const re = c.re; | ||
| 21 | const im = c.im; | ||
| 22 | |||
| 23 | debug.assert(math.approxEq(f32, re, 58.049110, epsilon)); | ||
| 24 | debug.assert(math.approxEq(f32, im, -101.003433, epsilon)); | ||
| 25 | } | ||
std/math/complex/proj.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 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 proj(z: var) Complex(@typeOf(z.re)) { | ||
| 8 | const T = @typeOf(z.re); | ||
| 9 | |||
| 10 | if (math.isInf(z.re) or math.isInf(z.im)) { | ||
| 11 | return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re)); | ||
| 12 | } | ||
| 13 | |||
| 14 | return Complex(T).new(z.re, z.im); | ||
| 15 | } | ||
| 16 | |||
| 17 | const epsilon = 0.0001; | ||
| 18 | |||
| 19 | test "complex.cproj" { | ||
| 20 | const a = Complex(f32).new(5, 3); | ||
| 21 | const c = proj(a); | ||
| 22 | |||
| 23 | debug.assert(c.re == 5 and c.im == 3); | ||
| 24 | } | ||
std/math/complex/sin.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 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 sin(z: var) Complex(@typeOf(z.re)) { | ||
| 8 | const T = @typeOf(z.re); | ||
| 9 | const p = Complex(T).new(-z.im, z.re); | ||
| 10 | const q = cmath.sinh(p); | ||
| 11 | return Complex(T).new(q.im, -q.re); | ||
| 12 | } | ||
| 13 | |||
| 14 | const epsilon = 0.0001; | ||
| 15 | |||
| 16 | test "complex.csin" { | ||
| 17 | const a = Complex(f32).new(5, 3); | ||
| 18 | const c = sin(a); | ||
| 19 | |||
| 20 | const re = c.re; | ||
| 21 | const im = c.im; | ||
| 22 | |||
| 23 | debug.assert(math.approxEq(f32, re, -9.654126, epsilon)); | ||
| 24 | debug.assert(math.approxEq(f32, im, 2.841692, epsilon)); | ||
| 25 | } | ||
std/math/complex/sinh.zig created+170| ... | @@ -0,0 +1,170 @@ | ||
| 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 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; | ||
| 8 | |||
| 9 | pub fn sinh(z: var) Complex(@typeOf(z.re)) { | ||
| 10 | const T = @typeOf(z.re); | ||
| 11 | return switch (T) { | ||
| 12 | f32 => sinh32(z), | ||
| 13 | f64 => sinh64(z), | ||
| 14 | else => @compileError("tan not implemented for " ++ @typeName(z)), | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | fn sinh32(z: &const Complex(f32)) Complex(f32) { | ||
| 19 | const x = z.re; | ||
| 20 | const y = z.im; | ||
| 21 | |||
| 22 | const hx = @bitCast(u32, x); | ||
| 23 | const ix = hx & 0x7fffffff; | ||
| 24 | |||
| 25 | const hy = @bitCast(u32, y); | ||
| 26 | const iy = hy & 0x7fffffff; | ||
| 27 | |||
| 28 | if (ix < 0x7f800000 and iy < 0x7f800000) { | ||
| 29 | if (iy == 0) { | ||
| 30 | return Complex(f32).new(math.sinh(x), y); | ||
| 31 | } | ||
| 32 | // small x: normal case | ||
| 33 | if (ix < 0x41100000) { | ||
| 34 | return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); | ||
| 35 | } | ||
| 36 | |||
| 37 | // |x|>= 9, so cosh(x) ~= exp(|x|) | ||
| 38 | if (ix < 0x42b17218) { | ||
| 39 | // x < 88.7: exp(|x|) won't overflow | ||
| 40 | const h = math.exp(math.fabs(x)) * 0.5; | ||
| 41 | return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); | ||
| 42 | } | ||
| 43 | // x < 192.7: scale to avoid overflow | ||
| 44 | else if (ix < 0x4340b1e7) { | ||
| 45 | const v = Complex(f32).new(math.fabs(x), y); | ||
| 46 | const r = ldexp_cexp(v, -1); | ||
| 47 | return Complex(f32).new(x * math.copysign(f32, 1, x), y); | ||
| 48 | } | ||
| 49 | // x >= 192.7: result always overflows | ||
| 50 | else { | ||
| 51 | const h = 0x1p127 * x; | ||
| 52 | return Complex(f32).new(h * math.cos(y), h * h * math.sin(y)); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | if (ix == 0 and iy >= 0x7f800000) { | ||
| 57 | return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y); | ||
| 58 | } | ||
| 59 | |||
| 60 | if (iy == 0 and ix >= 0x7f800000) { | ||
| 61 | if (hx & 0x7fffff == 0) { | ||
| 62 | return Complex(f32).new(x, y); | ||
| 63 | } | ||
| 64 | return Complex(f32).new(x, math.copysign(f32, 0, y)); | ||
| 65 | } | ||
| 66 | |||
| 67 | if (ix < 0x7f800000 and iy >= 0x7f800000) { | ||
| 68 | return Complex(f32).new(y - y, x * (y - y)); | ||
| 69 | } | ||
| 70 | |||
| 71 | if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) { | ||
| 72 | if (iy >= 0x7f800000) { | ||
| 73 | return Complex(f32).new(x * x, x * (y - y)); | ||
| 74 | } | ||
| 75 | return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y)); | ||
| 76 | } | ||
| 77 | |||
| 78 | return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); | ||
| 79 | } | ||
| 80 | |||
| 81 | fn sinh64(z: &const Complex(f64)) Complex(f64) { | ||
| 82 | const x = z.re; | ||
| 83 | const y = z.im; | ||
| 84 | |||
| 85 | const fx = @bitCast(u64, x); | ||
| 86 | const hx = u32(fx >> 32); | ||
| 87 | const lx = @truncate(u32, fx); | ||
| 88 | const ix = hx & 0x7fffffff; | ||
| 89 | |||
| 90 | const fy = @bitCast(u64, y); | ||
| 91 | const hy = u32(fy >> 32); | ||
| 92 | const ly = @truncate(u32, fy); | ||
| 93 | const iy = hy & 0x7fffffff; | ||
| 94 | |||
| 95 | if (ix < 0x7ff00000 and iy < 0x7ff00000) { | ||
| 96 | if (iy | ly == 0) { | ||
| 97 | return Complex(f64).new(math.sinh(x), y); | ||
| 98 | } | ||
| 99 | // small x: normal case | ||
| 100 | if (ix < 0x40360000) { | ||
| 101 | return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); | ||
| 102 | } | ||
| 103 | |||
| 104 | // |x|>= 22, so cosh(x) ~= exp(|x|) | ||
| 105 | if (ix < 0x40862e42) { | ||
| 106 | // x < 710: exp(|x|) won't overflow | ||
| 107 | const h = math.exp(math.fabs(x)) * 0.5; | ||
| 108 | return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y)); | ||
| 109 | } | ||
| 110 | // x < 1455: scale to avoid overflow | ||
| 111 | else if (ix < 0x4096bbaa) { | ||
| 112 | const v = Complex(f64).new(math.fabs(x), y); | ||
| 113 | const r = ldexp_cexp(v, -1); | ||
| 114 | return Complex(f64).new(x * math.copysign(f64, 1, x), y); | ||
| 115 | } | ||
| 116 | // x >= 1455: result always overflows | ||
| 117 | else { | ||
| 118 | const h = 0x1p1023 * x; | ||
| 119 | return Complex(f64).new(h * math.cos(y), h * h * math.sin(y)); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | if (ix | lx == 0 and iy >= 0x7ff00000) { | ||
| 124 | return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y); | ||
| 125 | } | ||
| 126 | |||
| 127 | if (iy | ly == 0 and ix >= 0x7ff00000) { | ||
| 128 | if ((hx & 0xfffff) | lx == 0) { | ||
| 129 | return Complex(f64).new(x, y); | ||
| 130 | } | ||
| 131 | return Complex(f64).new(x, math.copysign(f64, 0, y)); | ||
| 132 | } | ||
| 133 | |||
| 134 | if (ix < 0x7ff00000 and iy >= 0x7ff00000) { | ||
| 135 | return Complex(f64).new(y - y, x * (y - y)); | ||
| 136 | } | ||
| 137 | |||
| 138 | if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) { | ||
| 139 | if (iy >= 0x7ff00000) { | ||
| 140 | return Complex(f64).new(x * x, x * (y - y)); | ||
| 141 | } | ||
| 142 | return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y)); | ||
| 143 | } | ||
| 144 | |||
| 145 | return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y)); | ||
| 146 | } | ||
| 147 | |||
| 148 | const epsilon = 0.0001; | ||
| 149 | |||
| 150 | test "complex.csinh32" { | ||
| 151 | const a = Complex(f32).new(5, 3); | ||
| 152 | const c = sinh(a); | ||
| 153 | |||
| 154 | const re = c.re; | ||
| 155 | const im = c.im; | ||
| 156 | |||
| 157 | debug.assert(math.approxEq(f32, re, -73.460617, epsilon)); | ||
| 158 | debug.assert(math.approxEq(f32, im, 10.472508, epsilon)); | ||
| 159 | } | ||
| 160 | |||
| 161 | test "complex.csinh64" { | ||
| 162 | const a = Complex(f64).new(5, 3); | ||
| 163 | const c = sinh(a); | ||
| 164 | |||
| 165 | const re = c.re; | ||
| 166 | const im = c.im; | ||
| 167 | |||
| 168 | debug.assert(math.approxEq(f64, re, -73.460617, epsilon)); | ||
| 169 | debug.assert(math.approxEq(f64, im, 10.472508, epsilon)); | ||
| 170 | } | ||
std/math/complex/sqrt.zig created+140| ... | @@ -0,0 +1,140 @@ | ||
| 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 | // NOTE: Returning @typeOf(z) here causes issues when trying to access the value. This is | ||
| 8 | // why we currently assign re, im parts to a new value explicitly for all tests. | ||
| 9 | pub fn sqrt(z: var) Complex(@typeOf(z.re)) { | ||
| 10 | const T = @typeOf(z.re); | ||
| 11 | |||
| 12 | return switch (T) { | ||
| 13 | f32 => sqrt32(z), | ||
| 14 | f64 => sqrt64(z), | ||
| 15 | else => @compileError("sqrt not implemented for " ++ @typeName(z)), | ||
| 16 | }; | ||
| 17 | } | ||
| 18 | |||
| 19 | fn sqrt32(z: &const Complex(f32)) Complex(f32) { | ||
| 20 | const x = z.re; | ||
| 21 | const y = z.im; | ||
| 22 | |||
| 23 | if (x == 0 and y == 0) { | ||
| 24 | return Complex(f32).new(0, y); | ||
| 25 | } | ||
| 26 | if (math.isInf(y)) { | ||
| 27 | return Complex(f32).new(math.inf(f32), y); | ||
| 28 | } | ||
| 29 | if (math.isNan(x)) { | ||
| 30 | // raise invalid if y is not nan | ||
| 31 | const t = (y - y) / (y - y); | ||
| 32 | return Complex(f32).new(x, t); | ||
| 33 | } | ||
| 34 | if (math.isInf(x)) { | ||
| 35 | // sqrt(inf + i nan) = inf + nan i | ||
| 36 | // sqrt(inf + iy) = inf + i0 | ||
| 37 | // sqrt(-inf + i nan) = nan +- inf i | ||
| 38 | // sqrt(-inf + iy) = 0 + inf i | ||
| 39 | if (math.signbit(x)) { | ||
| 40 | return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y)); | ||
| 41 | } else { | ||
| 42 | return Complex(f32).new(x, math.copysign(f32, y - y, y)); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | // y = nan special case is handled fine below | ||
| 47 | |||
| 48 | // double-precision avoids overflow with correct rounding. | ||
| 49 | const dx = f64(x); | ||
| 50 | const dy = f64(y); | ||
| 51 | |||
| 52 | if (dx >= 0) { | ||
| 53 | const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); | ||
| 54 | return Complex(f32).new(f32(t), f32(dy / (2.0 * t))); | ||
| 55 | } else { | ||
| 56 | const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); | ||
| 57 | return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y))); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | fn sqrt64(z: &const Complex(f64)) Complex(f64) { | ||
| 62 | // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2)) | ||
| 63 | const threshold = 0x1.a827999fcef32p+1022; | ||
| 64 | |||
| 65 | var x = z.re; | ||
| 66 | var y = z.im; | ||
| 67 | |||
| 68 | if (x == 0 and y == 0) { | ||
| 69 | return Complex(f64).new(0, y); | ||
| 70 | } | ||
| 71 | if (math.isInf(y)) { | ||
| 72 | return Complex(f64).new(math.inf(f64), y); | ||
| 73 | } | ||
| 74 | if (math.isNan(x)) { | ||
| 75 | // raise invalid if y is not nan | ||
| 76 | const t = (y - y) / (y - y); | ||
| 77 | return Complex(f64).new(x, t); | ||
| 78 | } | ||
| 79 | if (math.isInf(x)) { | ||
| 80 | // sqrt(inf + i nan) = inf + nan i | ||
| 81 | // sqrt(inf + iy) = inf + i0 | ||
| 82 | // sqrt(-inf + i nan) = nan +- inf i | ||
| 83 | // sqrt(-inf + iy) = 0 + inf i | ||
| 84 | if (math.signbit(x)) { | ||
| 85 | return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y)); | ||
| 86 | } else { | ||
| 87 | return Complex(f64).new(x, math.copysign(f64, y - y, y)); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | // y = nan special case is handled fine below | ||
| 92 | |||
| 93 | // scale to avoid overflow | ||
| 94 | var scale = false; | ||
| 95 | if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) { | ||
| 96 | x *= 0.25; | ||
| 97 | y *= 0.25; | ||
| 98 | scale = true; | ||
| 99 | } | ||
| 100 | |||
| 101 | var result: Complex(f64) = undefined; | ||
| 102 | if (x >= 0) { | ||
| 103 | const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5); | ||
| 104 | result = Complex(f64).new(t, y / (2.0 * t)); | ||
| 105 | } else { | ||
| 106 | const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5); | ||
| 107 | result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y)); | ||
| 108 | } | ||
| 109 | |||
| 110 | if (scale) { | ||
| 111 | result.re *= 2; | ||
| 112 | result.im *= 2; | ||
| 113 | } | ||
| 114 | |||
| 115 | return result; | ||
| 116 | } | ||
| 117 | |||
| 118 | const epsilon = 0.0001; | ||
| 119 | |||
| 120 | test "complex.csqrt32" { | ||
| 121 | const a = Complex(f32).new(5, 3); | ||
| 122 | const c = sqrt(a); | ||
| 123 | |||
| 124 | const re = c.re; | ||
| 125 | const im = c.im; | ||
| 126 | |||
| 127 | debug.assert(math.approxEq(f32, re, 2.327117, epsilon)); | ||
| 128 | debug.assert(math.approxEq(f32, im, 0.644574, epsilon)); | ||
| 129 | } | ||
| 130 | |||
| 131 | test "complex.csqrt64" { | ||
| 132 | const a = Complex(f64).new(5, 3); | ||
| 133 | const c = sqrt(a); | ||
| 134 | |||
| 135 | const re = c.re; | ||
| 136 | const im = c.im; | ||
| 137 | |||
| 138 | debug.assert(math.approxEq(f64, re, 2.3271175190399496, epsilon)); | ||
| 139 | debug.assert(math.approxEq(f64, im, 0.6445742373246469, epsilon)); | ||
| 140 | } | ||
std/math/complex/tan.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 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 tan(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.tanh(q); | ||
| 11 | return Complex(T).new(r.im, -r.re); | ||
| 12 | } | ||
| 13 | |||
| 14 | const epsilon = 0.0001; | ||
| 15 | |||
| 16 | test "complex.ctan" { | ||
| 17 | const a = Complex(f32).new(5, 3); | ||
| 18 | const c = tan(a); | ||
| 19 | |||
| 20 | const re = c.re; | ||
| 21 | const im = c.im; | ||
| 22 | |||
| 23 | debug.assert(math.approxEq(f32, re, -0.002708233, epsilon)); | ||
| 24 | debug.assert(math.approxEq(f32, im, 1.004165, epsilon)); | ||
| 25 | } | ||
std/math/complex/tanh.zig created+117| ... | @@ -0,0 +1,117 @@ | ||
| 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 tanh(z: var) Complex(@typeOf(z.re)) { | ||
| 8 | const T = @typeOf(z.re); | ||
| 9 | return switch (T) { | ||
| 10 | f32 => tanh32(z), | ||
| 11 | f64 => tanh64(z), | ||
| 12 | else => @compileError("tan not implemented for " ++ @typeName(z)), | ||
| 13 | }; | ||
| 14 | } | ||
| 15 | |||
| 16 | fn tanh32(z: &const Complex(f32)) Complex(f32) { | ||
| 17 | const x = z.re; | ||
| 18 | const y = z.im; | ||
| 19 | |||
| 20 | const hx = @bitCast(u32, x); | ||
| 21 | const ix = hx & 0x7fffffff; | ||
| 22 | |||
| 23 | if (ix >= 0x7f800000) { | ||
| 24 | if (ix & 0x7fffff != 0) { | ||
| 25 | const r = if (y == 0) y else x * y; | ||
| 26 | return Complex(f32).new(x, r); | ||
| 27 | } | ||
| 28 | const xx = @bitCast(f32, hx - 0x40000000); | ||
| 29 | const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); | ||
| 30 | return Complex(f32).new(xx, math.copysign(f32, 0, r)); | ||
| 31 | } | ||
| 32 | |||
| 33 | if (!math.isFinite(y)) { | ||
| 34 | const r = if (ix != 0) y - y else x; | ||
| 35 | return Complex(f32).new(r, y - y); | ||
| 36 | } | ||
| 37 | |||
| 38 | // x >= 11 | ||
| 39 | if (ix >= 0x41300000) { | ||
| 40 | const exp_mx = math.exp(-math.fabs(x)); | ||
| 41 | return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); | ||
| 42 | } | ||
| 43 | |||
| 44 | // Kahan's algorithm | ||
| 45 | const t = math.tan(y); | ||
| 46 | const beta = 1.0 + t * t; | ||
| 47 | const s = math.sinh(x); | ||
| 48 | const rho = math.sqrt(1 + s * s); | ||
| 49 | const den = 1 + beta * s * s; | ||
| 50 | |||
| 51 | return Complex(f32).new((beta * rho * s) / den, t / den); | ||
| 52 | } | ||
| 53 | |||
| 54 | fn tanh64(z: &const Complex(f64)) Complex(f64) { | ||
| 55 | const x = z.re; | ||
| 56 | const y = z.im; | ||
| 57 | |||
| 58 | const fx = @bitCast(u64, x); | ||
| 59 | const hx = u32(fx >> 32); | ||
| 60 | const lx = @truncate(u32, fx); | ||
| 61 | const ix = hx & 0x7fffffff; | ||
| 62 | |||
| 63 | if (ix >= 0x7ff00000) { | ||
| 64 | if ((ix & 0x7fffff) | lx != 0) { | ||
| 65 | const r = if (y == 0) y else x * y; | ||
| 66 | return Complex(f64).new(x, r); | ||
| 67 | } | ||
| 68 | |||
| 69 | const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx); | ||
| 70 | const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); | ||
| 71 | return Complex(f64).new(xx, math.copysign(f64, 0, r)); | ||
| 72 | } | ||
| 73 | |||
| 74 | if (!math.isFinite(y)) { | ||
| 75 | const r = if (ix != 0) y - y else x; | ||
| 76 | return Complex(f64).new(r, y - y); | ||
| 77 | } | ||
| 78 | |||
| 79 | // x >= 22 | ||
| 80 | if (ix >= 0x40360000) { | ||
| 81 | const exp_mx = math.exp(-math.fabs(x)); | ||
| 82 | return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); | ||
| 83 | } | ||
| 84 | |||
| 85 | // Kahan's algorithm | ||
| 86 | const t = math.tan(y); | ||
| 87 | const beta = 1.0 + t * t; | ||
| 88 | const s = math.sinh(x); | ||
| 89 | const rho = math.sqrt(1 + s * s); | ||
| 90 | const den = 1 + beta * s * s; | ||
| 91 | |||
| 92 | return Complex(f64).new((beta * rho * s) / den, t / den); | ||
| 93 | } | ||
| 94 | |||
| 95 | const epsilon = 0.0001; | ||
| 96 | |||
| 97 | test "complex.ctanh32" { | ||
| 98 | const a = Complex(f32).new(5, 3); | ||
| 99 | const c = tanh(a); | ||
| 100 | |||
| 101 | const re = c.re; | ||
| 102 | const im = c.im; | ||
| 103 | |||
| 104 | debug.assert(math.approxEq(f32, re, 0.999913, epsilon)); | ||
| 105 | debug.assert(math.approxEq(f32, im, -0.000025, epsilon)); | ||
| 106 | } | ||
| 107 | |||
| 108 | test "complex.ctanh64" { | ||
| 109 | const a = Complex(f64).new(5, 3); | ||
| 110 | const c = tanh(a); | ||
| 111 | |||
| 112 | const re = c.re; | ||
| 113 | const im = c.im; | ||
| 114 | |||
| 115 | debug.assert(math.approxEq(f64, re, 0.999913, epsilon)); | ||
| 116 | debug.assert(math.approxEq(f64, im, -0.000025, epsilon)); | ||
| 117 | } | ||
std/math/index.zig+5| ... | @@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos; | ... | @@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos; |
| 129 | pub const sin = @import("sin.zig").sin; | 129 | pub const sin = @import("sin.zig").sin; |
| 130 | pub const tan = @import("tan.zig").tan; | 130 | pub const tan = @import("tan.zig").tan; |
| 131 | 131 | ||
| 132 | pub const complex = @import("complex/index.zig"); | ||
| 133 | pub const Complex = complex.Complex; | ||
| 134 | |||
| 132 | test "math" { | 135 | test "math" { |
| 133 | _ = @import("nan.zig"); | 136 | _ = @import("nan.zig"); |
| 134 | _ = @import("isnan.zig"); | 137 | _ = @import("isnan.zig"); |
| ... | @@ -172,6 +175,8 @@ test "math" { | ... | @@ -172,6 +175,8 @@ test "math" { |
| 172 | _ = @import("sin.zig"); | 175 | _ = @import("sin.zig"); |
| 173 | _ = @import("cos.zig"); | 176 | _ = @import("cos.zig"); |
| 174 | _ = @import("tan.zig"); | 177 | _ = @import("tan.zig"); |
| 178 | |||
| 179 | _ = @import("complex/index.zig"); | ||
| 175 | } | 180 | } |
| 176 | 181 | ||
| 177 | 182 |