Last active
September 25, 2017 08:14
-
-
Save vassvik/0a08ce59b678a77579db9392df983306 to your computer and use it in GitHub Desktop.
Odin vector types and bulk operations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Vectors are similar to arrays, but they are allow for bulk operations to all of its elements. | |
// Thet are meant to map to hardware level vector instructions but they can be any arbitrary length. | |
// Vector types only allow numeric and logical types (integers, floats, complex numbers, booleans). | |
// A vector type is composed as: `[vector size]T`. | |
// Almost all operators that work on the base type also work on the vector type. | |
// Exceptions are comparisons for bool vectors, shifts are for integer vectors. | |
fmt.println("complex operations"); | |
c1 := [vector 2]complex64{1.0 + 3.14i, 3.0 - 5.0i}; | |
c2 := [vector 2]complex64{-1.0 + 1i, -13 - 3.5i}; | |
fmt.printf("c1 = %v\n", c1); | |
fmt.printf("c2 = %v\n", c2); | |
fmt.printf("c1 + c2 = %v\n", c1 + c2); | |
fmt.printf("c1 - c2 = %v\n", c1 - c2); | |
fmt.printf("c1 * c2 = %v\n", c1 * c2); | |
fmt.printf("c1 / c2 = %v\n", c1 / c2); | |
fmt.println(); | |
fmt.println("float operations"); | |
v1 := [vector 2]f32{1.0, 2.0}; | |
v2 := [vector 2]f32{3.0, 5.0}; | |
fmt.printf("v1 = %v\n", v1); | |
fmt.printf("v2 = %v\n", v2); | |
fmt.printf("v1 + v2 = %v\n", v1 + v2); | |
fmt.printf("v1 - v2 = %v\n", v1 - v2); | |
fmt.printf("v1 * v2 = %v\n", v1 * v2); | |
fmt.printf("v1 / v2 = %v\n", v1 / v2); | |
fmt.println(); | |
fmt.println("int operations"); | |
i1 := [vector 3]int{7, -13, 17}; | |
i2 := [vector 3]int{3, 5, -7}; | |
fmt.printf("i1 = %v\n", i1); | |
fmt.printf("i2 = %v\n", i2); | |
fmt.printf("i1 + i2 = %v\n", i1 + i2); | |
fmt.printf("i1 - i2 = %v\n", i1 - i2); | |
fmt.printf("i1 * i2 = %v\n", i1 * i2); | |
fmt.printf("i1 / i2 = %v\n", i1 / i2); | |
fmt.printf("i1 %% i2 = %v\n", i1 % i2); | |
fmt.printf("i1 %%%% i2 = %v\n", i1 %% i2); | |
fmt.printf("i1 & i2 = %v\n", i1 & i2); | |
fmt.printf("i1 | i2 = %v\n", i1 | i2); | |
fmt.printf("i1 ~ i2 = %v\n", i1 ~ i2); // XOR | |
fmt.printf("~i1 = %v\n", ~i1); | |
fmt.printf("~i2 = %v\n", ~i2); | |
fmt.println(); | |
fmt.println("bool operations"); | |
b1 := [vector 4]bool{false, false, true, true}; | |
b2 := [vector 4]bool{false, true, false, true}; | |
fmt.printf("b1 = %v\n", b1); | |
fmt.printf("b2 = %v\n", b2); | |
fmt.printf("b1 == b2 = %v\n", b1 == b2); | |
fmt.printf("b1 != b2 = %v\n", b1 != b2); | |
fmt.printf("b1 & b2 = %v\n", b1 & b2); | |
fmt.printf("b1 | b2 = %v\n", b1 | b2); | |
fmt.printf("b1 ~ b2 = %v\n", b1 ~ b2); // XOR | |
fmt.printf("!b1 = %v\n", !b1); | |
fmt.printf("!b2 = %v\n", !b2); | |
fmt.printf("~b1 = %v\n", ~b1); | |
fmt.printf("~b2 = %v\n", ~b2); | |
// fmt.printf("b1 && b2 = %v\n", b1 && b2); // Currently bugged | |
// fmt.printf("b1 || b2 = %v\n", b1 || b2); // Currently bugged | |
fmt.println(); | |
// Note that while the vectors are typically initialized with the struct initialization syntax, | |
// there is also a shorthand to broadcast a single value to all elements | |
fmt.println([vector 2]f32{1.0}); | |
fmt.println([vector 10]f32{0.0}); | |
// if vector types are smaller than 16 bytes they will round up to the nearest power-of-two that is less than or equal to 16 bytes | |
// otherwise they will round up to the closest multiple of 16 bytes (to fit in 128 bit SIMD registers) | |
fmt.printf("size_of([vector 1]f32) = %d\n", size_of([vector 1]f32)); | |
fmt.printf("size_of([vector 2]f32) = %d\n", size_of([vector 2]f32)); | |
fmt.printf("size_of([vector 3]f32) = %d\n", size_of([vector 3]f32)); | |
fmt.printf("size_of([vector 4]f32) = %d\n", size_of([vector 4]f32)); | |
fmt.printf("size_of([vector 5]f32) = %d\n", size_of([vector 5]f32)); | |
fmt.printf("size_of([vector 8]f32) = %d\n", size_of([vector 8]f32)); | |
fmt.printf("size_of([vector 9]f32) = %d\n", size_of([vector 9]f32)); | |
fmt.printf("size_of([vector 12]f32) = %d\n", size_of([vector 12]f32)); | |
fmt.printf("size_of([vector 13]f32) = %d\n", size_of([vector 13]f32)); | |
fmt.printf("size_of([vector 16]f32) = %d\n", size_of([vector 16]f32)); | |
fmt.printf("size_of([vector 17]f32) = %d\n", size_of([vector 17]f32)); | |
fmt.printf("size_of([vector 20]f32) = %d\n", size_of([vector 20]f32)); | |
fmt.printf("size_of([vector 21]f32) = %d\n", size_of([vector 21]f32)); | |
fmt.printf("size_of([vector 24]f32) = %d\n", size_of([vector 24]f32)); | |
fmt.printf("size_of([vector 25]f32) = %d\n", size_of([vector 25]f32)); | |
fmt.printf("size_of([vector 28]f32) = %d\n", size_of([vector 28]f32)); | |
fmt.printf("size_of([vector 29]f32) = %d\n", size_of([vector 29]f32)); | |
fmt.printf("size_of([vector 32]f32) = %d\n", size_of([vector 32]f32)); | |
fmt.println(); | |
fmt.printf("size_of([vector 1]bool) = %d\n", size_of([vector 1]bool)); | |
fmt.printf("size_of([vector 2]bool) = %d\n", size_of([vector 2]bool)); | |
fmt.printf("size_of([vector 3]bool) = %d\n", size_of([vector 3]bool)); | |
fmt.printf("size_of([vector 4]bool) = %d\n", size_of([vector 4]bool)); | |
fmt.printf("size_of([vector 5]bool) = %d\n", size_of([vector 5]bool)); | |
fmt.printf("size_of([vector 8]bool) = %d\n", size_of([vector 8]bool)); | |
fmt.printf("size_of([vector 9]bool) = %d\n", size_of([vector 9]bool)); | |
fmt.printf("size_of([vector 16]bool) = %d\n", size_of([vector 16]bool)); | |
fmt.printf("size_of([vector 17]bool) = %d\n", size_of([vector 17]bool)); | |
fmt.printf("size_of([vector 32]bool) = %d\n", size_of([vector 32]bool)); | |
fmt.printf("size_of([vector 33]bool) = %d\n", size_of([vector 33]bool)); | |
fmt.printf("size_of([vector 48]bool) = %d\n", size_of([vector 48]bool)); | |
fmt.printf("size_of([vector 49]bool) = %d\n", size_of([vector 49]bool)); | |
fmt.printf("size_of([vector 64]bool) = %d\n", size_of([vector 64]bool)); | |
fmt.printf("size_of([vector 65]bool) = %d\n", size_of([vector 65]bool)); | |
fmt.printf("size_of([vector 80]bool) = %d\n", size_of([vector 80]bool)); | |
fmt.printf("size_of([vector 81]bool) = %d\n", size_of([vector 81]bool)); | |
fmt.printf("size_of([vector 96]bool) = %d\n", size_of([vector 96]bool)); | |
fmt.printf("size_of([vector 97]bool) = %d\n", size_of([vector 97]bool)); | |
fmt.printf("size_of([vector 112]bool) = %d\n", size_of([vector 112]bool)); | |
fmt.printf("size_of([vector 113]bool) = %d\n", size_of([vector 113]bool)); | |
fmt.printf("size_of([vector 128]bool) = %d\n", size_of([vector 128]bool)); | |
fmt.println(); | |
// the individual components of vectors whose length are smaller than or equal to 4 | |
// can be accessed by x, y, z and w, respectively: | |
v4 := [vector 4]f32{1.0, 2.0, 3.0, 4.0}; | |
fmt.printf("v4.x, v4.y, v4.z, v4.w = %v %v %v %v\n", v4.x, v4.y, v4.z, v4.w); | |
fmt.println(); | |
// you can additionally index vectors using the regular [] syntax. | |
// these are bounds checked, unless explicitly deactivated with #no_bounds_check | |
v10 := [vector 10]f32{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; | |
fmt.printf("v10[2], v10[7], v10[9] = %v %v %v\n", v10[2], v10[7], v10[9]); | |
fmt.println(); | |
// while you cannot explicitly take address the address of an element in a vector, you can use the following trick: | |
Thing :: struct { | |
using v: [vector 4]f32, // this makes the Vec4 type "array-like" such that you can index it with []. | |
}; | |
v := Thing{[vector 4]f32{1.0, 2.0, 3.0, 4.0}}; // you cannot initialize anonymous members at the moment | |
fmt.printf("v.x, v.y, v.z, v.w = %v %v %v %v\n", v.x, v.y, v.z, v.w); | |
fmt.printf("v[0], v[1], v[2], v[3] = %v %v %v %v\n", v[0], v[1], v[2], v[3]); | |
fmt.printf("&v[0], &v[1], &v[2], &v[3] = %p %p %p %p\n", &v[0], &v[1], &v[2], &v[3]); | |
fmt.println(); | |
// you can swizzle vectors by using the built in `swizzle` procedure | |
fmt.printf("swizzle(v4, 3, 1, 2) = %v, type %T]\n", swizzle(v4, 3, 1, 2), swizzle(v4, 3, 1, 2)); | |
fmt.printf("swizzle(v4, 3, 1) = %v, type %T]\n", swizzle(v4, 3, 1), swizzle(v4, 3, 1)); | |
fmt.printf("swizzle(v4, 0, 0, 0, 0) = %v, type %T]\n", swizzle(v4, 0, 0, 0, 0), swizzle(v4, 0, 0, 0, 0)); | |
fmt.println(); | |
// it is often useful to create an alias for the type to make the initialization syntax cleaner | |
Vec4 :: [vector 4]f32; | |
a := Vec4{4.0, 3.0, 2.0, 1.0}; | |
b := Vec4{1.0, 2.0, 3.0, 4.0}; | |
fmt.printf("a + b = %v\n", a + b); | |
fmt.println(); | |
// note that an aliased type is a distinct type and is distinct from the base type, so you cannot mix and match them. | |
// on the other hand, you can create *specialized* polymorphic procedures that work on any type that is derived from the same vector type | |
do_stuff :: proc(v: $T/[vector 4]f32) { | |
fmt.printf("v = %v\n", v); | |
} | |
do_stuff(a); | |
do_stuff(v4); | |
fmt.println(); | |
// you can also cast between them: | |
c := cast(Vec4)v4; | |
fmt.printf("c = %v\n", c); | |
fmt.println(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment