724 lines
25 KiB
HTML
724 lines
25 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
|
<title>The Zig Programming Language</title>
|
|
<link rel="stylesheet" type="text/css" href="highlight/styles/default.css">
|
|
<style type="text/css">
|
|
img {
|
|
max-width: 100%;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<img src="zig-logo.svg">
|
|
<p>
|
|
Zig is an open-source programming language designed for <strong>robustness</strong>,
|
|
<strong>optimality</strong>, and <strong>clarity</strong>.
|
|
</p>
|
|
<p>
|
|
<a href="download/">Download</a> |
|
|
<a href="documentation/master/">Documentation</a> |
|
|
<a href="https://github.com/zig-lang/zig">Source Code</a> |
|
|
<a href="https://github.com/zig-lang/zig/issues">Bug Tracker</a> |
|
|
<a href="https://webchat.freenode.net/?channels=%23zig">IRC</a> |
|
|
<a href="https://www.patreon.com/andrewrk">Donate $1/month</a>
|
|
</p>
|
|
<h2>Feature Highlights</h2>
|
|
<ul>
|
|
<li>Manual memory management. Memory allocation failure is handled correctly. Edge cases matter!</li>
|
|
<li>Zig competes with C instead of depending on it. The Zig Standard Library does not depend on libc.</li>
|
|
<li>Small, simple language. Focus on debugging your application rather than debugging your knowledge of your programming language.</li>
|
|
<li>A fresh take on error handling that resembles what well-written C error handling looks like,
|
|
minus the boilerplate and verbosity.</li>
|
|
<li>Debug mode optimizes for fast compilation time and crashing with a stack trace when undefined behavior
|
|
<em>would</em> happen.</li>
|
|
<li>ReleaseFast mode produces heavily optimized code. What other projects call
|
|
"Link Time Optimization" Zig does automatically.</li>
|
|
<li>ReleaseSafe mode produces optimized code but keeps safety checks enabled. Disable safety checks in the bottlenecks of your code.</li>
|
|
<li>Generic data structures and functions.</li>
|
|
<li>Compile-time reflection and compile-time code execution.</li>
|
|
<li>Import .h files and directly use C types, variables, and functions.</li>
|
|
<li>Export functions, variables, and types for C code to depend on. Automatically generate .h files.</li>
|
|
<li>Nullable type instead of null pointers.</li>
|
|
<li>Order independent top level declarations.</li>
|
|
<li>Friendly toward package maintainers. Reproducible build, bootstrapping process carefully documented. Issues filed by package maintainers are considered especially important.</li>
|
|
<li>Cross-compiling is a first-class use case.</li>
|
|
<li>No preprocessor. Instead Zig has a few carefully designed features that
|
|
provide a way to accomplish things you might do with a preprocessor.</li>
|
|
</ul>
|
|
<h2 id="reading-material">Reading Material</h2>
|
|
<ul>
|
|
<li>2017-10-17 - <a href="download/0.1.1/release-notes.html">Zig 0.1.1 Release Notes</a></li>
|
|
<li>2017-07-19 - <a href="http://tiehuis.github.io/iterative-replacement-of-c-with-zig">Iterative Replacement of C with Zig</a></li>
|
|
<li>2017-02-16 - <a href="http://andrewkelley.me/post/a-better-way-to-implement-bit-fields.html">A Better Way to Implement Bit-Fields</a></li>
|
|
<li>2017-02-13 - <a href="http://andrewkelley.me/post/zig-already-more-knowable-than-c.html">Zig: Already More Knowable Than C</a></li>
|
|
<li>2017-01-30 - <a href="http://andrewkelley.me/post/zig-programming-language-blurs-line-compile-time-run-time.html">Zig Programming Language Blurs the Line Between Compile-Time and Run-Time</a></li>
|
|
<li>2016-02-08 - <a href="http://andrewkelley.me/post/intro-to-zig.html">Introduction to the Zig Programming Language</a></li>
|
|
</ul>
|
|
<h2 id="source-examples">Source Code Examples</h2>
|
|
<ul>
|
|
<li><a href="#hello">Hello World</a></li>
|
|
<li><a href="#hello_libc">Hello World with libc</a></li>
|
|
<li><a href="#parse">Parsing Unsigned Integers</a></li>
|
|
<li><a href="#hashmap">HashMap with Custom Allocator</a></li>
|
|
<li><a href="#tetris">Tetris Clone</a></li>
|
|
<li><a href="#clashos">Bare Bones Operating System</a></li>
|
|
<li><a href="#cat">Cat Utility</a></li>
|
|
<li><a href="#multiline-strings">Multiline String Syntax</a></li>
|
|
<li><a href="#mersenne">Mersenne Twister Random Number Generator</a></li>
|
|
</ul>
|
|
<h3 id="hello">Hello World</h3>
|
|
<pre><code class="zig">const std = @import("std");
|
|
|
|
pub fn main() -> %void {
|
|
// If this program is run without stdout attached, exit with an error.
|
|
var stdout_file = %return std.io.getStdOut();
|
|
// If this program encounters pipe failure when printing to stdout, exit
|
|
// with an error.
|
|
%return stdout_file.write("Hello, world!\n");
|
|
}</code></pre>
|
|
<p>Build this with:</p>
|
|
<pre>zig build-exe hello.zig</pre>
|
|
<h3 id="hello_libc">Hello World with libc</h3>
|
|
<pre><code class="zig">const c = @cImport({
|
|
// See https://github.com/zig-lang/zig/issues/515
|
|
@cDefine("_NO_CRT_STDIO_INLINE", "1");
|
|
@cInclude("stdio.h");
|
|
@cInclude("string.h");
|
|
});
|
|
|
|
const msg = c"Hello, world!\n";
|
|
|
|
export fn main(argc: c_int, argv: &&u8) -> c_int {
|
|
if (c.printf(msg) != c_int(c.strlen(msg)))
|
|
return -1;
|
|
|
|
return 0;
|
|
}</code></pre>
|
|
<p>Build this with:</p>
|
|
<pre>zig build-exe hello.zig --library c</pre>
|
|
<h3 id="parse">Parsing Unsigned Integers</h3>
|
|
<pre><code class="zig">pub fn parseUnsigned(comptime T: type, buf: []u8, radix: u8) -> %T {
|
|
var x: T = 0;
|
|
|
|
for (buf) |c| {
|
|
const digit = %return charToDigit(c, radix);
|
|
x = %return mulOverflow(T, x, radix);
|
|
x = %return addOverflow(T, x, digit);
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
error InvalidChar;
|
|
|
|
fn charToDigit(c: u8, radix: u8) -> %u8 {
|
|
const value = switch (c) {
|
|
'0' ... '9' => c - '0',
|
|
'A' ... 'Z' => c - 'A' + 10,
|
|
'a' ... 'z' => c - 'a' + 10,
|
|
else => return error.InvalidChar,
|
|
};
|
|
|
|
if (value >= radix)
|
|
return error.InvalidChar;
|
|
|
|
return value;
|
|
}
|
|
|
|
error Overflow;
|
|
|
|
pub fn mulOverflow(comptime T: type, a: T, b: T) -> %T {
|
|
var answer: T = undefined;
|
|
if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer
|
|
}
|
|
|
|
pub fn addOverflow(comptime T: type, a: T, b: T) -> %T {
|
|
var answer: T = undefined;
|
|
if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer
|
|
}
|
|
|
|
fn getNumberWithDefault(s: []u8) -> u32 {
|
|
parseUnsigned(u32, s, 10) %% 42
|
|
}
|
|
|
|
fn getNumberOrCrash(s: []u8) -> u32 {
|
|
%%parseUnsigned(u32, s, 10)
|
|
}
|
|
|
|
fn addTwoTogetherOrReturnErr(a_str: []u8, b_str: []u8) -> %u32 {
|
|
const a = parseUnsigned(u32, a_str, 10) %% |err| return err;
|
|
const b = parseUnsigned(u32, b_str, 10) %% |err| return err;
|
|
return a + b;
|
|
}</code></pre>
|
|
<h3 id="hashmap">HashMap with Custom Allocator</h3>
|
|
<pre><code class="zig">const debug = @import("debug.zig");
|
|
const assert = debug.assert;
|
|
const math = @import("math.zig");
|
|
const mem = @import("mem.zig");
|
|
const Allocator = mem.Allocator;
|
|
|
|
const want_modification_safety = !@compileVar("is_release");
|
|
const debug_u32 = if (want_modification_safety) u32 else void;
|
|
|
|
pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn(key: K)->u32,
|
|
comptime eql: fn(a: K, b: K)->bool) -> type
|
|
{
|
|
struct {
|
|
entries: []Entry,
|
|
size: usize,
|
|
max_distance_from_start_index: usize,
|
|
allocator: &Allocator,
|
|
// this is used to detect bugs where a hashtable is edited while an iterator is running.
|
|
modification_count: debug_u32,
|
|
|
|
const Self = this;
|
|
|
|
pub const Entry = struct {
|
|
used: bool,
|
|
distance_from_start_index: usize,
|
|
key: K,
|
|
value: V,
|
|
};
|
|
|
|
pub const Iterator = struct {
|
|
hm: &Self,
|
|
// how many items have we returned
|
|
count: usize,
|
|
// iterator through the entry array
|
|
index: usize,
|
|
// used to detect concurrent modification
|
|
initial_modification_count: debug_u32,
|
|
|
|
pub fn next(it: &Iterator) -> ?&Entry {
|
|
if (want_modification_safety) {
|
|
assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
|
|
}
|
|
if (it.count >= it.hm.size) return null;
|
|
while (it.index < it.hm.entries.len) : (it.index += 1) {
|
|
const entry = &it.hm.entries[it.index];
|
|
if (entry.used) {
|
|
it.index += 1;
|
|
it.count += 1;
|
|
return entry;
|
|
}
|
|
}
|
|
unreachable // no next item
|
|
}
|
|
};
|
|
|
|
pub fn init(hm: &Self, allocator: &Allocator) {
|
|
hm.entries = []Entry{};
|
|
hm.allocator = allocator;
|
|
hm.size = 0;
|
|
hm.max_distance_from_start_index = 0;
|
|
// it doesn't actually matter what we set this to since we use wrapping integer arithmetic
|
|
hm.modification_count = undefined;
|
|
}
|
|
|
|
pub fn deinit(hm: &Self) {
|
|
hm.allocator.free(Entry, hm.entries);
|
|
}
|
|
|
|
pub fn clear(hm: &Self) {
|
|
for (hm.entries) |*entry| {
|
|
entry.used = false;
|
|
}
|
|
hm.size = 0;
|
|
hm.max_distance_from_start_index = 0;
|
|
hm.incrementModificationCount();
|
|
}
|
|
|
|
pub fn put(hm: &Self, key: K, value: V) -> %void {
|
|
if (hm.entries.len == 0) {
|
|
%return hm.initCapacity(16);
|
|
}
|
|
hm.incrementModificationCount();
|
|
|
|
// if we get too full (60%), double the capacity
|
|
if (hm.size * 5 >= hm.entries.len * 3) {
|
|
const old_entries = hm.entries;
|
|
%return hm.initCapacity(hm.entries.len * 2);
|
|
// dump all of the old elements into the new table
|
|
for (old_entries) |*old_entry| {
|
|
if (old_entry.used) {
|
|
hm.internalPut(old_entry.key, old_entry.value);
|
|
}
|
|
}
|
|
hm.allocator.free(Entry, old_entries);
|
|
}
|
|
|
|
hm.internalPut(key, value);
|
|
}
|
|
|
|
pub fn get(hm: &Self, key: K) -> ?&Entry {
|
|
return hm.internalGet(key);
|
|
}
|
|
|
|
pub fn remove(hm: &Self, key: K) {
|
|
hm.incrementModificationCount();
|
|
const start_index = hm.keyToIndex(key);
|
|
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
|
|
const index = (start_index + roll_over) % hm.entries.len;
|
|
var entry = &hm.entries[index];
|
|
|
|
assert(entry.used); // key not found
|
|
|
|
if (!eql(entry.key, key)) continue;
|
|
|
|
while (roll_over < hm.entries.len) : (roll_over += 1) {
|
|
const next_index = (start_index + roll_over + 1) % hm.entries.len;
|
|
const next_entry = &hm.entries[next_index];
|
|
if (!next_entry.used or next_entry.distance_from_start_index == 0) {
|
|
entry.used = false;
|
|
hm.size -= 1;
|
|
return;
|
|
}
|
|
*entry = *next_entry;
|
|
entry.distance_from_start_index -= 1;
|
|
entry = next_entry;
|
|
}
|
|
unreachable // shifting everything in the table
|
|
}}
|
|
unreachable // key not found
|
|
}
|
|
|
|
pub fn entryIterator(hm: &Self) -> Iterator {
|
|
return Iterator {
|
|
.hm = hm,
|
|
.count = 0,
|
|
.index = 0,
|
|
.initial_modification_count = hm.modification_count,
|
|
};
|
|
}
|
|
|
|
fn initCapacity(hm: &Self, capacity: usize) -> %void {
|
|
hm.entries = %return hm.allocator.alloc(Entry, capacity);
|
|
hm.size = 0;
|
|
hm.max_distance_from_start_index = 0;
|
|
for (hm.entries) |*entry| {
|
|
entry.used = false;
|
|
}
|
|
}
|
|
|
|
fn incrementModificationCount(hm: &Self) {
|
|
if (want_modification_safety) {
|
|
hm.modification_count +%= 1;
|
|
}
|
|
}
|
|
|
|
fn internalPut(hm: &Self, orig_key: K, orig_value: V) {
|
|
var key = orig_key;
|
|
var value = orig_value;
|
|
const start_index = hm.keyToIndex(key);
|
|
var roll_over: usize = 0;
|
|
var distance_from_start_index: usize = 0;
|
|
while (roll_over < hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1}) {
|
|
const index = (start_index + roll_over) % hm.entries.len;
|
|
const entry = &hm.entries[index];
|
|
|
|
if (entry.used and !eql(entry.key, key)) {
|
|
if (entry.distance_from_start_index < distance_from_start_index) {
|
|
// robin hood to the rescue
|
|
const tmp = *entry;
|
|
hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index,
|
|
distance_from_start_index);
|
|
*entry = Entry {
|
|
.used = true,
|
|
.distance_from_start_index = distance_from_start_index,
|
|
.key = key,
|
|
.value = value,
|
|
};
|
|
key = tmp.key;
|
|
value = tmp.value;
|
|
distance_from_start_index = tmp.distance_from_start_index;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!entry.used) {
|
|
// adding an entry. otherwise overwriting old value with
|
|
// same key
|
|
hm.size += 1;
|
|
}
|
|
|
|
hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);
|
|
*entry = Entry {
|
|
.used = true,
|
|
.distance_from_start_index = distance_from_start_index,
|
|
.key = key,
|
|
.value = value,
|
|
};
|
|
return;
|
|
}
|
|
unreachable // put into a full map
|
|
}
|
|
|
|
fn internalGet(hm: &Self, key: K) -> ?&Entry {
|
|
const start_index = hm.keyToIndex(key);
|
|
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
|
|
const index = (start_index + roll_over) % hm.entries.len;
|
|
const entry = &hm.entries[index];
|
|
|
|
if (!entry.used) return null;
|
|
if (eql(entry.key, key)) return entry;
|
|
}}
|
|
return null;
|
|
}
|
|
|
|
fn keyToIndex(hm: &Self, key: K) -> usize {
|
|
return usize(hash(key)) % hm.entries.len;
|
|
}
|
|
}
|
|
}
|
|
|
|
test "basic hash map test" {
|
|
var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined;
|
|
map.init(&debug.global_allocator);
|
|
defer map.deinit();
|
|
|
|
%%map.put(1, 11);
|
|
%%map.put(2, 22);
|
|
%%map.put(3, 33);
|
|
%%map.put(4, 44);
|
|
%%map.put(5, 55);
|
|
|
|
assert((??map.get(2)).value == 22);
|
|
map.remove(2);
|
|
assert(if (const entry ?= map.get(2)) false else true);
|
|
}
|
|
|
|
fn hash_i32(x: i32) -> u32 {
|
|
*(&u32)(&x)
|
|
}
|
|
fn eql_i32(a: i32, b: i32) -> bool {
|
|
a == b
|
|
}</code></pre>
|
|
<h3 id="tetris">Tetris Clone</h3>
|
|
<img src="tetris-screenshot.png">
|
|
<p>
|
|
<a href="https://github.com/andrewrk/tetris">Source Code on GitHub</a>
|
|
</p>
|
|
<h3 id="clashos">Bare Bones Operating System</h3>
|
|
<p>
|
|
<a href="https://github.com/andrewrk/clashos">Source Code on GitHub</a>
|
|
</p>
|
|
<h3 id="cat">Cat Utility</h3>
|
|
<pre><code class="zig">const std = @import("std");
|
|
const io = std.io;
|
|
const mem = std.mem;
|
|
const os = std.os;
|
|
|
|
pub fn main() -> %void {
|
|
const exe = os.args.at(0);
|
|
var catted_anything = false;
|
|
var arg_i: usize = 1;
|
|
while (arg_i < os.args.count()) : (arg_i += 1) {
|
|
const arg = os.args.at(arg_i);
|
|
if (mem.eql(u8, arg, "-")) {
|
|
catted_anything = true;
|
|
%return cat_stream(&io.stdin);
|
|
} else if (arg[0] == '-') {
|
|
return usage(exe);
|
|
} else {
|
|
var is = io.InStream.open(arg, null) %% |err| {
|
|
%%io.stderr.printf("Unable to open file: {}\n", @errorName(err));
|
|
return err;
|
|
};
|
|
defer is.close();
|
|
|
|
catted_anything = true;
|
|
%return cat_stream(&is);
|
|
}
|
|
}
|
|
if (!catted_anything) {
|
|
%return cat_stream(&io.stdin);
|
|
}
|
|
%return io.stdout.flush();
|
|
}
|
|
|
|
fn usage(exe: []const u8) -> %void {
|
|
%%io.stderr.printf("Usage: {} [FILE]...\n", exe);
|
|
return error.Invalid;
|
|
}
|
|
|
|
fn cat_stream(is: &io.InStream) -> %void {
|
|
var buf: [1024 * 4]u8 = undefined;
|
|
|
|
while (true) {
|
|
const bytes_read = is.read(buf[0..]) %% |err| {
|
|
%%io.stderr.printf("Unable to read from stream: {}\n", @errorName(err));
|
|
return err;
|
|
};
|
|
|
|
if (bytes_read == 0) {
|
|
break;
|
|
}
|
|
|
|
io.stdout.write(buf[0..bytes_read]) %% |err| {
|
|
%%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err));
|
|
return err;
|
|
};
|
|
}
|
|
}</code></pre>
|
|
<h3 id="multiline-strings">Multiline String Syntax</h3>
|
|
<pre><code class="zig">pub fn createAllShaders() -> AllShaders {
|
|
var as : AllShaders = undefined;
|
|
|
|
as.primitive = createShader(
|
|
\\#version 150 core
|
|
\\
|
|
\\in vec3 VertexPosition;
|
|
\\
|
|
\\uniform mat4 MVP;
|
|
\\
|
|
\\void main(void) {
|
|
\\ gl_Position = vec4(VertexPosition, 1.0) * MVP;
|
|
\\}
|
|
,
|
|
\\#version 150 core
|
|
\\
|
|
\\out vec4 FragColor;
|
|
\\
|
|
\\uniform vec4 Color;
|
|
\\
|
|
\\void main(void) {
|
|
\\ FragColor = Color;
|
|
\\}
|
|
, null);
|
|
|
|
as.primitive_attrib_position = as.primitive.attrib_location(c"VertexPosition");
|
|
as.primitive_uniform_mvp = as.primitive.uniform_location(c"MVP");
|
|
as.primitive_uniform_color = as.primitive.uniform_location(c"Color");
|
|
|
|
|
|
|
|
as.texture = createShader(
|
|
\\#version 150 core
|
|
\\
|
|
\\in vec3 VertexPosition;
|
|
\\in vec2 TexCoord;
|
|
\\
|
|
\\out vec2 FragTexCoord;
|
|
\\
|
|
\\uniform mat4 MVP;
|
|
\\
|
|
\\void main(void)
|
|
\\{
|
|
\\ FragTexCoord = TexCoord;
|
|
\\ gl_Position = vec4(VertexPosition, 1.0) * MVP;
|
|
\\}
|
|
,
|
|
\\#version 150 core
|
|
\\
|
|
\\in vec2 FragTexCoord;
|
|
\\out vec4 FragColor;
|
|
\\
|
|
\\uniform sampler2D Tex;
|
|
\\
|
|
\\void main(void)
|
|
\\{
|
|
\\ FragColor = texture(Tex, FragTexCoord);
|
|
\\}
|
|
, null);
|
|
|
|
as.texture_attrib_tex_coord = as.texture.attrib_location(c"TexCoord");
|
|
as.texture_attrib_position = as.texture.attrib_location(c"VertexPosition");
|
|
as.texture_uniform_mvp = as.texture.uniform_location(c"MVP");
|
|
as.texture_uniform_tex = as.texture.uniform_location(c"Tex");
|
|
|
|
debug_gl.assert_no_error();
|
|
|
|
return as;
|
|
}</code></pre>
|
|
<h3 id="mersenne">Mersenne Twister Random Number Generator</h3>
|
|
<pre><code class="zig">const assert = @import("debug.zig").assert;
|
|
const rand_test = @import("rand_test.zig");
|
|
|
|
pub const MT19937_32 = MersenneTwister(
|
|
u32, 624, 397, 31,
|
|
0x9908B0DF,
|
|
11, 0xFFFFFFFF,
|
|
7, 0x9D2C5680,
|
|
15, 0xEFC60000,
|
|
18, 1812433253);
|
|
|
|
pub const MT19937_64 = MersenneTwister(
|
|
u64, 312, 156, 31,
|
|
0xB5026F5AA96619E9,
|
|
29, 0x5555555555555555,
|
|
17, 0x71D67FFFEDA60000,
|
|
37, 0xFFF7EEE000000000,
|
|
43, 6364136223846793005);
|
|
|
|
/// Use `init` to initialize this state.
|
|
pub const Rand = struct {
|
|
const Rng = if (@sizeOf(usize) >= 8) MT19937_64 else MT19937_32;
|
|
|
|
rng: Rng,
|
|
|
|
/// Initialize random state with the given seed.
|
|
pub fn init(r: &Rand, seed: usize) {
|
|
r.rng.init(seed);
|
|
}
|
|
|
|
/// Get an integer with random bits.
|
|
pub fn scalar(r: &Rand, comptime T: type) -> T {
|
|
if (T == usize) {
|
|
return r.rng.get();
|
|
} else {
|
|
var result: [@sizeOf(T)]u8 = undefined;
|
|
r.fillBytes(result);
|
|
return ([]T)(result)[0];
|
|
}
|
|
}
|
|
|
|
/// Fill `buf` with randomness.
|
|
pub fn fillBytes(r: &Rand, buf: []u8) {
|
|
var bytes_left = buf.len;
|
|
while (bytes_left >= @sizeOf(usize)) {
|
|
([]usize)(buf[buf.len - bytes_left...])[0] = r.rng.get();
|
|
bytes_left -= @sizeOf(usize);
|
|
}
|
|
if (bytes_left > 0) {
|
|
var rand_val_array : [@sizeOf(usize)]u8 = undefined;
|
|
([]usize)(rand_val_array)[0] = r.rng.get();
|
|
while (bytes_left > 0) {
|
|
buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left];
|
|
bytes_left -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get a random unsigned integer with even distribution between `start`
|
|
/// inclusive and `end` exclusive.
|
|
// TODO support signed integers and then rename to "range"
|
|
pub fn rangeUnsigned(r: &Rand, comptime T: type, start: T, end: T) -> T {
|
|
const range = end - start;
|
|
const leftover = @maxValue(T) % range;
|
|
const upper_bound = @maxValue(T) - leftover;
|
|
var rand_val_array : [@sizeOf(T)]u8 = undefined;
|
|
|
|
while (true) {
|
|
r.fillBytes(rand_val_array);
|
|
const rand_val = ([]T)(rand_val_array)[0];
|
|
if (rand_val < upper_bound) {
|
|
return start + (rand_val % range);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get a floating point value in the range 0.0..1.0.
|
|
pub fn float(r: &Rand, comptime T: type) -> T {
|
|
// TODO Implement this way instead:
|
|
// const int = @int_type(false, @sizeOf(T) * 8);
|
|
// const mask = ((1 << @float_mantissa_bit_count(T)) - 1);
|
|
// const rand_bits = r.rng.scalar(int) & mask;
|
|
// return @float_compose(T, false, 0, rand_bits) - 1.0
|
|
const int_type = @intType(false, @sizeOf(T) * 8);
|
|
const precision = if (T == f32) {
|
|
16777216
|
|
} else if (T == f64) {
|
|
9007199254740992
|
|
} else {
|
|
@compileError("unknown floating point type")
|
|
};
|
|
return T(r.rangeUnsigned(int_type, 0, precision)) / T(precision);
|
|
}
|
|
};
|
|
|
|
fn MersenneTwister(
|
|
comptime int: type, comptime n: usize, comptime m: usize, comptime r: int,
|
|
comptime a: int,
|
|
comptime u: int, comptime d: int,
|
|
comptime s: int, comptime b: int,
|
|
comptime t: int, comptime c: int,
|
|
comptime l: int, comptime f: int) -> type
|
|
{
|
|
struct {
|
|
const Self = this;
|
|
|
|
array: [n]int,
|
|
index: usize,
|
|
|
|
pub fn init(mt: &Self, seed: int) {
|
|
mt.index = n;
|
|
|
|
var prev_value = seed;
|
|
mt.array[0] = prev_value;
|
|
{var i: usize = 1; while (i < n) : (i += 1) {
|
|
prev_value = int(i) +% f *% (prev_value ^ (prev_value >> (int.bit_count - 2)));
|
|
mt.array[i] = prev_value;
|
|
}};
|
|
}
|
|
|
|
pub fn get(mt: &Self) -> int {
|
|
const mag01 = []int{0, a};
|
|
const LM: int = (1 << r) - 1;
|
|
const UM = ~LM;
|
|
|
|
if (mt.index >= n) {
|
|
var i: usize = 0;
|
|
|
|
while (i < n - m) : (i += 1) {
|
|
const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
|
|
mt.array[i] = mt.array[i + m] ^ (x >> 1) ^ mag01[x & 0x1];
|
|
}
|
|
|
|
while (i < n - 1) : (i += 1) {
|
|
const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
|
|
mt.array[i] = mt.array[i + m - n] ^ (x >> 1) ^ mag01[x & 0x1];
|
|
|
|
}
|
|
const x = (mt.array[i] & UM) | (mt.array[0] & LM);
|
|
mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[x & 0x1];
|
|
|
|
mt.index = 0;
|
|
}
|
|
|
|
var x = mt.array[mt.index];
|
|
mt.index += 1;
|
|
|
|
x ^= ((x >> u) & d);
|
|
x ^= ((x <<% s) & b);
|
|
x ^= ((x <<% t) & c);
|
|
x ^= (x >> l);
|
|
|
|
return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
test "float 32" {
|
|
var r: Rand = undefined;
|
|
r.init(42);
|
|
|
|
{var i: usize = 0; while (i < 1000) : (i += 1) {
|
|
const val = r.float(f32);
|
|
assert(val >= 0.0);
|
|
assert(val < 1.0);
|
|
}}
|
|
}
|
|
|
|
test "MT19937_64" {
|
|
var rng: MT19937_64 = undefined;
|
|
rng.init(rand_test.mt64_seed);
|
|
for (rand_test.mt64_data) |value| {
|
|
assert(value == rng.get());
|
|
}
|
|
}
|
|
|
|
test "MT19937_32" {
|
|
var rng: MT19937_32 = undefined;
|
|
rng.init(rand_test.mt32_seed);
|
|
for (rand_test.mt32_data) |value| {
|
|
assert(value == rng.get());
|
|
}
|
|
}</code></pre>
|
|
<script src="highlight/highlight.pack.js"></script>
|
|
<script>hljs.initHighlightingOnLoad();</script>
|
|
</body>
|
|
</html>
|