| author | |
| committer | |
| log | 7270f35c934ab5075c9d9128b5f5752c11d77056 |
| tree | 7d2791171cceaf8f81a3353afed564d45d3e5308 |
| parent | e5175d432ef01e078ef247ea0a781243219ddfb6 |
| parent | 27cbb44993389ae042a03266743379c0f15a523e |
30 files changed, 1540 insertions(+), 12 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" |
src/bigint.cpp+5| ... | @@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) | ... | @@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) |
| 86 | size_t digits_to_copy = bit_count / 64; | 86 | size_t digits_to_copy = bit_count / 64; |
| 87 | size_t leftover_bits = bit_count % 64; | 87 | size_t leftover_bits = bit_count % 64; |
| 88 | dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); | 88 | dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); |
| 89 | if (dest->digit_count == 1 && leftover_bits == 0) { | ||
| 90 | dest->data.digit = op_digits[0]; | ||
| 91 | if (dest->data.digit == 0) dest->digit_count = 0; | ||
| 92 | return; | ||
| 93 | } | ||
| 89 | dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count); | 94 | dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count); |
| 90 | for (size_t i = 0; i < digits_to_copy; i += 1) { | 95 | for (size_t i = 0; i < digits_to_copy; i += 1) { |
| 91 | uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0; | 96 | uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0; |
src/ir.cpp+3| ... | @@ -3147,6 +3147,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode | ... | @@ -3147,6 +3147,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode |
| 3147 | if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { | 3147 | if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { |
| 3148 | return noreturn_return_value; | 3148 | return noreturn_return_value; |
| 3149 | } | 3149 | } |
| 3150 | |||
| 3151 | ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); | ||
| 3152 | return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); | ||
| 3150 | } else { | 3153 | } else { |
| 3151 | incoming_blocks.append(irb->current_basic_block); | 3154 | incoming_blocks.append(irb->current_basic_block); |
| 3152 | incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node))); | 3155 | incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node))); |
std/crypto/throughput_test.zig+7-10| ... | @@ -1,17 +1,13 @@ | ... | @@ -1,17 +1,13 @@ |
| 1 | // Modify the HashFunction variable to the one wanted to test. | 1 | // Modify the HashFunction variable to the one wanted to test. |
| 2 | // | 2 | // |
| 3 | // NOTE: The throughput measurement may be slightly lower than other measurements since we run | ||
| 4 | // through our block alignment functions as well. Be aware when comparing against other tests. | ||
| 5 | // | ||
| 6 | // ``` | 3 | // ``` |
| 7 | // zig build-exe --release-fast --library c throughput_test.zig | 4 | // zig build-exe --release-fast throughput_test.zig |
| 8 | // ./throughput_test | 5 | // ./throughput_test |
| 9 | // ``` | 6 | // ``` |
| 10 | 7 | ||
| 11 | const std = @import("std"); | 8 | const std = @import("std"); |
| 12 | const c = @cImport({ | 9 | const time = std.os.time; |
| 13 | @cInclude("time.h"); | 10 | const Timer = time.Timer; |
| 14 | }); | ||
| 15 | const HashFunction = @import("md5.zig").Md5; | 11 | const HashFunction = @import("md5.zig").Md5; |
| 16 | 12 | ||
| 17 | const MiB = 1024 * 1024; | 13 | const MiB = 1024 * 1024; |
| ... | @@ -28,13 +24,14 @@ pub fn main() !void { | ... | @@ -28,13 +24,14 @@ pub fn main() !void { |
| 28 | var h = HashFunction.init(); | 24 | var h = HashFunction.init(); |
| 29 | var offset: usize = 0; | 25 | var offset: usize = 0; |
| 30 | 26 | ||
| 31 | const start = c.clock(); | 27 | var timer = try Timer.start(); |
| 28 | const start = timer.lap(); | ||
| 32 | while (offset < BytesToHash) : (offset += block.len) { | 29 | while (offset < BytesToHash) : (offset += block.len) { |
| 33 | h.update(block[0..]); | 30 | h.update(block[0..]); |
| 34 | } | 31 | } |
| 35 | const end = c.clock(); | 32 | const end = timer.read(); |
| 36 | 33 | ||
| 37 | const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC); | 34 | const elapsed_s = f64(end - start) / time.ns_per_s; |
| 38 | const throughput = u64(BytesToHash / elapsed_s); | 35 | const throughput = u64(BytesToHash / elapsed_s); |
| 39 | 36 | ||
| 40 | try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); | 37 | try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); |
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+21| ... | @@ -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 created+21| ... | @@ -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 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+27| ... | @@ -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 created+22| ... | @@ -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 created+130| ... | @@ -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 created+22| ... | @@ -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 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+21| ... | @@ -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 | } | ||
std/math/complex/cosh.zig created+165| ... | @@ -0,0 +1,165 @@ | ||
| 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 | debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon)); | ||
| 156 | debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon)); | ||
| 157 | } | ||
| 158 | |||
| 159 | test "complex.ccosh64" { | ||
| 160 | const a = Complex(f64).new(5, 3); | ||
| 161 | const c = cosh(a); | ||
| 162 | |||
| 163 | debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon)); | ||
| 164 | debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon)); | ||
| 165 | } | ||
std/math/complex/exp.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 | 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 | debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon)); | ||
| 131 | debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon)); | ||
| 132 | } | ||
| 133 | |||
| 134 | test "complex.cexp64" { | ||
| 135 | const a = Complex(f32).new(5, 3); | ||
| 136 | const c = exp(a); | ||
| 137 | |||
| 138 | debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon)); | ||
| 139 | debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon)); | ||
| 140 | } | ||
std/math/complex/index.zig created+171| ... | @@ -0,0 +1,171 @@ | ||
| 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 | pub fn Complex(comptime T: type) type { | ||
| 27 | return struct { | ||
| 28 | const Self = this; | ||
| 29 | |||
| 30 | re: T, | ||
| 31 | im: T, | ||
| 32 | |||
| 33 | pub fn new(re: T, im: T) Self { | ||
| 34 | return Self { | ||
| 35 | .re = re, | ||
| 36 | .im = im, | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn add(self: &const Self, other: &const Self) Self { | ||
| 41 | return Self { | ||
| 42 | .re = self.re + other.re, | ||
| 43 | .im = self.im + other.im, | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn sub(self: &const Self, other: &const Self) Self { | ||
| 48 | return Self { | ||
| 49 | .re = self.re - other.re, | ||
| 50 | .im = self.im - other.im, | ||
| 51 | }; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn mul(self: &const Self, other: &const Self) Self { | ||
| 55 | return Self { | ||
| 56 | .re = self.re * other.re - self.im * other.im, | ||
| 57 | .im = self.im * other.re + self.re * other.im, | ||
| 58 | }; | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn div(self: &const Self, other: &const Self) Self { | ||
| 62 | const re_num = self.re * other.re + self.im * other.im; | ||
| 63 | const im_num = self.im * other.re - self.re * other.im; | ||
| 64 | const den = other.re * other.re + other.im * other.im; | ||
| 65 | |||
| 66 | return Self { | ||
| 67 | .re = re_num / den, | ||
| 68 | .im = im_num / den, | ||
| 69 | }; | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn conjugate(self: &const Self) Self { | ||
| 73 | return Self { | ||
| 74 | .re = self.re, | ||
| 75 | .im = -self.im, | ||
| 76 | }; | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn reciprocal(self: &const Self) Self { | ||
| 80 | const m = self.re * self.re + self.im * self.im; | ||
| 81 | return Self { | ||
| 82 | .re = self.re / m, | ||
| 83 | .im = -self.im / m, | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn magnitude(self: &const Self) T { | ||
| 88 | return math.sqrt(self.re * self.re + self.im * self.im); | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | const epsilon = 0.0001; | ||
| 94 | |||
| 95 | test "complex.add" { | ||
| 96 | const a = Complex(f32).new(5, 3); | ||
| 97 | const b = Complex(f32).new(2, 7); | ||
| 98 | const c = a.add(b); | ||
| 99 | |||
| 100 | debug.assert(c.re == 7 and c.im == 10); | ||
| 101 | } | ||
| 102 | |||
| 103 | test "complex.sub" { | ||
| 104 | const a = Complex(f32).new(5, 3); | ||
| 105 | const b = Complex(f32).new(2, 7); | ||
| 106 | const c = a.sub(b); | ||
| 107 | |||
| 108 | debug.assert(c.re == 3 and c.im == -4); | ||
| 109 | } | ||
| 110 | |||
| 111 | test "complex.mul" { | ||
| 112 | const a = Complex(f32).new(5, 3); | ||
| 113 | const b = Complex(f32).new(2, 7); | ||
| 114 | const c = a.mul(b); | ||
| 115 | |||
| 116 | debug.assert(c.re == -11 and c.im == 41); | ||
| 117 | } | ||
| 118 | |||
| 119 | test "complex.div" { | ||
| 120 | const a = Complex(f32).new(5, 3); | ||
| 121 | const b = Complex(f32).new(2, 7); | ||
| 122 | const c = a.div(b); | ||
| 123 | |||
| 124 | debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and | ||
| 125 | math.approxEq(f32, c.im, f32(-29)/53, epsilon)); | ||
| 126 | } | ||
| 127 | |||
| 128 | test "complex.conjugate" { | ||
| 129 | const a = Complex(f32).new(5, 3); | ||
| 130 | const c = a.conjugate(); | ||
| 131 | |||
| 132 | debug.assert(c.re == 5 and c.im == -3); | ||
| 133 | } | ||
| 134 | |||
| 135 | test "complex.reciprocal" { | ||
| 136 | const a = Complex(f32).new(5, 3); | ||
| 137 | const c = a.reciprocal(); | ||
| 138 | |||
| 139 | debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and | ||
| 140 | math.approxEq(f32, c.im, f32(-3)/34, epsilon)); | ||
| 141 | } | ||
| 142 | |||
| 143 | test "complex.magnitude" { | ||
| 144 | const a = Complex(f32).new(5, 3); | ||
| 145 | const c = a.magnitude(); | ||
| 146 | |||
| 147 | debug.assert(math.approxEq(f32, c, 5.83095, epsilon)); | ||
| 148 | } | ||
| 149 | |||
| 150 | test "complex.cmath" { | ||
| 151 | _ = @import("abs.zig"); | ||
| 152 | _ = @import("acosh.zig"); | ||
| 153 | _ = @import("acos.zig"); | ||
| 154 | _ = @import("arg.zig"); | ||
| 155 | _ = @import("asinh.zig"); | ||
| 156 | _ = @import("asin.zig"); | ||
| 157 | _ = @import("atanh.zig"); | ||
| 158 | _ = @import("atan.zig"); | ||
| 159 | _ = @import("conj.zig"); | ||
| 160 | _ = @import("cosh.zig"); | ||
| 161 | _ = @import("cos.zig"); | ||
| 162 | _ = @import("exp.zig"); | ||
| 163 | _ = @import("log.zig"); | ||
| 164 | _ = @import("pow.zig"); | ||
| 165 | _ = @import("proj.zig"); | ||
| 166 | _ = @import("sinh.zig"); | ||
| 167 | _ = @import("sin.zig"); | ||
| 168 | _ = @import("sqrt.zig"); | ||
| 169 | _ = @import("tanh.zig"); | ||
| 170 | _ = @import("tan.zig"); | ||
| 171 | } | ||
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+23| ... | @@ -0,0 +1,23 @@ | ||
| 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 | debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon)); | ||
| 22 | debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon)); | ||
| 23 | } | ||
std/math/complex/pow.zig created+22| ... | @@ -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 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 | debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon)); | ||
| 21 | debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon)); | ||
| 22 | } | ||
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+22| ... | @@ -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 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 | debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon)); | ||
| 21 | debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon)); | ||
| 22 | } | ||
std/math/complex/sinh.zig created+164| ... | @@ -0,0 +1,164 @@ | ||
| 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 | debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon)); | ||
| 155 | debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon)); | ||
| 156 | } | ||
| 157 | |||
| 158 | test "complex.csinh64" { | ||
| 159 | const a = Complex(f64).new(5, 3); | ||
| 160 | const c = sinh(a); | ||
| 161 | |||
| 162 | debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon)); | ||
| 163 | debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon)); | ||
| 164 | } | ||
std/math/complex/sqrt.zig created+133| ... | @@ -0,0 +1,133 @@ | ||
| 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 | // TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re)) | ||
| 8 | pub fn sqrt(z: var) Complex(@typeOf(z.re)) { | ||
| 9 | const T = @typeOf(z.re); | ||
| 10 | |||
| 11 | return switch (T) { | ||
| 12 | f32 => sqrt32(z), | ||
| 13 | f64 => sqrt64(z), | ||
| 14 | else => @compileError("sqrt not implemented for " ++ @typeName(z)), | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | fn sqrt32(z: &const Complex(f32)) Complex(f32) { | ||
| 19 | const x = z.re; | ||
| 20 | const y = z.im; | ||
| 21 | |||
| 22 | if (x == 0 and y == 0) { | ||
| 23 | return Complex(f32).new(0, y); | ||
| 24 | } | ||
| 25 | if (math.isInf(y)) { | ||
| 26 | return Complex(f32).new(math.inf(f32), y); | ||
| 27 | } | ||
| 28 | if (math.isNan(x)) { | ||
| 29 | // raise invalid if y is not nan | ||
| 30 | const t = (y - y) / (y - y); | ||
| 31 | return Complex(f32).new(x, t); | ||
| 32 | } | ||
| 33 | if (math.isInf(x)) { | ||
| 34 | // sqrt(inf + i nan) = inf + nan i | ||
| 35 | // sqrt(inf + iy) = inf + i0 | ||
| 36 | // sqrt(-inf + i nan) = nan +- inf i | ||
| 37 | // sqrt(-inf + iy) = 0 + inf i | ||
| 38 | if (math.signbit(x)) { | ||
| 39 | return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y)); | ||
| 40 | } else { | ||
| 41 | return Complex(f32).new(x, math.copysign(f32, y - y, y)); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | // y = nan special case is handled fine below | ||
| 46 | |||
| 47 | // double-precision avoids overflow with correct rounding. | ||
| 48 | const dx = f64(x); | ||
| 49 | const dy = f64(y); | ||
| 50 | |||
| 51 | if (dx >= 0) { | ||
| 52 | const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); | ||
| 53 | return Complex(f32).new(f32(t), f32(dy / (2.0 * t))); | ||
| 54 | } else { | ||
| 55 | const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); | ||
| 56 | return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y))); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | fn sqrt64(z: &const Complex(f64)) Complex(f64) { | ||
| 61 | // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2)) | ||
| 62 | const threshold = 0x1.a827999fcef32p+1022; | ||
| 63 | |||
| 64 | var x = z.re; | ||
| 65 | var y = z.im; | ||
| 66 | |||
| 67 | if (x == 0 and y == 0) { | ||
| 68 | return Complex(f64).new(0, y); | ||
| 69 | } | ||
| 70 | if (math.isInf(y)) { | ||
| 71 | return Complex(f64).new(math.inf(f64), y); | ||
| 72 | } | ||
| 73 | if (math.isNan(x)) { | ||
| 74 | // raise invalid if y is not nan | ||
| 75 | const t = (y - y) / (y - y); | ||
| 76 | return Complex(f64).new(x, t); | ||
| 77 | } | ||
| 78 | if (math.isInf(x)) { | ||
| 79 | // sqrt(inf + i nan) = inf + nan i | ||
| 80 | // sqrt(inf + iy) = inf + i0 | ||
| 81 | // sqrt(-inf + i nan) = nan +- inf i | ||
| 82 | // sqrt(-inf + iy) = 0 + inf i | ||
| 83 | if (math.signbit(x)) { | ||
| 84 | return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y)); | ||
| 85 | } else { | ||
| 86 | return Complex(f64).new(x, math.copysign(f64, y - y, y)); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | // y = nan special case is handled fine below | ||
| 91 | |||
| 92 | // scale to avoid overflow | ||
| 93 | var scale = false; | ||
| 94 | if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) { | ||
| 95 | x *= 0.25; | ||
| 96 | y *= 0.25; | ||
| 97 | scale = true; | ||
| 98 | } | ||
| 99 | |||
| 100 | var result: Complex(f64) = undefined; | ||
| 101 | if (x >= 0) { | ||
| 102 | const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5); | ||
| 103 | result = Complex(f64).new(t, y / (2.0 * t)); | ||
| 104 | } else { | ||
| 105 | const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5); | ||
| 106 | result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y)); | ||
| 107 | } | ||
| 108 | |||
| 109 | if (scale) { | ||
| 110 | result.re *= 2; | ||
| 111 | result.im *= 2; | ||
| 112 | } | ||
| 113 | |||
| 114 | return result; | ||
| 115 | } | ||
| 116 | |||
| 117 | const epsilon = 0.0001; | ||
| 118 | |||
| 119 | test "complex.csqrt32" { | ||
| 120 | const a = Complex(f32).new(5, 3); | ||
| 121 | const c = sqrt(a); | ||
| 122 | |||
| 123 | debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon)); | ||
| 124 | debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon)); | ||
| 125 | } | ||
| 126 | |||
| 127 | test "complex.csqrt64" { | ||
| 128 | const a = Complex(f64).new(5, 3); | ||
| 129 | const c = sqrt(a); | ||
| 130 | |||
| 131 | debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon)); | ||
| 132 | debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon)); | ||
| 133 | } | ||
std/math/complex/tan.zig created+22| ... | @@ -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 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 | debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon)); | ||
| 21 | debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon)); | ||
| 22 | } | ||
std/math/complex/tanh.zig created+111| ... | @@ -0,0 +1,111 @@ | ||
| 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 | debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon)); | ||
| 102 | debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon)); | ||
| 103 | } | ||
| 104 | |||
| 105 | test "complex.ctanh64" { | ||
| 106 | const a = Complex(f64).new(5, 3); | ||
| 107 | const c = tanh(a); | ||
| 108 | |||
| 109 | debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon)); | ||
| 110 | debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon)); | ||
| 111 | } | ||
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 |
std/mem.zig+82-2| ... | @@ -20,7 +20,7 @@ pub const Allocator = struct { | ... | @@ -20,7 +20,7 @@ pub const Allocator = struct { |
| 20 | /// * alignment >= alignment of old_mem.ptr | 20 | /// * alignment >= alignment of old_mem.ptr |
| 21 | /// | 21 | /// |
| 22 | /// If `new_byte_count <= old_mem.len`: | 22 | /// If `new_byte_count <= old_mem.len`: |
| 23 | /// * this function must return successfully. | 23 | /// * this function must return successfully. |
| 24 | /// * alignment <= alignment of old_mem.ptr | 24 | /// * alignment <= alignment of old_mem.ptr |
| 25 | /// | 25 | /// |
| 26 | /// The returned newly allocated memory is undefined. | 26 | /// The returned newly allocated memory is undefined. |
| ... | @@ -174,6 +174,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T { | ... | @@ -174,6 +174,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T { |
| 174 | return new_buf; | 174 | return new_buf; |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | /// Remove values from the beginning of a slice. | ||
| 178 | pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { | ||
| 179 | var begin: usize = 0; | ||
| 180 | while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {} | ||
| 181 | return slice[begin..]; | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Remove values from the end of a slice. | ||
| 185 | pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { | ||
| 186 | var end: usize = slice.len; | ||
| 187 | while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {} | ||
| 188 | return slice[0..end]; | ||
| 189 | } | ||
| 190 | |||
| 177 | /// Remove values from the beginning and end of a slice. | 191 | /// Remove values from the beginning and end of a slice. |
| 178 | pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { | 192 | pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { |
| 179 | var begin: usize = 0; | 193 | var begin: usize = 0; |
| ... | @@ -184,6 +198,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co | ... | @@ -184,6 +198,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co |
| 184 | } | 198 | } |
| 185 | 199 | ||
| 186 | test "mem.trim" { | 200 | test "mem.trim" { |
| 201 | assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n ")); | ||
| 202 | assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo")); | ||
| 187 | assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo")); | 203 | assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo")); |
| 188 | assert(eql(u8, trim(u8, "foo", " \n"), "foo")); | 204 | assert(eql(u8, trim(u8, "foo", " \n"), "foo")); |
| 189 | } | 205 | } |
| ... | @@ -193,6 +209,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { | ... | @@ -193,6 +209,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { |
| 193 | return indexOfScalarPos(T, slice, 0, value); | 209 | return indexOfScalarPos(T, slice, 0, value); |
| 194 | } | 210 | } |
| 195 | 211 | ||
| 212 | /// Linear search for the last index of a scalar value inside a slice. | ||
| 213 | pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { | ||
| 214 | var i: usize = slice.len; | ||
| 215 | while (i != 0) { | ||
| 216 | i -= 1; | ||
| 217 | if (slice[i] == value) | ||
| 218 | return i; | ||
| 219 | } | ||
| 220 | return null; | ||
| 221 | } | ||
| 222 | |||
| 196 | pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { | 223 | pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { |
| 197 | var i: usize = start_index; | 224 | var i: usize = start_index; |
| 198 | while (i < slice.len) : (i += 1) { | 225 | while (i < slice.len) : (i += 1) { |
| ... | @@ -206,6 +233,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize | ... | @@ -206,6 +233,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize |
| 206 | return indexOfAnyPos(T, slice, 0, values); | 233 | return indexOfAnyPos(T, slice, 0, values); |
| 207 | } | 234 | } |
| 208 | 235 | ||
| 236 | pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { | ||
| 237 | var i: usize = slice.len; | ||
| 238 | while (i != 0) { | ||
| 239 | i -= 1; | ||
| 240 | for (values) |value| { | ||
| 241 | if (slice[i] == value) | ||
| 242 | return i; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | return null; | ||
| 246 | } | ||
| 247 | |||
| 209 | pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { | 248 | pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { |
| 210 | var i: usize = start_index; | 249 | var i: usize = start_index; |
| 211 | while (i < slice.len) : (i += 1) { | 250 | while (i < slice.len) : (i += 1) { |
| ... | @@ -221,6 +260,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize | ... | @@ -221,6 +260,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize |
| 221 | return indexOfPos(T, haystack, 0, needle); | 260 | return indexOfPos(T, haystack, 0, needle); |
| 222 | } | 261 | } |
| 223 | 262 | ||
| 263 | /// Find the index in a slice of a sub-slice, searching from the end backwards. | ||
| 264 | /// To start looking at a different index, slice the haystack first. | ||
| 265 | /// TODO is there even a better algorithm for this? | ||
| 266 | pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { | ||
| 267 | if (needle.len > haystack.len) | ||
| 268 | return null; | ||
| 269 | |||
| 270 | var i: usize = haystack.len - needle.len; | ||
| 271 | while (true) : (i -= 1) { | ||
| 272 | if (mem.eql(T, haystack[i..i+needle.len], needle)) | ||
| 273 | return i; | ||
| 274 | if (i == 0) | ||
| 275 | return null; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 224 | // TODO boyer-moore algorithm | 279 | // TODO boyer-moore algorithm |
| 225 | pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize { | 280 | pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize { |
| 226 | if (needle.len > haystack.len) | 281 | if (needle.len > haystack.len) |
| ... | @@ -237,9 +292,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee | ... | @@ -237,9 +292,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee |
| 237 | 292 | ||
| 238 | test "mem.indexOf" { | 293 | test "mem.indexOf" { |
| 239 | assert(??indexOf(u8, "one two three four", "four") == 14); | 294 | assert(??indexOf(u8, "one two three four", "four") == 14); |
| 295 | assert(??lastIndexOf(u8, "one two three two four", "two") == 14); | ||
| 240 | assert(indexOf(u8, "one two three four", "gour") == null); | 296 | assert(indexOf(u8, "one two three four", "gour") == null); |
| 297 | assert(lastIndexOf(u8, "one two three four", "gour") == null); | ||
| 241 | assert(??indexOf(u8, "foo", "foo") == 0); | 298 | assert(??indexOf(u8, "foo", "foo") == 0); |
| 299 | assert(??lastIndexOf(u8, "foo", "foo") == 0); | ||
| 242 | assert(indexOf(u8, "foo", "fool") == null); | 300 | assert(indexOf(u8, "foo", "fool") == null); |
| 301 | assert(lastIndexOf(u8, "foo", "lfoo") == null); | ||
| 302 | assert(lastIndexOf(u8, "foo", "fool") == null); | ||
| 303 | |||
| 304 | assert(??indexOf(u8, "foo foo", "foo") == 0); | ||
| 305 | assert(??lastIndexOf(u8, "foo foo", "foo") == 4); | ||
| 306 | assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6); | ||
| 307 | assert(??lastIndexOfScalar(u8, "boo", 'o') == 2); | ||
| 243 | } | 308 | } |
| 244 | 309 | ||
| 245 | /// Reads an integer from memory with size equal to bytes.len. | 310 | /// Reads an integer from memory with size equal to bytes.len. |
| ... | @@ -359,9 +424,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool | ... | @@ -359,9 +424,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool |
| 359 | return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle); | 424 | return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle); |
| 360 | } | 425 | } |
| 361 | 426 | ||
| 427 | test "mem.startsWith" { | ||
| 428 | assert(startsWith(u8, "Bob", "Bo")); | ||
| 429 | assert(!startsWith(u8, "Needle in haystack", "haystack")); | ||
| 430 | } | ||
| 431 | |||
| 432 | pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { | ||
| 433 | return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); | ||
| 434 | } | ||
| 435 | |||
| 436 | |||
| 437 | test "mem.endsWith" { | ||
| 438 | assert(endsWith(u8, "Needle in haystack", "haystack")); | ||
| 439 | assert(!endsWith(u8, "Bob", "Bo")); | ||
| 440 | } | ||
| 441 | |||
| 362 | pub const SplitIterator = struct { | 442 | pub const SplitIterator = struct { |
| 363 | buffer: []const u8, | 443 | buffer: []const u8, |
| 364 | split_bytes: []const u8, | 444 | split_bytes: []const u8, |
| 365 | index: usize, | 445 | index: usize, |
| 366 | 446 | ||
| 367 | pub fn next(self: &SplitIterator) ?[]const u8 { | 447 | pub fn next(self: &SplitIterator) ?[]const u8 { |
test/cases/defer.zig+11| ... | @@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void { | ... | @@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void { |
| 41 | assert(i == 5); | 41 | assert(i == 5); |
| 42 | } | 42 | } |
| 43 | } | 43 | } |
| 44 | |||
| 45 | test "defer and labeled break" { | ||
| 46 | var i = usize(0); | ||
| 47 | |||
| 48 | blk: { | ||
| 49 | defer i += 1; | ||
| 50 | break :blk; | ||
| 51 | } | ||
| 52 | |||
| 53 | assert(i == 1); | ||
| 54 | } |
test/cases/eval.zig+16| ... | @@ -513,3 +513,19 @@ test "array concat of slices gives slice" { | ... | @@ -513,3 +513,19 @@ test "array concat of slices gives slice" { |
| 513 | assert(std.mem.eql(u8, c, "aoeuasdf")); | 513 | assert(std.mem.eql(u8, c, "aoeuasdf")); |
| 514 | } | 514 | } |
| 515 | } | 515 | } |
| 516 | |||
| 517 | test "comptime shlWithOverflow" { | ||
| 518 | const ct_shifted: u64 = comptime amt: { | ||
| 519 | var amt = u64(0); | ||
| 520 | _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); | ||
| 521 | break :amt amt; | ||
| 522 | }; | ||
| 523 | |||
| 524 | const rt_shifted: u64 = amt: { | ||
| 525 | var amt = u64(0); | ||
| 526 | _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); | ||
| 527 | break :amt amt; | ||
| 528 | }; | ||
| 529 | |||
| 530 | assert(ct_shifted == rt_shifted); | ||
| 531 | } |