Vectors

A Vector is a group of numbers, booleans, or pointers that are operated on in parallel. They’re implemented with SIMD instructions where possible.

Floating-point vectors

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    // defining vectors
    const a: @Vector(4, f64) = .{ 1, -1, 2, -2 };
    print("a = {}\n", .{a});
    const b: @Vector(4, f64) = @splat(-2);
    print("b = {}\n", .{b});
    print("\n", .{});

    // accessing elements
    print("a[0] = {}\n", .{a[0]});
    print("a[1] = {}\n", .{a[1]});
    print("a[2] = {}\n", .{a[2]});
    print("a[3] = {}\n", .{a[3]});
    print("\n", .{});

    // element-wise arithmetic with vectors
    print("a+b = {}\n", .{a + b});
    print("a-b = {}\n", .{a - b});
    print("a*b = {}\n", .{a * b});
    print("a/b = {}\n", .{a / b});
    print("\n", .{});

    // using builtin functions
    const pi = std.math.pi;
    const c: @Vector(4, f64) = .{
        0, 0.5 * pi, pi, 1.5 * pi,
    };
    print("c = {d:.2}\n", .{c});
    print("sin(c) = {d:.2}\n", .{@sin(c)});
    print("cos(c) = {d:.2}\n", .{@cos(c)});
}
$ zig run vectors.zig
a = { 1, -1, 2, -2 }
b = { -2, -2, -2, -2 }

a[0] = 1
a[1] = -1
a[2] = 2
a[3] = -2

a+b = { -1, -3, 0, -4 }
a-b = { 3, 1, 4, 0 }
a*b = { -2, 2, -4, 4 }
a/b = { -0.5, 0.5, -1, 1 }

c = { 0.00, 1.57, 3.14, 4.71 }
sin(c) = { 0.00, 1.00, 0.00, -1.00 }
cos(c) = { 1.00, 0.00, -1.00, -0.00 }

Integer vectors

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    var a: @Vector(3, i64) = .{ 1, 2, 3 };
    print("a = {}\n", .{a});
    const twos: @Vector(3, i64) = @splat(2);

    // += and friends also work with vectors
    print("plus 2:\n", .{});
    a += twos;
    print("a = {}\n", .{a});

    // so do the builtin integer division functions
    print("divided by 2, rounding down:\n", .{});
    a = @divFloor(a, twos);
    print("a = {}\n", .{a});
    print("\n", .{});

    // @reduce combines the elements of a vector
    print("sum of a: {}\n", .{@reduce(.Add, a)});
    print("product of a: {}\n", .{@reduce(.Mul, a)});
    print("minimum of a: {}\n", .{@reduce(.Min, a)});
    print("maximum of a: {}\n", .{@reduce(.Max, a)});
}
$ zig run vectors-2.zig
a = { 1, 2, 3 }
plus 2:
a = { 3, 4, 5 }
divided by 2, rounding down:
a = { 1, 2, 2 }

sum of a: 5
product of a: 4
minimum of a: 1
maximum of a: 2

Boolean vectors

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    // initialize a boolean vector
    const a: @Vector(3, bool) = .{ true, false, true };
    print("a = {}\n", .{a});

    // create a boolean vector by comparing two vectors
    const b: @Vector(3, bool) =
        @Vector(3, f64){ 1, 2, -1 } >
        @Vector(3, f64){ 0, 0, 0 };
    print("b = {}\n", .{b});
    print("\n", .{});

    // basic boolean operations
    print("not a: {}\n", .{!a});
    print("a and b: {}\n", .{a & b});
    print("a or b: {}\n", .{a | b});
    print("\n", .{});

    // use @reduce with booleans
    print("all true in a? {}\n", .{@reduce(.And, a)});
    print("any true in a? {}\n", .{@reduce(.Or, a)});
}
$ zig run vectors-3.zig
a = { true, false, true }
b = { true, true, false }

not a: { false, true, false }
a and b: { true, false, false }
a or b: { true, true, true }

all true in a? false
any true in a? true

Converting to and from arrays

const std = @import("std");
const expectEqual = std.testing.expectEqual;

test "convert an array to a vector" {
    const array = [_]i64{ 1, 2, 3, 4 };
    const v1: @Vector(4, i64) = array;
    try expectEqual(@Vector(4, i64){ 1, 2, 3, 4 }, v1);
}

test "convert a slice to a vector" {
    const slice: []const i64 = &.{ 1, 2, 3, 4 };
    const v2: @Vector(4, i64) = slice.*;
    try expectEqual(@Vector(4, i64){ 1, 2, 3, 4 }, v2);
}

test "convert a vector to an array" {
    const vector: @Vector(4, i64) = .{ 1, 2, 3, 4 };
    const a: [4]i64 = vector;
    try expectEqual([_]i64{ 1, 2, 3, 4 }, a);
}
$ zig test vectors-4.zig
1/3 vectors-4.test.convert an array to a vector...OK
2/3 vectors-4.test.convert a slice to a vector...OK
3/3 vectors-4.test.convert a vector to an array...OK
All 3 tests passed.

Select and shuffle

@select selects elements from two vectors based on a predicate vector, where true selects an element from the first vector, false from the second:

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    const a: @Vector(4, f64) = .{ 1, 2, 3, 4 };
    const b: @Vector(4, f64) = .{ 10, 20, 30, 40 };
    const pred = [_]bool{ true, false, true, false };
    const c = @select(f64, pred, a, b);
    print("c = {}\n", .{c});
}
$ zig run vectors-5.zig
c = { 1, 20, 3, 40 }

@shuffle selects elements from two vectors based on an integer vector called a “mask”. Positive mask elements select from the first vector, negative ones from the second:

const std = @import("std");
const print = std.debug.print;

pub fn main() void {
    const a: @Vector(4, f64) = .{ 1, 2, 3, 4 };
    const b: @Vector(4, f64) = .{ 10, 20, 30, 40 };
    const mask = [_]i64{ 0, -1, 1, -2, 2, -3, 3, -4 };
    const c = @shuffle(f64, a, b, mask);
    print("c = {}\n", .{c});
}
$ zig run vectors-6.zig
c = { 1, 10, 2, 20, 3, 30, 4, 40 }

This is the last example for now.