Browse Source

Merge branch 'master' into odin-global-constants-as-enums

gingerBill 3 years ago
parent
commit
f5697dd7f2
100 changed files with 3765 additions and 1366 deletions
  1. 22 10
      .github/workflows/ci.yml
  2. 1 1
      .github/workflows/nightly.yml
  3. 3 0
      .gitignore
  4. 22 10
      Makefile
  5. 1 1
      core/builtin/builtin.odin
  6. 11 35
      core/bytes/bytes.odin
  7. 1 1
      core/c/libc/stdio.odin
  8. 2 1
      core/compress/gzip/gzip.odin
  9. 13 10
      core/compress/zlib/zlib.odin
  10. 112 2
      core/container/bit_array/bit_array.odin
  11. 192 0
      core/container/lru/lru_cache.odin
  12. 1 0
      core/container/queue/queue.odin
  13. 98 0
      core/container/topological_sort/topological_sort.odin
  14. 4 4
      core/crypto/blake/blake.odin
  15. 1 1
      core/crypto/blake2b/blake2b.odin
  16. 1 1
      core/crypto/blake2s/blake2s.odin
  17. 1 1
      core/crypto/gost/gost.odin
  18. 4 4
      core/crypto/groestl/groestl.odin
  19. 15 15
      core/crypto/haval/haval.odin
  20. 4 4
      core/crypto/jh/jh.odin
  21. 4 4
      core/crypto/keccak/keccak.odin
  22. 1 1
      core/crypto/md2/md2.odin
  23. 1 1
      core/crypto/md4/md4.odin
  24. 1 1
      core/crypto/md5/md5.odin
  25. 4 4
      core/crypto/ripemd/ripemd.odin
  26. 1 1
      core/crypto/sha1/sha1.odin
  27. 4 4
      core/crypto/sha2/sha2.odin
  28. 4 4
      core/crypto/sha3/sha3.odin
  29. 2 2
      core/crypto/shake/shake.odin
  30. 1 1
      core/crypto/sm3/sm3.odin
  31. 2 2
      core/crypto/streebog/streebog.odin
  32. 3 3
      core/crypto/tiger/tiger.odin
  33. 3 3
      core/crypto/tiger2/tiger2.odin
  34. 1 1
      core/crypto/whirlpool/whirlpool.odin
  35. 27 6
      core/fmt/fmt.odin
  36. 6 1
      core/fmt/fmt_js.odin
  37. 9 1
      core/fmt/fmt_os.odin
  38. 39 2
      core/io/io.odin
  39. 2 1
      core/math/big/common.odin
  40. 19 0
      core/math/linalg/glsl/linalg_glsl.odin
  41. 2 0
      core/math/linalg/glsl/linalg_glsl_math.odin
  42. 80 0
      core/math/linalg/hlsl/linalg_hlsl.odin
  43. 5 0
      core/math/linalg/hlsl/linalg_hlsl_math.odin
  44. 734 0
      core/math/noise/internal.odin
  45. 171 0
      core/math/noise/opensimplex2.odin
  46. 214 0
      core/math/rand/exp.odin
  47. 6 8
      core/math/rand/normal.odin
  48. 8 8
      core/math/rand/rand.odin
  49. 34 0
      core/mem/doc.odin
  50. 1 0
      core/odin/ast/ast.odin
  51. 12 2
      core/odin/doc-format/doc_format.odin
  52. 19 1
      core/odin/parser/parser.odin
  53. 10 2
      core/os/os.odin
  54. 82 5
      core/os/os_darwin.odin
  55. 2 0
      core/os/os_freebsd.odin
  56. 2 0
      core/os/os_linux.odin
  57. 1 2
      core/path/filepath/match.odin
  58. 1 1
      core/path/filepath/walk.odin
  59. 1 1
      core/reflect/reflect.odin
  60. 3 0
      core/reflect/types.odin
  61. 1 0
      core/runtime/core.odin
  62. 4 0
      core/runtime/core_builtin.odin
  63. 2 4
      core/runtime/internal.odin
  64. 18 5
      core/runtime/print.odin
  65. 12 12
      core/slice/slice.odin
  66. 31 488
      core/slice/sort.odin
  67. 200 0
      core/slice/sort_private.odin
  68. 65 30
      core/strings/strings.odin
  69. 1 1
      core/testing/runner_other.odin
  70. 0 2
      core/thread/thread_unix.odin
  71. 82 4
      examples/all/all_main.odin
  72. 33 12
      examples/all/all_vendor.odin
  73. 8 8
      examples/demo/demo.odin
  74. 52 1
      src/build_settings.cpp
  75. 39 4
      src/check_builtin.cpp
  76. 89 2
      src/check_decl.cpp
  77. 483 378
      src/check_expr.cpp
  78. 15 93
      src/check_stmt.cpp
  79. 29 17
      src/check_type.cpp
  80. 134 24
      src/checker.cpp
  81. 0 4
      src/checker_builtin_procs.hpp
  82. 15 5
      src/docs_format.cpp
  83. 41 21
      src/docs_writer.cpp
  84. 5 1
      src/entity.cpp
  85. 5 0
      src/error.cpp
  86. 1 0
      src/llvm_backend.hpp
  87. 4 4
      src/llvm_backend_const.cpp
  88. 71 0
      src/llvm_backend_debug.cpp
  89. 75 33
      src/llvm_backend_expr.cpp
  90. 6 1
      src/llvm_backend_general.cpp
  91. 6 3
      src/llvm_backend_proc.cpp
  92. 7 0
      src/llvm_backend_stmt.cpp
  93. 8 7
      src/llvm_backend_type.cpp
  94. 26 15
      src/llvm_backend_utility.cpp
  95. 2 1
      src/main.cpp
  96. 123 7
      src/parser.cpp
  97. 16 3
      src/parser.hpp
  98. 6 0
      src/parser_pos.cpp
  99. 9 0
      src/ptr_set.cpp
  100. 10 2
      src/string.cpp

+ 22 - 10
.github/workflows/ci.yml

@@ -17,13 +17,16 @@ jobs:
         run: ./odin report
         timeout-minutes: 1
       - name: Odin check
-        run: ./odin check examples/demo/demo.odin -vet
+        run: ./odin check examples/demo -vet
         timeout-minutes: 10
       - name: Odin run
-        run: ./odin run examples/demo/demo.odin
+        run: ./odin run examples/demo
         timeout-minutes: 10
       - name: Odin run -debug
-        run: ./odin run examples/demo/demo.odin -debug
+        run: ./odin run examples/demo -debug
+        timeout-minutes: 10
+      - name: Odin check examples/all
+        run: ./odin check examples/all -strict-style
         timeout-minutes: 10
       - name: Core library tests
         run: |
@@ -54,13 +57,16 @@ jobs:
         run: ./odin report
         timeout-minutes: 1
       - name: Odin check
-        run: ./odin check examples/demo/demo.odin -vet
+        run: ./odin check examples/demo -vet
         timeout-minutes: 10
       - name: Odin run
-        run: ./odin run examples/demo/demo.odin
+        run: ./odin run examples/demo
         timeout-minutes: 10
       - name: Odin run -debug
-        run: ./odin run examples/demo/demo.odin -debug
+        run: ./odin run examples/demo -debug
+        timeout-minutes: 10
+      - name: Odin check examples/all
+        run: ./odin check examples/all -strict-style
         timeout-minutes: 10
       - name: Core library tests
         run: |
@@ -73,7 +79,7 @@ jobs:
           make
         timeout-minutes: 10
   build_windows:
-    runs-on: windows-latest
+    runs-on: windows-2019
     steps:
       - uses: actions/checkout@v1
       - name: build Odin
@@ -91,19 +97,25 @@ jobs:
         shell: cmd
         run: |
           call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin check examples/demo/demo.odin -vet
+          odin check examples/demo -vet
         timeout-minutes: 10
       - name: Odin run
         shell: cmd
         run: |
           call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin run examples/demo/demo.odin
+          odin run examples/demo
         timeout-minutes: 10
       - name: Odin run -debug
         shell: cmd
         run: |
           call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin run examples/demo/demo.odin -debug
+          odin run examples/demo -debug
+        timeout-minutes: 10
+      - name: Odin check examples/all
+        shell: cmd
+        run: |
+          call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+          odin check examples/all -strict-style
         timeout-minutes: 10
       - name: Core library tests
         shell: cmd

+ 1 - 1
.github/workflows/nightly.yml

@@ -7,7 +7,7 @@ on:
 
 jobs:
   build_windows:
-    runs-on: windows-latest
+    runs-on: windows-2019
     steps:
       - uses: actions/checkout@v1
       - name: build Odin

+ 3 - 0
.gitignore

@@ -7,6 +7,9 @@
 # User-specific files (MonoDevelop/Xamarin Studio)
 *.userprefs
 
+# For macOS
+.DS_Store
+
 # Build results
 [Dd]ebug/
 [Dd]ebugPublic/

+ 22 - 10
Makefile

@@ -8,22 +8,34 @@ CC=clang
 OS=$(shell uname)
 
 ifeq ($(OS), Darwin)
+    
     ARCH=$(shell uname -m)
-    LLVM_CONFIG=llvm-config
+    LLVM_CONFIG=
 
-    # LLVM Version Setting  
-    LLVM_VERSION_PATTERN="^11\."
-    LLVM_VERSION="11"
+    # allow for arm only llvm's with version 13
     ifeq ($(ARCH), arm64)
-	LLVM_VERSION="13"
-        LLVM_VERSION_PATTERN="^13"
-    endif 
+        LLVM_VERSIONS = "13.%.%"
+    else
+    # allow for x86 / amd64 all llvm versions begining from 11
+        LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0"
+    endif
 
-    ifneq ($(shell llvm-config --version | grep $(LLVM_VERSION_PATTERN)),)
+    LLVM_VERSION_PATTERN_SEPERATOR = )|(
+    LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS))
+    LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT))
+    LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT))
+    LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
+    LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
+
+    ifneq ($(shell llvm-config --version | grep -E $(LLVM_VERSION_PATTERN)),)
         LLVM_CONFIG=llvm-config
     else
-        $(error "Requirement: llvm-config must be version $(LLVM_VERSION)")
-    endif
+        ifeq ($(ARCH), arm64)
+            $(error "Requirement: llvm-config must be base version 13 for arm64")
+        else 
+            $(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86")
+        endif 
+    endif 
 
     LDFLAGS:=$(LDFLAGS) -liconv
     CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)

+ 1 - 1
core/builtin/builtin.odin

@@ -2,7 +2,7 @@
 package builtin
 
 nil   :: nil;
-false :: 0!==0;
+false :: 0!=0;
 true  :: 0==0;
 
 ODIN_OS      :: ODIN_OS;

+ 11 - 35
core/bytes/bytes.odin

@@ -218,61 +218,37 @@ split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) ->
 
 
 @private
-_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save, n: int) -> (res: []byte, ok: bool) {
-	s, n := s, n
-
-	if n == 0 {
-		return
-	}
-
-	if sep == nil {
+_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save: int) -> (res: []byte, ok: bool) {
+	if len(sep) == 0 {
 		res = s[:]
 		ok = true
 		s^ = s[len(s):]
 		return
 	}
 
-	if n < 0 {
-		n = count(s^, sep) + 1
-	}
-
-	n -= 1
-
-	i := 0
-	for ; i < n; i += 1 {
-		m := index(s^, sep)
-		if m < 0 {
-			break
-		}
+	m := index(s^, sep)
+	if m < 0 {
+		// not found
+		res = s[:]
+		ok = len(res) != 0
+		s^ = s[len(s):]
+	} else {
 		res = s[:m+sep_save]
 		ok = true
 		s^ = s[m+len(sep):]
-		return
 	}
-	res = s[:]
-	ok = res != nil
-	s^ = s[len(s):]
 	return
 }
 
 
 split_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
-	return _split_iterator(s, sep, 0, -1)
-}
-
-split_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
-	return _split_iterator(s, sep, 0, n)
+	return _split_iterator(s, sep, 0)
 }
 
 split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
-	return _split_iterator(s, sep, len(sep), -1)
+	return _split_iterator(s, sep, len(sep))
 }
 
-split_after_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
-	return _split_iterator(s, sep, len(sep), n)
-}
-
-
 
 index_byte :: proc(s: []byte, c: byte) -> int {
 	for i := 0; i < len(s); i += 1 {

+ 1 - 1
core/c/libc/stdio.odin

@@ -149,7 +149,7 @@ foreign libc {
 	putchar   :: proc() -> int ---
 	puts      :: proc(s: cstring) -> int ---
 	ungetc    :: proc(c: int, stream: ^FILE) -> int ---
-	fread     :: proc(ptr: rawptr, size: size_t, stream: ^FILE) -> size_t ---
+	fread     :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
 	fwrite    :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
 
 	// 7.21.9 File positioning functions

+ 2 - 1
core/compress/gzip/gzip.odin

@@ -66,7 +66,8 @@ OS :: enum u8 {
 	_Unknown     = 14,
 	Unknown      = 255,
 }
-OS_Name :: #partial [OS]string{
+OS_Name :: #sparse[OS]string{
+	._Unknown     = "",
 	.FAT          = "FAT",
 	.Amiga        = "Amiga",
 	.VMS          = "VMS/OpenVMS",

+ 13 - 10
core/compress/zlib/zlib.odin

@@ -111,9 +111,9 @@ ZFAST_MASK :: ((1 << ZFAST_BITS) - 1)
 */
 Huffman_Table :: struct {
 	fast:        [1 << ZFAST_BITS]u16,
-	firstcode:   [16]u16,
+	firstcode:   [17]u16,
 	maxcode:     [17]int,
-	firstsymbol: [16]u16,
+	firstsymbol: [17]u16,
 	size:        [288]u8,
 	value:       [288]u16,
 }
@@ -244,7 +244,7 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
 @(optimization_mode="speed")
 build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
 	sizes:     [HUFFMAN_MAX_BITS+1]int
-	next_code: [HUFFMAN_MAX_BITS]int
+	next_code: [HUFFMAN_MAX_BITS+1]int
 
 	k := int(0)
 
@@ -256,14 +256,14 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
 	}
 	sizes[0] = 0
 
-	for i in 1..<(HUFFMAN_MAX_BITS+1) {
+	for i in 1 ..< HUFFMAN_MAX_BITS {
 		if sizes[i] > (1 << uint(i)) {
 			return E_Deflate.Huffman_Bad_Sizes
 		}
 	}
 	code := int(0)
 
-	for i in 1..<HUFFMAN_MAX_BITS {
+	for i in 1 ..= HUFFMAN_MAX_BITS {
 		next_code[i]     = code
 		z.firstcode[i]   = u16(code)
 		z.firstsymbol[i] = u16(k)
@@ -538,19 +538,20 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
 		final = compress.read_bits_lsb(z, 1)
 		type  = compress.read_bits_lsb(z, 2)
 
-		// fmt.printf("Final: %v | Type: %v\n", final, type);
+		// fmt.printf("Final: %v | Type: %v\n", final, type)
 
 		switch type {
 		case 0:
+			// fmt.printf("Method 0: STORED\n")
 			// Uncompressed block
 
 			// Discard bits until next byte boundary
 			compress.discard_to_next_byte_lsb(z)
 
-			uncompressed_len := i16(compress.read_bits_lsb(z, 16))
-			length_check     := i16(compress.read_bits_lsb(z, 16))
+			uncompressed_len := u16(compress.read_bits_lsb(z, 16))
+			length_check     := u16(compress.read_bits_lsb(z, 16))
 
-			// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
+			// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check)
 
 
 			if ~uncompressed_len != length_check {
@@ -567,10 +568,12 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
 				write_byte(z, u8(lit))
 				uncompressed_len -= 1
 			}
+			assert(uncompressed_len == 0)
+
 		case 3:
 			return E_Deflate.BType_3
 		case:
-			// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
+			// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
 			if type == 1 {
 				// Use fixed code lengths.
 				build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return

+ 112 - 2
core/container/bit_array/bit_array.odin

@@ -11,11 +11,119 @@ INDEX_SHIFT :: 6
 @(private="file")
 INDEX_MASK  :: 63
 
+@(private="file")
+NUM_BITS :: 64
+
 Bit_Array :: struct {
 	bits: [dynamic]u64,
 	bias: int,
+	max_index: int,
+}
+
+Bit_Array_Iterator :: struct {
+	array: ^Bit_Array,
+	word_idx: int,
+	bit_idx: uint,
+}
+
+/*
+	In:
+		- ba:   ^Bit_Array - the array to iterate over
+
+	Out:
+		- it:   ^Bit_Array_Iterator - the iterator that holds iteration state
+*/
+make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
+	return Bit_Array_Iterator { array = ba }
+}
+
+/*
+	In:
+		- it:    ^Bit_Array_Iterator - the iterator struct that holds the state.
+
+	Out:
+		- set:    bool - the state of the bit at `index`
+		- index:  int - the next bit of the Bit_Array referenced by `it`.
+		- ok:	  bool - `true` if the iterator returned a valid index,
+			  `false` if there were no more bits
+*/
+iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
+	index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
+	if index > it.array.max_index { return false, 0, false }
+
+	word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+	set = (word >> it.bit_idx & 1) == 1
+
+	it.bit_idx += 1
+	if it.bit_idx >= NUM_BITS {
+		it.bit_idx = 0
+		it.word_idx += 1
+	}
+
+	return set, index, true
 }
 
+/*
+	In:
+		- it:     ^Bit_Array_Iterator - the iterator struct that holds the state.
+
+	Out:
+		- index:  int - the next set bit of the Bit_Array referenced by `it`.
+		- ok:	  bool - `true` if the iterator returned a valid index,
+			  `false` if there were no more bits set
+*/
+iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
+	return iterate_internal_(it, true)
+}
+
+/*
+	In:
+		- it:	  ^Bit_Array_Iterator - the iterator struct that holds the state.
+
+	Out:
+		- index:  int - the next unset bit of the Bit_Array referenced by `it`.
+		- ok:	  bool - `true` if the iterator returned a valid index,
+			  `false` if there were no more unset bits
+*/
+iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
+	return iterate_internal_(it, false)
+}
+
+@(private="file")
+iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
+	word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+	when ! ITERATE_SET_BITS { word = ~word }
+
+	// if the word is empty or we have already gone over all the bits in it,
+	// b.bit_idx is greater than the index of any set bit in the word,
+	// meaning that word >> b.bit_idx == 0.
+	for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
+		it.word_idx += 1
+		it.bit_idx = 0
+		word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+		when ! ITERATE_SET_BITS { word = ~word }
+	}
+
+	// if we are iterating the set bits, reaching the end of the array means we have no more bits to check
+	when ITERATE_SET_BITS {
+		if it.word_idx >= len(it.array.bits) {
+			return 0, false
+		}
+	}
+
+	// reaching here means that the word has some set bits
+	it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx))
+	index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
+
+	it.bit_idx += 1
+	if it.bit_idx >= NUM_BITS {
+		it.bit_idx = 0
+		it.word_idx += 1
+	}
+	return index, index <= it.array.max_index
+}
+
+
 /*
 	In:
 		- ba:    ^Bit_Array - a pointer to the Bit Array
@@ -70,6 +178,7 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
 
 	resize_if_needed(ba, leg_index) or_return
 
+	ba.max_index = max(idx, ba.max_index)
 	ba.bits[leg_index] |= 1 << uint(bit_index)
 	return true
 }
@@ -87,8 +196,9 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
 
 	res = Bit_Array{
 		bias = min_index,
+		max_index = max_index,
 	}
-	return res, resize_if_needed(&res, size_in_bits)
+	return res, resize_if_needed(&res, legs)
 }
 
 /*
@@ -121,4 +231,4 @@ resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocat
 		resize(&ba.bits, legs + 1)
 	}
 	return len(ba.bits) > legs
-}
+}

+ 192 - 0
core/container/lru/lru_cache.odin

@@ -0,0 +1,192 @@
+package container_lru
+
+import "core:runtime"
+import "core:intrinsics"
+_ :: runtime
+_ :: intrinsics
+
+Node :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
+	prev, next: ^Node(Key, Value),
+	key:   Key,
+	value: Value,
+}
+
+// Cache is an LRU cache. It automatically removes entries as new entries are
+// added if the capacity is reached. Entries are removed based on how recently
+// they were used where the oldest entries are removed first.
+Cache :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
+	head: ^Node(Key, Value),
+	tail: ^Node(Key, Value),
+
+	entries: map[Key]^Node(Key, Value),
+
+	count:    int,
+	capacity: int,
+
+	node_allocator: runtime.Allocator,
+
+	on_remove: proc(key: Key, value: Value, user_data: rawptr),
+	on_remove_user_data: rawptr,
+}
+
+// init initializes a Cache
+init :: proc(c: ^$C/Cache($Key, $Value), capacity: int, entries_allocator := context.allocator, node_allocator := context.allocator) {
+	c.entries.allocator = entries_allocator
+	c.node_allocator = node_allocator
+	c.capacity = capacity
+}
+
+// destroy deinitializes a Cachem
+destroy :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
+	clear(c, call_on_remove)
+	delete(c.entries)
+}
+
+// clear the contents of a Cache
+clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
+	for _, node in c.entries {
+		if call_on_remove {
+			_call_on_remove(c, node)
+		}
+		free(node, c.node_allocator)
+	}
+	runtime.clear(&c.entries)
+	c.head = nil
+	c.tail = nil
+	c.count = 0
+}
+
+// set the given key value pair. This operation updates the recent usage of the item.
+set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
+	if e, ok := c.entries[key]; ok {
+		e.value = value
+		return nil
+	}
+
+	e := new(Node(Key, Value), c.node_allocator) or_return
+	e.key = key
+	e.value = value
+
+	_push_front_node(c, e)
+	if c.count > c.capacity {
+		_remove_node(c, c.tail)
+	}
+
+	c.entries[key] = e
+	return nil
+}
+
+// get a value from the cache from a given key. This operation updates the usage of the item.
+get :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
+	e: ^Node(Key, Value)
+	e, ok = c.entries[key]
+	if !ok {
+		return
+	}
+	_pop_node(c, e)
+	_push_front_node(c, e)
+	return e.value, true
+}
+
+// get_ptr gets the pointer to a value the cache from a given key. This operation updates the usage of the item.
+get_ptr :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: ^Value, ok: bool) #optional_ok {
+	e: ^Node(Key, Value)
+	e, ok = c.entries[key]
+	if !ok {
+		return
+	}
+	_pop_node(c, e)
+	_push_front_node(c, e)
+	return &e.value, true
+}
+
+// peek gets the value from the cache from a given key without updating the recent usage.
+peek :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
+	e: ^Node(Key, Value)
+	e, ok = c.entries[key]
+	if !ok {
+		return
+	}
+	return e.value, true
+}
+
+// exists checks for the existence of a value from a given key without updating the recent usage.
+exists :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
+	return key in c.entries
+}
+
+// remove removes an item from the cache.
+remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
+	e, ok := c.entries[key]
+	if !ok {
+		return false
+	}
+	_remove_node(c, e)
+	return true
+}
+
+
+@(private)
+_remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
+	if c.head == node {
+		c.head = node.next
+	}
+	if c.tail == node {
+		c.tail = node.prev
+	}
+	if node.prev != nil {
+		node.prev.next = node.next
+	}
+	if node.next != nil {
+		node.next.prev = node.prev
+	}
+	node.prev = nil
+	node.next = nil
+
+	c.count -= 1
+
+	delete_key(&c.entries, node.key)
+
+	_call_on_remove(c, node)
+
+	free(node, c.node_allocator)
+
+}
+
+@(private)
+_call_on_remove :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
+	if c.on_remove != nil {
+		c.on_remove(node.key, node.value, c.on_remove_user_data)
+	}
+}
+
+@(private)
+_push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
+	if c.head != nil {
+		e.next = c.head
+		e.next.prev = e
+	}
+	c.head = e
+	if c.tail == nil {
+		c.tail = e
+	}
+	e.prev = nil
+
+	c.count += 1
+}
+
+@(private)
+_pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
+	if e == nil {
+		return
+	}
+	if e.prev != nil {
+		e.prev.next = e.next
+	}
+
+	if e.next != nil {
+		e.next.prev = e.prev
+	}
+	e.prev = nil
+	e.next = nil
+}

+ 1 - 0
core/container/queue/queue.odin

@@ -2,6 +2,7 @@ package container_queue
 
 import "core:builtin"
 import "core:runtime"
+_ :: runtime
 
 // Dynamically resizable double-ended queue/ring-buffer
 Queue :: struct($T: typeid) {

+ 98 - 0
core/container/topological_sort/topological_sort.odin

@@ -0,0 +1,98 @@
+// The following is a generic O(V+E) topological sorter implementation.
+// This is the fastest known method for topological sorting and Odin's
+// map type is being used to accelerate lookups.
+package container_topological_sort
+
+import "core:intrinsics"
+import "core:runtime"
+_ :: intrinsics
+_ :: runtime
+
+
+Relations :: struct($K: typeid) where intrinsics.type_is_valid_map_key(K) {
+	dependents:   map[K]bool,
+	dependencies: int,
+}
+
+Sorter :: struct(K: typeid) where intrinsics.type_is_valid_map_key(K)  {
+	relations: map[K]Relations(K),
+	dependents_allocator: runtime.Allocator,
+}
+
+@(private="file")
+make_relations :: proc(sorter: ^$S/Sorter($K)) -> (r: Relations(K)) {
+	r.dependents.allocator = sorter.dependents_allocator
+	return
+}
+
+
+init :: proc(sorter: ^$S/Sorter($K)) {
+	sorter.relations = make(map[K]Relations(K))
+	sorter.dependents_allocator = context.allocator
+}
+
+destroy :: proc(sorter: ^$S/Sorter($K)) {
+	for _, v in &sorter.relations {
+		delete(v.dependents)
+	}
+	delete(sorter.relations)
+}
+
+add_key :: proc(sorter: ^$S/Sorter($K), key: K) -> bool {
+	if key in sorter.relations {
+		return false
+	}
+	sorter.relations[key] = make_relations(sorter)
+	return true
+}
+
+add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool {
+	if key == dependency {
+		return false
+	}
+
+	find := &sorter.relations[dependency]
+	if find == nil {
+		find = map_insert(&sorter.relations, dependency, make_relations(sorter))
+	}
+
+	if find.dependents[key] {
+		return true
+	}
+	find.dependents[key] = true
+
+ 	find = &sorter.relations[key]
+	if find == nil {
+		find = map_insert(&sorter.relations, key, make_relations(sorter))
+	}
+
+	find.dependencies += 1
+
+	return true
+}
+
+sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) {
+	relations := &sorter.relations
+
+	for k, v in relations {
+		if v.dependencies == 0 {
+			append(&sorted, k)
+		}
+	}
+
+	for root in &sorted do for k, _ in relations[root].dependents {
+		relation := &relations[k]
+		relation.dependencies -= 1
+		if relation.dependencies == 0 {
+			append(&sorted, k)
+		}
+	}
+
+	for k, v in relations {
+		if v.dependencies != 0 {
+			append(&cycled, k)
+		}
+	}
+
+	return
+}

+ 4 - 4
core/crypto/blake/blake.odin

@@ -44,7 +44,7 @@ hash_bytes_224 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224 will hash the given input and write the
@@ -123,7 +123,7 @@ hash_bytes_256 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -202,7 +202,7 @@ hash_bytes_384 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_384]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_384 will hash the given input and write the
@@ -281,7 +281,7 @@ hash_bytes_512 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_512]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_512 will hash the given input and write the

+ 1 - 1
core/crypto/blake2b/blake2b.odin

@@ -46,7 +46,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 1 - 1
core/crypto/blake2s/blake2s.odin

@@ -47,7 +47,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 1 - 1
core/crypto/gost/gost.odin

@@ -41,7 +41,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 4 - 4
core/crypto/groestl/groestl.odin

@@ -44,7 +44,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224 will hash the given input and write the
@@ -123,7 +123,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -202,7 +202,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_384 will hash the given input and write the
@@ -281,7 +281,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_512 will hash the given input and write the

+ 15 - 15
core/crypto/haval/haval.odin

@@ -50,7 +50,7 @@ hash_bytes_128_3 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_128_3 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_128_3(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_128_3(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_128_3 will hash the given input and write the
@@ -135,7 +135,7 @@ hash_bytes_128_4 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_128_4 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_128_4(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_128_4(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_128_4 will hash the given input and write the
@@ -220,7 +220,7 @@ hash_bytes_128_5 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_128_5 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_128_5(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_128_5(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_128_5 will hash the given input and write the
@@ -305,7 +305,7 @@ hash_bytes_160_3 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_160_3 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_160_3(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_160_3(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_160_3 will hash the given input and write the
@@ -390,7 +390,7 @@ hash_bytes_160_4 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_160_4 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_160_4(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_160_4(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_160_4 will hash the given input and write the
@@ -475,7 +475,7 @@ hash_bytes_160_5 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_160_5 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_160_5(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_160_5(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_160_5 will hash the given input and write the
@@ -560,7 +560,7 @@ hash_bytes_192_3 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_192_3 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_192_3(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_192_3(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_192_3 will hash the given input and write the
@@ -645,7 +645,7 @@ hash_bytes_192_4 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_192_4 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_192_4(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_192_4(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_192_4 will hash the given input and write the
@@ -730,7 +730,7 @@ hash_bytes_192_5 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_192_5 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_192_5(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_192_5(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_192_5 will hash the given input and write the
@@ -815,7 +815,7 @@ hash_bytes_224_3 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224_3 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224_3(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224_3(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224_3 will hash the given input and write the
@@ -900,7 +900,7 @@ hash_bytes_224_4 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224_4 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224_4(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224_4(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224_4 will hash the given input and write the
@@ -985,7 +985,7 @@ hash_bytes_224_5 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224_5 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224_5(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224_5(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224_5 will hash the given input and write the
@@ -1070,7 +1070,7 @@ hash_bytes_256_3 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256_3 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256_3(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256_3(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256_3 will hash the given input and write the
@@ -1155,7 +1155,7 @@ hash_bytes_256_4 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256_4 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256_4(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256_4(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256_4 will hash the given input and write the
@@ -1240,7 +1240,7 @@ hash_bytes_256_5 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256_5 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256_5(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256_5(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256_5 will hash the given input and write the

+ 4 - 4
core/crypto/jh/jh.odin

@@ -44,7 +44,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224 will hash the given input and write the
@@ -123,7 +123,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -202,7 +202,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_384 will hash the given input and write the
@@ -281,7 +281,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_512 will hash the given input and write the

+ 4 - 4
core/crypto/keccak/keccak.odin

@@ -49,7 +49,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224 will hash the given input and write the
@@ -131,7 +131,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -213,7 +213,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_384 will hash the given input and write the
@@ -295,7 +295,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_512 will hash the given input and write the

+ 1 - 1
core/crypto/md2/md2.odin

@@ -40,7 +40,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-	hash_bytes_to_buffer(transmute([]byte)(data), hash);
+	hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 1 - 1
core/crypto/md4/md4.odin

@@ -44,7 +44,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 1 - 1
core/crypto/md5/md5.odin

@@ -43,7 +43,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 4 - 4
core/crypto/ripemd/ripemd.odin

@@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_128 will hash the given input and write the
@@ -121,7 +121,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_160(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_160 will hash the given input and write the
@@ -197,7 +197,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -273,7 +273,7 @@ hash_bytes_320 :: proc(data: []byte) -> [DIGEST_SIZE_320]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_320 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_320(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_320(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_320 will hash the given input and write the

+ 1 - 1
core/crypto/sha1/sha1.odin

@@ -43,7 +43,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 4 - 4
core/crypto/sha2/sha2.odin

@@ -48,7 +48,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224 will hash the given input and write the
@@ -127,7 +127,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -206,7 +206,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_384 will hash the given input and write the
@@ -285,7 +285,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_512 will hash the given input and write the

+ 4 - 4
core/crypto/sha3/sha3.odin

@@ -47,7 +47,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_224 will hash the given input and write the
@@ -126,7 +126,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -205,7 +205,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_384 will hash the given input and write the
@@ -284,7 +284,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_512 will hash the given input and write the

+ 2 - 2
core/crypto/shake/shake.odin

@@ -46,7 +46,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_128 will hash the given input and write the
@@ -128,7 +128,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the

+ 1 - 1
core/crypto/sm3/sm3.odin

@@ -42,7 +42,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 2 - 2
core/crypto/streebog/streebog.odin

@@ -44,7 +44,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_256 will hash the given input and write the
@@ -122,7 +122,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_512 will hash the given input and write the

+ 3 - 3
core/crypto/tiger/tiger.odin

@@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_128 will hash the given input and write the
@@ -124,7 +124,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_160(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_160 will hash the given input and write the
@@ -203,7 +203,7 @@ hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_192(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_192 will hash the given input and write the

+ 3 - 3
core/crypto/tiger2/tiger2.odin

@@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_128 will hash the given input and write the
@@ -124,7 +124,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_160(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_160 will hash the given input and write the
@@ -203,7 +203,7 @@ hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer_192(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer_192 will hash the given input and write the

+ 1 - 1
core/crypto/whirlpool/whirlpool.odin

@@ -42,7 +42,7 @@ hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
 // computed hash to the second parameter.
 // It requires that the destination buffer is at least as big as the digest size
 hash_string_to_buffer :: proc(data: string, hash: []byte) {
-    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+    hash_bytes_to_buffer(transmute([]byte)(data), hash)
 }
 
 // hash_bytes_to_buffer will hash the given input and write the

+ 27 - 6
core/fmt/fmt.odin

@@ -11,6 +11,7 @@ import "core:time"
 import "core:unicode/utf8"
 import "core:intrinsics"
 
+// Internal data structure that stores the required information for formatted printing
 Info :: struct {
 	minus:     bool,
 	plus:      bool,
@@ -46,9 +47,13 @@ Register_User_Formatter_Error :: enum {
 // it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary
 _user_formatters: ^map[typeid]User_Formatter
 
+// set_user_formatters assigns m to a global value allowing the user have custom print formatting for specific
+// types
 set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
 	_user_formatters = m
 }
+// register_user_formatter assigns a formatter to a specific typeid. set_user_formatters must be called
+// before any use of this procedure.
 register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error {
 	if _user_formatters == nil {
 		return .No_User_Formatter
@@ -61,7 +66,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
 }
 
 
-// aprint* procedures return a string that was allocated with the current context
+// aprint procedure return a string that was allocated with the current context
 // They must be freed accordingly
 aprint :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
@@ -69,12 +74,16 @@ aprint :: proc(args: ..any, sep := " ") -> string {
 	sbprint(buf=&str, args=args, sep=sep)
 	return strings.to_string(str)
 }
+// aprintln procedure return a string that was allocated with the current context
+// They must be freed accordingly
 aprintln :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.init_builder(&str)
 	sbprintln(buf=&str, args=args, sep=sep)
 	return strings.to_string(str)
 }
+// aprintf procedure return a string that was allocated with the current context
+// They must be freed accordingly
 aprintf :: proc(fmt: string, args: ..any) -> string {
 	str: strings.Builder
 	strings.init_builder(&str)
@@ -83,19 +92,21 @@ aprintf :: proc(fmt: string, args: ..any) -> string {
 }
 
 
-// tprint* procedures return a string that was allocated with the current context's temporary allocator
+// tprint procedure return a string that was allocated with the current context's temporary allocator
 tprint :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.init_builder(&str, context.temp_allocator)
 	sbprint(buf=&str, args=args, sep=sep)
 	return strings.to_string(str)
 }
+// tprintln procedure return a string that was allocated with the current context's temporary allocator
 tprintln :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.init_builder(&str, context.temp_allocator)
 	sbprintln(buf=&str, args=args, sep=sep)
 	return strings.to_string(str)
 }
+// tprintf procedure return a string that was allocated with the current context's temporary allocator
 tprintf :: proc(fmt: string, args: ..any) -> string {
 	str: strings.Builder
 	strings.init_builder(&str, context.temp_allocator)
@@ -104,21 +115,24 @@ tprintf :: proc(fmt: string, args: ..any) -> string {
 }
 
 
-// bprint* procedures return a string using a buffer from an array
+// bprint procedures return a string using a buffer from an array
 bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 	sb := strings.builder_from_slice(buf[0:len(buf)])
 	return sbprint(buf=&sb, args=args, sep=sep)
 }
+// bprintln procedures return a string using a buffer from an array
 bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 	sb := strings.builder_from_slice(buf[0:len(buf)])
 	return sbprintln(buf=&sb, args=args, sep=sep)
 }
+// bprintf procedures return a string using a buffer from an array
 bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
 	sb := strings.builder_from_slice(buf[0:len(buf)])
 	return sbprintf(&sb, fmt, ..args)
 }
 
 
+// formatted assert
 assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool {
 	if !condition {
 		p := context.assertion_failure_proc
@@ -131,6 +145,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
 	return condition
 }
 
+// formatted panic
 panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
 	p := context.assertion_failure_proc
 	if p == nil {
@@ -142,24 +157,26 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
 
 
 
-
-
+// sbprint formats using the default print settings and writes to buf
 sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
 	wprint(w=strings.to_writer(buf), args=args, sep=sep)
 	return strings.to_string(buf^)
 }
 
+// sbprintln formats using the default print settings and writes to buf
 sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
 	wprintln(w=strings.to_writer(buf), args=args, sep=sep)
 	return strings.to_string(buf^)
 }
 
+// sbprintf formats according to the specififed format string and writes to buf
 sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
 	wprintf(w=strings.to_writer(buf), fmt=fmt, args=args)
 	return strings.to_string(buf^)
 }
 
 
+// wprint formats using the default print settings and writes to w
 wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
 	fi: Info
 	fi.writer = w
@@ -194,6 +211,7 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
 	return int(size1 - size0)
 }
 
+// wprintln formats using the default print settings and writes to w
 wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
 	fi: Info
 	fi.writer = w
@@ -214,6 +232,7 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
 	return int(size1 - size0)
 }
 
+// wprintf formats according to the specififed format string and writes to w
 wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
 	fi: Info
 	arg_index: int = 0
@@ -493,11 +512,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
 	return int(size1 - size0)
 }
 
+// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w
 wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> (int, io.Error) {
 	n, err := reflect.write_type(w, info)
 	io.flush(auto_cast w)
 	return n, err
 }
+// wprint_typeid is a utility procedure to write a typeid value to w
 wprint_typeid :: proc(w: io.Writer, id: typeid) -> (int, io.Error) {
 	n, err := reflect.write_type(w, type_info_of(id))
 	io.flush(auto_cast w)
@@ -829,7 +850,7 @@ _pad :: proc(fi: ^Info, s: string) {
 
 fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
 	switch verb {
-	case 'f', 'F', 'v':
+	case 'f', 'F', 'g', 'G', 'v':
 		prec: int = 3
 		if fi.prec_set {
 			prec = fi.prec

+ 6 - 1
core/fmt/fmt_js.odin

@@ -34,11 +34,16 @@ stderr := io.Writer{
 	},
 }
 
-// print* procedures return the number of bytes written
+// print formats using the default print settings and writes to stdout
 print   :: proc(args: ..any, sep := " ") -> int { return wprint(w=stdout, args=args, sep=sep) }
+// println formats using the default print settings and writes to stdout
 println :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stdout, args=args, sep=sep) }
+// printf formats according to the specififed format string and writes to stdout
 printf  :: proc(fmt: string, args: ..any) -> int { return wprintf(stdout, fmt, ..args) }
 
+// eprint formats using the default print settings and writes to stderr
 eprint   :: proc(args: ..any, sep := " ") -> int { return wprint(w=stderr, args=args, sep=sep) }
+// eprintln formats using the default print settings and writes to stderr
 eprintln :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stderr, args=args, sep=sep) }
+// eprintf formats according to the specififed format string and writes to stderr
 eprintf  :: proc(fmt: string, args: ..any) -> int { return wprintf(stderr, fmt, ..args) }

+ 9 - 1
core/fmt/fmt_os.odin

@@ -5,15 +5,18 @@ import "core:runtime"
 import "core:os"
 import "core:io"
 
+// fprint formats using the default print settings and writes to fd
 fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
 	w := io.to_writer(os.stream_from_handle(fd))
 	return wprint(w=w, args=args, sep=sep)
 }
 
+// fprintln formats using the default print settings and writes to fd
 fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
 	w := io.to_writer(os.stream_from_handle(fd))
 	return wprintln(w=w, args=args, sep=sep)
 }
+// fprintf formats according to the specififed format string and writes to fd
 fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
 	w := io.to_writer(os.stream_from_handle(fd))
 	return wprintf(w, fmt, ..args)
@@ -27,11 +30,16 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
 	return wprint_typeid(w, id)
 }
 
-// print* procedures return the number of bytes written
+// print formats using the default print settings and writes to os.stdout
 print   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
+// println formats using the default print settings and writes to os.stdout
 println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
+// printf formats according to the specififed format string and writes to os.stdout
 printf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
 
+// eprint formats using the default print settings and writes to os.stderr
 eprint   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
+// eprintln formats using the default print settings and writes to os.stderr
 eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
+// eprintf formats according to the specififed format string and writes to os.stderr
 eprintf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }

+ 39 - 2
core/io/io.odin

@@ -1,9 +1,13 @@
+// package io provides basic interfaces for generic data stream primitives.
+// The purpose of this package is wrap existing data structures and their
+// operations into an abstracted stream interface.
 package io
 
 import "core:intrinsics"
 import "core:runtime"
 import "core:unicode/utf8"
 
+// Seek whence values
 Seek_From :: enum {
 	Start   = 0, // seek relative to the origin of the file
 	Current = 1, // seek relative to the current offset
@@ -139,6 +143,10 @@ destroy :: proc(s: Stream) -> Error {
 	return .Empty
 }
 
+// read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred.
+//
+// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
+// bytes read along with the error.
 read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
 	if s.stream_vtable != nil && s.impl_read != nil {
 		n, err = s->impl_read(p)
@@ -150,6 +158,7 @@ read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
 	return 0, .Empty
 }
 
+// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
 write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
 	if s.stream_vtable != nil && s.impl_write != nil {
 		n, err = s->impl_write(p)
@@ -161,6 +170,13 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro
 	return 0, .Empty
 }
 
+// seek sets the offset of the next read or write to offset.
+//
+// .Start means seek relative to the origin of the file.
+// .Current means seek relative to the current offset.
+// .End means seek relative to the end.
+//
+// seek returns the new offset to the start of the file/stream, and any error if occurred.
 seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
 	if s.stream_vtable != nil && s.impl_seek != nil {
 		return s->impl_seek(offset, whence)
@@ -168,6 +184,8 @@ seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error)
 	return 0, .Empty
 }
 
+// The behaviour of close after the first call is stream implementation defined.
+// Different streams may document their own behaviour.
 close :: proc(s: Closer) -> Error {
 	if s.stream_vtable != nil && s.impl_close != nil {
 		return s->impl_close()
@@ -184,6 +202,7 @@ flush :: proc(s: Flusher) -> Error {
 	return .None
 }
 
+// size returns the size of the stream. If the stream does not support querying its size, 0 will be returned.
 size :: proc(s: Stream) -> i64 {
 	if s.stream_vtable == nil {
 		return 0
@@ -214,7 +233,12 @@ size :: proc(s: Stream) -> i64 {
 
 
 
-
+// read_at reads len(p) bytes into p starting with the provided offset in the underlying Reader_At stream r.
+// It returns the number of bytes read and any error if occurred.
+//
+// When read_at returns n < len(p), it returns a non-nil Error explaining why.
+//
+// If n == len(p), err may be either nil or .EOF
 read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: int, err: Error) {
 	defer if n_read != nil {
 		n_read^ += n
@@ -245,6 +269,11 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
 
 }
 
+// write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w.
+// It returns the number of bytes written and any error if occurred.
+//
+// If write_at is writing to a Writer_At which has a seek offset, then write_at should not affect the underlying
+// seek offset.
 write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> (n: int, err: Error) {
 	defer if n_written != nil {
 		n_written^ += n
@@ -294,6 +323,7 @@ read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
 }
 
 
+// read_byte reads and returns the next byte from r.
 read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
 	defer if err == nil && n_read != nil {
 		n_read^ += 1
@@ -347,6 +377,7 @@ _write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Err
 	return err
 }
 
+// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
 read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
 	defer if err == nil && n_read != nil {
 		n_read^ += size
@@ -405,10 +436,12 @@ unread_rune :: proc(s: Rune_Scanner) -> Error {
 }
 
 
+// write_string writes the contents of the string s to w.
 write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, err: Error) {
 	return write(s, transmute([]byte)str, n_written)
 }
 
+// write_rune writes a UTF-8 encoded rune to w.
 write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) {
 	defer if err == nil && n_written != nil {
 		n_written^ += size
@@ -430,12 +463,16 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
 }
 
 
-
+// read_full expected exactly len(buf) bytes from r into buf.
 read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
 	return read_at_least(r, buf, len(buf))
 }
 
 
+// read_at_least reads from r into buf until it has read at least min bytes. It returns the number
+// of bytes copied and an error if fewer bytes were read. `.EOF` is only returned if no bytes were read.
+// `.Unexpected_EOF` is returned when an `.EOF ` is returned by the passed Reader after reading
+// fewer than min bytes. If len(buf) is less than min, `.Short_Buffer` is returned.
 read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
 	if len(buf) < min {
 		return 0, .Short_Buffer

+ 2 - 1
core/math/big/common.odin

@@ -172,7 +172,7 @@ Error :: enum int {
 	Unimplemented           = 127,
 }
 
-Error_String :: #partial [Error]string{
+Error_String :: #sparse[Error]string{
 	.Okay                    = "Okay",
 	.Out_Of_Memory           = "Out of memory",
 	.Invalid_Pointer         = "Invalid pointer",
@@ -182,6 +182,7 @@ Error_String :: #partial [Error]string{
 	.Max_Iterations_Reached  = "Max iterations reached",
 	.Buffer_Overflow         = "Buffer overflow",
 	.Integer_Overflow        = "Integer overflow",
+	.Integer_Underflow       = "Integer underflow",
 
 	.Division_by_Zero        = "Division by zero",
 	.Math_Domain_Error       = "Math domain error",

+ 19 - 0
core/math/linalg/glsl/linalg_glsl.odin

@@ -473,6 +473,25 @@ floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), fl
 floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
 
 
+
+round :: proc{
+	round_f32,
+	round_f64,
+	round_vec2,
+	round_vec3,
+	round_vec4,
+	round_dvec2,
+	round_dvec3,
+	round_dvec4,
+}
+round_vec2 :: proc "c" (x: vec2) -> vec2 { return {round(x.x), round(x.y)} }
+round_vec3 :: proc "c" (x: vec3) -> vec3 { return {round(x.x), round(x.y), round(x.z)} }
+round_vec4 :: proc "c" (x: vec4) -> vec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+round_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {round(x.x), round(x.y)} }
+round_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {round(x.x), round(x.y), round(x.z)} }
+round_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+
+
 ceil :: proc{
 	ceil_f32,
 	ceil_f64,

+ 2 - 0
core/math/linalg/glsl/linalg_glsl_math.odin

@@ -23,6 +23,7 @@ log_f32         :: proc "c" (x: f32) -> f32 { return math.ln(x) }
 exp2_f32        :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
 sign_f32        :: proc "c" (x: f32) -> f32 { return math.sign(x) }
 floor_f32       :: proc "c" (x: f32) -> f32 { return math.floor(x) }
+round_f32       :: proc "c" (x: f32) -> f32 { return math.round(x) }
 ceil_f32        :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
 mod_f32         :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
 fract_f32 :: proc "c" (x: f32) -> f32 {
@@ -53,6 +54,7 @@ log_f64         :: proc "c" (x: f64) -> f64 { return math.ln(x) }
 exp2_f64        :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
 sign_f64        :: proc "c" (x: f64) -> f64 { return math.sign(x) }
 floor_f64       :: proc "c" (x: f64) -> f64 { return math.floor(x) }
+round_f64       :: proc "c" (x: f64) -> f64 { return math.round(x) }
 ceil_f64        :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
 mod_f64         :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
 fract_f64 :: proc "c" (x: f64) -> f64 {

+ 80 - 0
core/math/linalg/hlsl/linalg_hlsl.odin

@@ -551,6 +551,23 @@ floor_double2 :: proc "c" (x: double2) -> double2 { return {floor(x.x), floor(x.
 floor_double3 :: proc "c" (x: double3) -> double3 { return {floor(x.x), floor(x.y), floor(x.z)} }
 floor_double4 :: proc "c" (x: double4) -> double4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
 
+round :: proc{
+	round_float,
+	round_double,
+	round_float2,
+	round_float3,
+	round_float4,
+	round_double2,
+	round_double3,
+	round_double4,
+}
+round_float2 :: proc "c" (x: float2) -> float2 { return {round(x.x), round(x.y)} }
+round_float3 :: proc "c" (x: float3) -> float3 { return {round(x.x), round(x.y), round(x.z)} }
+round_float4 :: proc "c" (x: float4) -> float4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+round_double2 :: proc "c" (x: double2) -> double2 { return {round(x.x), round(x.y)} }
+round_double3 :: proc "c" (x: double3) -> double3 { return {round(x.x), round(x.y), round(x.z)} }
+round_double4 :: proc "c" (x: double4) -> double4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+
 
 ceil :: proc{
 	ceil_float,
@@ -570,6 +587,69 @@ ceil_double3 :: proc "c" (x: double3) -> double3 { return {ceil(x.x), ceil(x.y),
 ceil_double4 :: proc "c" (x: double4) -> double4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
 
 
+isfinite_float  :: proc "c" (x: float)  -> bool  { return !isinf_float(x) }
+isfinite_float2 :: proc "c" (x: float2) -> bool2 { return {isfinite_float(x.x), isfinite_float(x.y)} }
+isfinite_float3 :: proc "c" (x: float3) -> bool3 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z)} }
+isfinite_float4 :: proc "c" (x: float4) -> bool4 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z), isfinite_float(x.w)} }
+isfinite_double  :: proc "c" (x: double)  -> bool  { return !isinf_double(x) }
+isfinite_double2 :: proc "c" (x: double2) -> bool2 { return {isfinite_double(x.x), isfinite_double(x.y)} }
+isfinite_double3 :: proc "c" (x: double3) -> bool3 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z)} }
+isfinite_double4 :: proc "c" (x: double4) -> bool4 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z), isfinite_double(x.w)} }
+
+// isfinite is the opposite of isinf and returns true if the number is neither positive-infinite or negative-infinite
+isfinite :: proc{
+	isfinite_float,
+	isfinite_float2,
+	isfinite_float3,
+	isfinite_float4,
+	isfinite_double,
+	isfinite_double2,
+	isfinite_double3,
+	isfinite_double4,
+}
+
+
+isinf_float  :: proc "c" (x: float)  -> bool  { return x * 0.5 == x }
+isinf_float2 :: proc "c" (x: float2) -> bool2 { return {isinf_float(x.x), isinf_float(x.y)} }
+isinf_float3 :: proc "c" (x: float3) -> bool3 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z)} }
+isinf_float4 :: proc "c" (x: float4) -> bool4 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z), isinf_float(x.w)} }
+isinf_double  :: proc "c" (x: double)  -> bool  { return x * 0.5 == x }
+isinf_double2 :: proc "c" (x: double2) -> bool2 { return {isinf_double(x.x), isinf_double(x.y)} }
+isinf_double3 :: proc "c" (x: double3) -> bool3 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z)} }
+isinf_double4 :: proc "c" (x: double4) -> bool4 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z), isinf_double(x.w)} }
+
+// isinf is the opposite of isfinite and returns true if the number is either positive-infinite or negative-infinite
+isinf :: proc{
+	isinf_float,
+	isinf_float2,
+	isinf_float3,
+	isinf_float4,
+	isinf_double,
+	isinf_double2,
+	isinf_double3,
+	isinf_double4,
+}
+
+
+isnan_float2 :: proc "c" (x: float2) -> bool2 { return {isnan_float(x.x), isnan_float(x.y)} }
+isnan_float3 :: proc "c" (x: float3) -> bool3 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z)} }
+isnan_float4 :: proc "c" (x: float4) -> bool4 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z), isnan_float(x.w)} }
+isnan_double2 :: proc "c" (x: double2) -> bool2 { return {isnan_double(x.x), isnan_double(x.y)} }
+isnan_double3 :: proc "c" (x: double3) -> bool3 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z)} }
+isnan_double4 :: proc "c" (x: double4) -> bool4 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z), isnan_double(x.w)} }
+
+// isnan returns true if the input value is the special case of Not-A-Number
+isnan :: proc{
+	isnan_float,
+	isnan_float2,
+	isnan_float3,
+	isnan_float4,
+	isnan_double,
+	isnan_double2,
+	isnan_double3,
+	isnan_double4,
+}
+
 fmod :: proc{
 	fmod_float,
 	fmod_double,

+ 5 - 0
core/math/linalg/hlsl/linalg_hlsl_math.odin

@@ -26,7 +26,9 @@ log10_float       :: proc "c" (x: float)    -> float { return math.log(x, 10) }
 exp2_float        :: proc "c" (x: float)    -> float { return math.pow(float(2), x) }
 sign_float        :: proc "c" (x: float)    -> float { return math.sign(x) }
 floor_float       :: proc "c" (x: float)    -> float { return math.floor(x) }
+round_float       :: proc "c" (x: float)    -> float { return math.round(x) }
 ceil_float        :: proc "c" (x: float)    -> float { return math.ceil(x) }
+isnan_float       :: proc "c" (x: float)    -> bool  { return math.classify(x) == .NaN}
 fmod_float        :: proc "c" (x, y: float) -> float { return math.mod(x, y) }
 frac_float :: proc "c" (x: float) -> float {
 	if x >= 0 {
@@ -35,6 +37,7 @@ frac_float :: proc "c" (x: float) -> float {
 	return math.trunc(-x) + x
 }
 
+
 cos_double         :: proc "c" (x: double)    -> double { return math.cos(x) }
 sin_double         :: proc "c" (x: double)    -> double { return math.sin(x) }
 tan_double         :: proc "c" (x: double)    -> double { return math.tan(x) }
@@ -59,7 +62,9 @@ log10_double       :: proc "c" (x: double)    -> double { return math.log(x, 10)
 exp2_double        :: proc "c" (x: double)    -> double { return math.pow(double(2), x) }
 sign_double        :: proc "c" (x: double)    -> double { return math.sign(x) }
 floor_double       :: proc "c" (x: double)    -> double { return math.floor(x) }
+round_double       :: proc "c" (x: double)    -> double { return math.round(x) }
 ceil_double        :: proc "c" (x: double)    -> double { return math.ceil(x) }
+isnan_double       :: proc "c" (x: double)    -> bool   { return math.classify(x) == .NaN}
 fmod_double        :: proc "c" (x, y: double) -> double { return math.mod(x, y) }
 frac_double :: proc "c" (x: double) -> double {
 	if x >= 0 {

+ 734 - 0
core/math/noise/internal.odin

@@ -0,0 +1,734 @@
+/*
+	OpenSimplex2 noise implementation.
+
+	Ported from https://github.com/KdotJPG/OpenSimplex2.
+	Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+//+private
+package math_noise
+
+/*
+	Private implementation details follow.
+*/
+
+PRIME_X                  :: i64(0x5205402B9270C86F)
+PRIME_Y                  :: i64(0x598CD327003817B5)
+PRIME_Z                  :: i64(0x5BCC226E9FA0BACB)
+PRIME_W                  :: i64(0x56CC5227E58F554B)
+
+HASH_MULTIPLIER          :: i64(0x53A3F72DEEC546F5)
+SEED_FLIP_3D             :: i64(-0x52D547B2E96ED629)
+SEED_OFFSET_4D           :: i64(0xE83DC3E0DA7164D)
+
+ROOT_2_OVER_2            :: f64(0.7071067811865476)
+SKEW_2D                  :: f64(0.366025403784439)
+UNSKEW_2D                :: f64(-0.21132486540518713)
+ROOT_3_OVER_3            :: f64(0.577350269189626)
+
+FALLBACK_ROTATE_3D       :: f64(2.0) / f64(3.0)
+ROTATE_3D_ORTHOGONALIZER :: f64(UNSKEW_2D)
+
+SKEW_4D                  :: f32(0hbe0d8369)
+UNSKEW_4D                :: f32(0.309016994374947)
+LATTICE_STEP_4D          :: f32(0.2)
+
+N_GRADS_2D_EXPONENT      :: 7
+N_GRADS_3D_EXPONENT      :: 8
+N_GRADS_4D_EXPONENT      :: 9
+N_GRADS_2D               :: 1 << N_GRADS_2D_EXPONENT
+N_GRADS_3D               :: 1 << N_GRADS_3D_EXPONENT
+N_GRADS_4D               :: 1 << N_GRADS_4D_EXPONENT
+
+NORMALIZER_2D            :: f64(0.01001634121365712)
+NORMALIZER_3D            :: f64(0.07969837668935331)
+NORMALIZER_4D            :: f64(0.0220065933241897)
+RSQUARED_2D              :: f32(0.5)
+RSQUARED_3D              :: f32(0.6)
+RSQUARED_4D              :: f32(0.6)
+
+GRADIENTS_2D := [N_GRADS_2D * 2]f32{
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+	0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d, 
+	0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a, 
+	0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d, 
+	0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a, 
+	0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975, 
+	0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975, 
+}
+
+GRADIENTS_3D := [N_GRADS_3D * 4]f32{
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+	0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 
+	0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 
+	0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 
+	0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000, 
+	0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000, 
+	0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 
+	0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000, 
+	0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000, 
+	0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000, 
+	0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000, 
+	0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 
+	0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 
+	0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 
+	0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 
+}
+
+GRADIENTS_4D := [N_GRADS_4D * 4]f32{
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+	0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+	0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+	0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+	0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+	0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+	0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+	0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+	0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+	0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+	0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+	0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+	0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+	0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+	0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+	0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+	0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+	0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+	0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+	0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+	0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+	0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+	0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+	0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+	0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+	0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+	0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+	0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+	0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+	0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+	0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+	0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+	0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+	0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+	0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+	0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+	0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+	0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+	0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+	0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+	0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+	0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+	0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+	0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+	0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+	0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+	0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+	0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+	0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+	0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+	0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+	0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+	0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+	0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+	0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+	0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+	0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+	0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+	0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+	0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+	0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+	0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+	0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+	0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+	0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+	0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+	0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+	0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+	0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+	0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+	0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+	0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+	0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+	0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+	0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+	0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+	0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+	0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+	0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+	0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+	0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+	0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+	0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+	0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+	0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+	0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+	0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+	0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+	0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+	0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+	0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+	0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+	0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+	0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+	0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+	0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+	0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+	0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+	0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+	0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+	0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+	0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+	0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+	0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+	0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+	0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+	0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+	0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+	0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+	0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+	0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+	0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+	0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+	0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+	0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+	0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+	0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+	0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+	0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+	0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+	0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+	0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+	0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+	0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+	0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+	0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+	0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+	0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+	0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+	0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+	0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+	0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+	0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+	0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+	0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+	0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+	0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+	0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+	0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+	0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+	0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+	0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+	0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+	0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+	0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+	0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+	0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+	0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+	0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+	0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+	0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+	0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+	0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+	0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+	0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+	0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+	0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+	0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+	0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+	0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+	0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+	0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+	0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+	0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+	0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+	0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+	0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+	0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+	0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+	0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+	0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+	0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+	0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+	0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+	0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+	0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+	0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+	0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+	0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+	0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+	0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+	0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+	0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+	0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+	0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+	0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+	0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+	0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+	0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+	0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+	0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+	0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+	0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+	0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+	0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+	0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+	0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+	0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+	0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+	0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+	0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+}
+
+/*
+	2D Simplex noise base.
+*/
+_internal_noise_2d_unskewed_base :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+	// Get base points and offsets.
+	base := [2]i64{fast_floor(coord.x), fast_floor(coord.y)}
+	i    := [2]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y))}
+
+	// Prime pre-multiplication for hash.
+	bp := base * [2]i64{PRIME_X, PRIME_Y}
+
+	// Unskew.
+	t  := f32(i.x + i.y) * f32(UNSKEW_2D)
+	d0 := i + [2]f32{t, t}
+
+	// First vertex.
+	a0 := RSQUARED_2D - d0.x * d0.x - d0.y * d0.y
+	if a0 > 0 {
+		value = (a0 * a0) * (a0 * a0) * grad(seed, [2]i64{bp.x, bp.y}, d0)
+	}
+
+	// Second vertex.
+	a1 := f32(2 * (1 + 2 * UNSKEW_2D) * (1 / UNSKEW_2D + 2)) * t + f32(-2 * (1 + 2 * UNSKEW_2D) * (1 + 2 * UNSKEW_2D)) + a0
+	if a1 > 0 {
+		d1 := d0 - [2]f32{f32(1 + 2 * UNSKEW_2D), f32(1 + 2 * UNSKEW_2D)}
+		value += (a1 * a1) * (a1 * a1) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y + PRIME_Y}, d1)
+	}
+
+	// Third vertex.
+	if d0.y > d0.x {
+		d2 := d0 - [2]f32{f32(UNSKEW_2D), f32(UNSKEW_2D + 1)}
+		a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+		if(a2 > 0) {
+			value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x, bp.y + PRIME_Y}, d2)
+		}
+	} else {
+		d2 := d0 - [2]f32{f32(UNSKEW_2D + 1), f32(UNSKEW_2D)}
+		a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+		if(a2 > 0) {
+			value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y}, d2)
+		}
+	}
+
+	return
+}
+
+
+/*
+	Generate overlapping cubic lattices for 3D OpenSimplex2 noise.
+*/
+_internal_noise_3d_unrotated_base :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	seed := seed
+	// Get base points and offsets.
+	// xr, yr, zr := coord.x, coord.y, coord.z
+
+	rb := [3]i64{fast_round(coord.x), fast_round(coord.y), fast_round(coord.z)}
+	ri := [3]f32{f32(coord.x - f64(rb.x)), f32(coord.y - f64(rb.y)), f32(coord.z - f64(rb.z))}
+
+	// -1 if positive, 1 if negative.
+	i_sign := [3]i64{i64(-1.0 - ri.x) | 1, i64(-1.0 - ri.y) | 1, i64(-1.0 - ri.z) | 1}
+	f_sign := [3]f32{f32(i_sign.x), f32(i_sign.y), f32(i_sign.z)}
+
+	// Compute absolute values, using the above as a shortcut. This was faster in my tests for some reason.
+	a0 := f_sign * -ri
+
+	// Prime pre-multiplication for hash.
+	rbp := rb * [3]i64{PRIME_X, PRIME_Y, PRIME_Z}
+
+	// Loop: Pick an edge on each lattice copy.
+	a := (RSQUARED_3D - ri.x * ri.x) - (ri.y * ri.y + ri.z * ri.z)
+
+	l := 0
+	for {
+		defer l += 1
+		
+		// Closest point on cube.
+		if a > 0 {
+			a2 := a * a; a4 := a2 * a2
+			value += a4 * grad(seed, rbp, ri)
+		}
+
+		// Second-closest point.
+		if a0.x >= a0.y && a0.x >= a0.z {
+			b := a + a0.x + a0.x
+			if b > 1 {
+				b -= 1
+				b2 := b * b; b4 := b2 * b2
+				value += b4 * grad(seed, [3]i64{rbp.x - i_sign.x * PRIME_X, rbp.y, rbp.z}, [3]f32{ri.x + f_sign.x, ri.y, ri.z})
+			}
+		} else if a0.y > a0.x && a0.y >= a0.z {
+			b := a + a0.y + a0.y
+			if b > 1 {
+				b -= 1
+				b2 := b * b; b4 := b2 * b2
+				value += b4 * grad(seed, [3]i64{rbp.x, rbp.y - i_sign.y * PRIME_Y, rbp.z}, [3]f32{ri.x, ri.y + f_sign.y, ri.z})
+			}
+		} else {
+			b := a + a0.z + a0.z
+			if b > 1 {
+				b -= 1
+				b2 := b * b; b4 := b2 * b2
+				value += b4 * grad(seed, [3]i64{rbp.x, rbp.y, rbp.z - i_sign.z * PRIME_Z}, [3]f32{ri.x, ri.y, ri.z + f_sign.z})
+			}
+		}
+
+		// Break from loop if we're done, skipping updates below.
+		if l == 1 {
+			break
+		}
+
+		// Update absolute value.
+		a0 = 0.5 - a0
+
+		// Update relative coordinate.
+		ri = a0 * f_sign
+
+		// Update falloff.
+		a += (0.75 - a0.x) - (a0.y + a0.z)
+
+		// Update prime for hash.
+		rbp += [3]i64{i_sign.x >> 1, i_sign.y >> 1, i_sign.z >> 1} & {PRIME_X, PRIME_Y, PRIME_Z}
+
+		// Update the reverse sign indicators.
+		i_sign = -i_sign
+		f_sign = -f_sign
+
+		// And finally update the seed for the other lattice copy.
+		seed ~= SEED_FLIP_3D
+	}
+
+	return value
+}
+
+/*
+	4D OpenSimplex2 noise base.
+*/
+_internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	seed := seed
+
+	// Get base points and offsets
+	base := [4]i64{fast_floor(coord.x), fast_floor(coord.y), fast_floor(coord.z), fast_floor(coord.w)}
+	si   := [4]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y)), f32(coord.z - f64(base.z)), f32(coord.w - f64(base.w))}
+
+	// Determine which lattice we can be confident has a contributing point its corresponding cell's base simplex.
+	// We only look at the spaces between the diagonal planes. This proved effective in all of my tests.
+	si_sum := (si.x + si.y) + (si.z + si.w)
+	starting_lattice := i64(si_sum * 1.25)
+
+	// Offset for seed based on first lattice copy.
+	seed += starting_lattice * SEED_OFFSET_4D
+
+	// Offset for lattice point relative positions (skewed)
+	starting_lattice_offset := f32(starting_lattice) * -LATTICE_STEP_4D
+	si += starting_lattice_offset
+
+	// Prep for vertex contributions.
+	ssi := (si_sum + starting_lattice_offset * 4) * UNSKEW_4D
+				
+	// Prime pre-multiplication for hash.
+	svp := base * [4]i64{PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+
+	// Five points to add, total, from five copies of the A4 lattice.
+	for i : i64 = 0; ; i += 1 {
+		
+		// Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
+		score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
+		if si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score {
+			svp.x += PRIME_X
+			si.x -= 1
+			ssi -= UNSKEW_4D
+		}
+		else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score {
+			svp.y += PRIME_Y
+			si.y -= 1
+			ssi -= UNSKEW_4D
+		}
+		else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score {
+			svp.z += PRIME_Z
+			si.z -= 1
+			ssi -= UNSKEW_4D
+		}
+		else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score {
+			svp.w += PRIME_W
+			si.w -= 1
+			ssi -= UNSKEW_4D
+		}
+
+		// gradient contribution with falloff.
+		d := si + ssi
+		a := (d.x * d.x + d.y * d.y) + (d.z * d.z + d.w * d.w)
+
+		if a < RSQUARED_4D {
+			a -= RSQUARED_4D
+			a *= a; a4 := a * a
+			value += a4 * grad(seed, svp, d)
+		}
+
+		// Break from loop if we're done, skipping updates below.
+		if i == 4 {
+			break
+		}
+
+		// Update for next lattice copy shifted down by <-0.2, -0.2, -0.2, -0.2>.
+		si   += LATTICE_STEP_4D
+		ssi  += LATTICE_STEP_4D * 4 * UNSKEW_4D
+		seed -= SEED_OFFSET_4D
+
+		// Because we don't always start on the same lattice copy, there's a special reset case.
+		if i == starting_lattice {
+			svp  -= {PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+			seed += SEED_OFFSET_4D * 5
+		}
+	}
+	return
+}
+
+/*
+	Utility functions
+*/
+@(optimization_mode="speed")
+grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
+	hash := seed ~ svp.x ~ svp.y
+	hash *= HASH_MULTIPLIER
+	hash ~= hash >> (64 - N_GRADS_2D_EXPONENT + 1)
+
+	gi   := hash & ((N_GRADS_2D - 1) << 1)
+	return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y
+}
+
+@(optimization_mode="speed")
+grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
+	hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
+	hash *= HASH_MULTIPLIER
+	hash ~= hash >> (64 - N_GRADS_3D_EXPONENT + 2)
+
+	gi   := hash & ((N_GRADS_3D - 1) << 2)
+	return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z
+}
+
+@(optimization_mode="speed")
+grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
+	hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
+	hash *= HASH_MULTIPLIER
+	hash ~= hash >> (64 - N_GRADS_4D_EXPONENT + 2)
+
+	gi := hash & ((N_GRADS_4D - 1) << 2)
+	return (GRADIENTS_4D[gi] * delta.x + GRADIENTS_4D[gi | 1] * delta.y) + (GRADIENTS_4D[gi | 2] * delta.z + GRADIENTS_4D[gi | 3] * delta.w)
+}
+
+grad :: proc {grad_2d, grad_3d, grad_4d}
+
+@(optimization_mode="speed")
+fast_floor :: proc(x: f64) -> (floored: i64) {
+	xi := i64(x)
+	return x < f64(xi) ? xi - 1 : xi
+}
+
+@(optimization_mode="speed")
+fast_round :: proc(x: f64) -> (rounded: i64) {
+	return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
+}

+ 171 - 0
core/math/noise/opensimplex2.odin

@@ -0,0 +1,171 @@
+/*
+	OpenSimplex2 noise implementation.
+
+	Ported from https://github.com/KdotJPG/OpenSimplex2.
+	Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+package math_noise
+
+/*
+	Input coordinate vectors
+*/
+Vec2 :: [2]f64
+Vec3 :: [3]f64
+Vec4 :: [4]f64
+
+/*
+	Noise Evaluators
+*/
+
+/*
+	2D Simplex noise, standard lattice orientation.
+*/
+noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+	// Get points for A2* lattice
+	skew   := SKEW_2D * (coord.x + coord.y)
+	skewed := coord + skew
+
+	return _internal_noise_2d_unskewed_base(seed, skewed)
+}
+
+/*
+	2D Simplex noise, with Y pointing down the main diagonal.
+	Might be better for a 2D sandbox style game, where Y is vertical.
+	Probably slightly less optimal for heightmaps or continent maps,
+	unless your map is centered around an equator. It's a subtle
+	difference, but the option is here to make it an easy choice.
+*/
+noise_2d_improve_x :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+	// Skew transform and rotation baked into one.
+	xx := coord.x * ROOT_2_OVER_2
+	yy := coord.y * (ROOT_2_OVER_2 * (1 + 2 * SKEW_2D))
+	return _internal_noise_2d_unskewed_base(seed, Vec2{yy + xx, yy - xx})
+}
+
+
+/*
+	3D OpenSimplex2 noise, with better visual isotropy in (X, Y).
+	Recommended for 3D terrain and time-varied animations.
+	The Z coordinate should always be the "different" coordinate in whatever your use case is.
+	If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, z, Y)` or use `noise_3d_xz_before_y`.
+	If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, y, Z)`.
+	For a time varied animation, call `noise_3d_improve_xz(x, y, T)`.
+*/
+noise_3d_improve_xy :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	/*
+		Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal,
+		and the planes formed by XY are moved far out of alignment with the cube faces.
+		Orthonormal rotation. Not a skew transform.
+	*/
+	xy := coord.x + coord.y
+	s2 := xy * ROTATE_3D_ORTHOGONALIZER
+	zz := coord.z * ROOT_3_OVER_3
+
+	r := Vec3{coord.x + s2 + zz, coord.y + s2 + zz, xy * -ROOT_3_OVER_3 + zz}
+
+	// Evaluate both lattices to form a BCC lattice.
+	return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+	3D OpenSimplex2 noise, with better visual isotropy in (X, Z).
+	Recommended for 3D terrain and time-varied animations.
+	The Y coordinate should always be the "different" coordinate in whatever your use case is.
+	If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, Y, z)`.
+	If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, Z, y)` or use `noise_3d_improve_xy`.
+	For a time varied animation, call `noise_3d_improve_xz(x, T, y)` or use `noise_3d_improve_xy`.
+*/
+noise_3d_improve_xz :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	/*
+		Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal,
+		and the planes formed by XZ are moved far out of alignment with the cube faces.
+		Orthonormal rotation. Not a skew transform.
+	*/
+	xz := coord.x + coord.z
+	s2 := xz * ROTATE_3D_ORTHOGONALIZER
+	yy := coord.y * ROOT_3_OVER_3
+
+	r := Vec3{coord.x + s2 + yy, xz * -ROOT_3_OVER_3 + yy, coord.z + s2 + yy}
+	
+	// Evaluate both lattices to form a BCC lattice.
+	return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+	3D OpenSimplex2 noise, fallback rotation option
+	Use `noise_3d_improve_xy` or `noise_3d_improve_xz` instead, wherever appropriate.
+	They have less diagonal bias. This function's best use is as a fallback.
+*/
+noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+	/*
+		Re-orient the cubic lattices via rotation, to produce a familiar look.
+		Orthonormal rotation. Not a skew transform.
+	*/
+	bias   := FALLBACK_ROTATE_3D * (coord.x + coord.y + coord.z)
+	biased := bias - coord
+	// Evaluate both lattices to form a BCC lattice.
+	return _internal_noise_3d_unrotated_base(seed, biased)
+}
+
+
+/*
+	4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xy`
+	and W for an extra degree of freedom. W repeats eventually.
+	Recommended for time-varied animations which texture a 3D object (W=time)
+	in a space where Z is vertical.
+*/
+noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	xy := coord.x + coord.y
+	s2 := xy * -0.21132486540518699998
+	zz := coord.z * 0.28867513459481294226
+	ww := coord.w * 0.2236067977499788
+
+	xr, yr : f64 = coord.x + (zz + ww + s2), coord.y + (zz + ww + s2)
+	zr : f64 = xy * -0.57735026918962599998 + (zz + ww)
+	wr : f64 = coord.z * -0.866025403784439 + ww
+
+	return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+	4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xz`
+	and W for an extra degree of freedom. W repeats eventually.
+	Recommended for time-varied animations which texture a 3D object (W=time)
+	in a space where Y is vertical.
+*/
+noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	xz := coord.x + coord.z
+	s2 := xz * -0.21132486540518699998
+	yy := coord.y * 0.28867513459481294226
+	ww := coord.w * 0.2236067977499788
+
+	xr, zr : f64 = coord.x + (yy + ww + s2), coord.z + (yy + ww + s2)
+	yr := xz * -0.57735026918962599998 + (yy + ww)
+	wr := coord.y * -0.866025403784439 + ww
+
+	return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+	4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_fallback`
+	and W for an extra degree of freedom. W repeats eventually.
+	Recommended for time-varied animations which texture a 3D object (W=time)
+	where there isn't a clear distinction between horizontal and vertical
+*/
+noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	xyz := coord.x + coord.y + coord.z
+	ww  := coord.w * 0.2236067977499788
+	s2  := xyz * -0.16666666666666666 + ww
+
+	skewed := Vec4{coord.x + s2, coord.y + s2, coord.z + s2, -0.5 * xyz + ww}
+	return _internal_noise_4d_unskewed_base(seed, skewed)
+}
+
+/*
+	4D OpenSimplex2 noise, fallback lattice orientation.
+*/
+noise_4d_fallback :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+	// Get points for A4 lattice
+	skew := f64(SKEW_4D) * (coord.x + coord.y + coord.z + coord.w)
+	return _internal_noise_4d_unskewed_base(seed, coord + skew)
+}

+ 214 - 0
core/math/rand/exp.odin

@@ -0,0 +1,214 @@
+package rand
+
+import "core:math"
+
+// exp_float64 returns a exponential distribution in the range (0, max(f64)],
+// with an exponential distribution who rate parameter is 1 (lambda) and whose mean
+// is 1 (1/lambda).
+//
+// To produce a distribution with a differetn rate parameter, divide the result by
+// the desired rate parameter
+//
+//    "The Ziggurat Method for Generating Random Variables"
+//    Authors: George Marsaglia, Wai Wan Tsang
+//    Submitted: 2000-04-15. Published: 2000-10-02.
+//    https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
+//    https://www.jstatsoft.org/article/view/v005i08 [web page]
+//
+exp_float64 :: proc(r: ^Rand = nil) -> f64 {
+	re :: 7.69711747013104972
+
+	@(static)
+	ke := [256]u32{
+		0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990,
+		0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8,
+		0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78,
+		0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651,
+		0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca,
+		0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8,
+		0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea,
+		0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba,
+		0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed,
+		0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662,
+		0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3,
+		0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace,
+		0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6,
+		0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7,
+		0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415,
+		0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4,
+		0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36,
+		0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46,
+		0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac,
+		0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245,
+		0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52,
+		0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06,
+		0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0,
+		0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9,
+		0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76,
+		0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516,
+		0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289,
+		0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed,
+		0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb,
+		0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e,
+		0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a,
+		0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1,
+		0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b,
+		0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621,
+		0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d,
+		0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3,
+		0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73,
+		0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88,
+		0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a,
+		0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb,
+		0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176,
+		0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be,
+		0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192,
+		0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed,
+		0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936,
+		0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b,
+		0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4,
+		0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1,
+		0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482,
+		0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023,
+		0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d,
+		0xe6da6ecf,
+	}
+	@(static)
+	we := [256]f32{
+		2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11,
+		3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11,
+		5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11,
+		7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11,
+		9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10,
+		1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10,
+		1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10,
+		1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10,
+		1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10,
+		1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10,
+		1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10,
+		1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10,
+		1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10,
+		1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10,
+		2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10,
+		2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10,
+		2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10,
+		2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10,
+		2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10,
+		2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10,
+		2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10,
+		2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10,
+		2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10,
+		2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10,
+		3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10,
+		3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10,
+		3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10,
+		3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10,
+		3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10,
+		3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10,
+		3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10,
+		3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10,
+		3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10,
+		4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10,
+		4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10,
+		4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10,
+		4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10,
+		4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10,
+		4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10,
+		4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10,
+		4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10,
+		5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10,
+		5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10,
+		5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10,
+		5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10,
+		5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10,
+		5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10,
+		6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10,
+		6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10,
+		6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10,
+		6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10,
+		6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10,
+		7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10,
+		7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10,
+		7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10,
+		8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10,
+		8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10,
+		8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10,
+		9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10,
+		9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09,
+		1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09,
+		1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09,
+		1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09,
+		1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09,
+	}
+	@(static)
+	fe := [256]f32{
+		1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933,
+		0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686,
+		0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665,
+		0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967,
+		0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896,
+		0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092,
+		0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386,
+		0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495,
+		0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752,
+		0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325,
+		0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955,
+		0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694,
+		0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218,
+		0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763,
+		0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044,
+		0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796,
+		0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408,
+		0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928,
+		0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393,
+		0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625,
+		0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107,
+		0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878,
+		0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438,
+		0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682,
+		0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852,
+		0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479,
+		0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354,
+		0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494,
+		0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119,
+		0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624,
+		0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574,
+		0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672,
+		0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763,
+		0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816,
+		0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919,
+		0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274,
+		0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195,
+		0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106,
+		0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434,
+		0.062193416, 0.060783047, 0.059384305, 0.057997175,
+		0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236,
+		0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623,
+		0.043502413, 0.042254124, 0.041017443, 0.039792392,
+		0.038578995, 0.037377283, 0.036187284, 0.035009038,
+		0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566,
+		0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421,
+		0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867,
+		0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392,
+		0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414,
+		0.008780315, 0.007963077, 0.0071633533, 0.006381906,
+		0.0056196423, 0.0048776558, 0.004157295, 0.0034602648,
+		0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693,
+		0.00045413437,
+	}
+
+	for {
+		j := uint32(r)
+		i := j & 0xFF
+		x := f64(j) * f64(we[i])
+		if j < ke[i] {
+			return x
+		}
+		if i == 0 {
+			return re - math.ln(float64(r))
+		}
+		if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
+			return x
+		}
+	}
+}

+ 6 - 8
core/math/rand/normal.odin

@@ -2,6 +2,12 @@ package rand
 
 import "core:math"
 
+
+// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
+// with a standard normal distribution with a mean of 0 and standard deviation of 1.
+//
+// sample = norm_float64() * std_dev + mean
+//
 //
 //    Normal distribution
 //
@@ -11,12 +17,6 @@ import "core:math"
 //    https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
 //    https://www.jstatsoft.org/article/view/v005i08 [web page]
 //
-
-// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
-// with a standard normal distribution with a mean of 0 and standard deviation of 1.
-//
-// sample = norm_float64() * std_dev + mean
-//
 norm_float64 :: proc(r: ^Rand = nil) -> f64 {
 	rn :: 3.442619855899
 
@@ -49,7 +49,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
 		0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
 		0x7ba90bdc, 0x7a722176, 0x77d664e5,
 	}
-
 	@(static)
 	wn := [128]f32{
 		1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
@@ -85,7 +84,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
 		1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
 		1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
 	}
-
 	@(static)
 	fn := [128]f32{
 		1.00000000,  0.9635997,   0.9362827,   0.9130436,   0.89228165,

+ 8 - 8
core/math/rand/rand.odin

@@ -1,5 +1,7 @@
 package rand
 
+import "core:intrinsics"
+
 Rand :: struct {
 	state: u64,
 	inc:   u64,
@@ -7,9 +9,7 @@ Rand :: struct {
 
 
 @(private)
-_GLOBAL_SEED_DATA := 1234567890
-@(private)
-global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)))
+global_rand := create(u64(intrinsics.read_cycle_counter()))
 
 set_global_seed :: proc(seed: u64) {
 	init(&global_rand, seed)
@@ -70,7 +70,7 @@ int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
 	if n&(n-1) == 0 {
 		return int31(r) & (n-1)
 	}
-	max := i32((1<<31) - 1 - (1<<31)&u32(n))
+	max := i32((1<<31) - 1 - (1<<31)%u32(n))
 	v := int31(r)
 	for v > max {
 		v = int31(r)
@@ -85,7 +85,7 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
 	if n&(n-1) == 0 {
 		return int63(r) & (n-1)
 	}
-	max := i64((1<<63) - 1 - (1<<63)&u64(n))
+	max := i64((1<<63) - 1 - (1<<63)%u64(n))
 	v := int63(r)
 	for v > max {
 		v = int63(r)
@@ -100,7 +100,7 @@ int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
 	if n&(n-1) == 0 {
 		return int127(r) & (n-1)
 	}
-	max := i128((1<<63) - 1 - (1<<63)&u128(n))
+	max := i128((1<<127) - 1 - (1<<127)%u128(n))
 	v := int127(r)
 	for v > max {
 		v = int127(r)
@@ -142,8 +142,8 @@ read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
 }
 
 // perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
-perm :: proc(n: int, r: ^Rand = nil) -> []int {
-	m := make([]int, n)
+perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> []int {
+	m := make([]int, n, allocator)
 	for i := 0; i < n; i += 1 {
 		j := int_max(i+1, r)
 		m[i] = m[j]

+ 34 - 0
core/mem/doc.odin

@@ -0,0 +1,34 @@
+/*
+package mem implements various types of allocators.
+
+
+An example of how to use the `Tracking_Allocator` to track subsequent allocations
+in your program and report leaks and bad frees:
+
+```odin
+package foo
+
+import "core:mem"
+import "core:fmt"
+
+_main :: proc() {
+   do stuff
+}
+
+main :: proc() {
+    track: mem.Tracking_Allocator
+    mem.tracking_allocator_init(&track, context.allocator)
+    context.allocator = mem.tracking_allocator(&track)
+
+    _main()
+
+    for _, v in track.allocation_map {
+        fmt.printf("%v leaked %v bytes", v.location, v.size)
+    }
+    for bf in track.bad_free_array {
+        fmt.printf("%v allocation %p was freed badly", bf.location, bf.memory)
+    }
+}
+```
+*/
+package mem

+ 1 - 0
core/odin/ast/ast.odin

@@ -151,6 +151,7 @@ Comp_Lit :: struct {
 	open: tokenizer.Pos,
 	elems: []^Expr,
 	close: tokenizer.Pos,
+	tag: ^Expr,
 }
 
 

+ 12 - 2
core/odin/doc-format/doc_format.odin

@@ -11,7 +11,7 @@ String :: distinct Array(byte)
 
 Version_Type_Major :: 0
 Version_Type_Minor :: 2
-Version_Type_Patch :: 3
+Version_Type_Patch :: 4
 
 Version_Type :: struct {
 	major, minor, patch: u8,
@@ -77,9 +77,15 @@ Pkg :: struct {
 	flags:    Pkg_Flags,
 	docs:     String,
 	files:    Array(File_Index),
-	entities: Array(Entity_Index),
+	entries:  Array(Scope_Entry),
+}
+
+Scope_Entry :: struct {
+	name:   String,
+	entity: Entity_Index,
 }
 
+
 Entity_Kind :: enum u32le {
 	Invalid      = 0,
 	Constant     = 1,
@@ -89,6 +95,7 @@ Entity_Kind :: enum u32le {
 	Proc_Group   = 5,
 	Import_Name  = 6,
 	Library_Name = 7,
+	Builtin      = 8,
 }
 
 Entity_Flag :: enum u32le {
@@ -105,6 +112,9 @@ Entity_Flag :: enum u32le {
 
 	Type_Alias = 20,
 
+	Builtin_Pkg_Builtin    = 30,
+	Builtin_Pkg_Intrinsics = 31,
+
 	Var_Thread_Local = 40,
 	Var_Static       = 41,
 

+ 19 - 1
core/odin/parser/parser.odin

@@ -2273,6 +2273,24 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			return original_type
 
 		case "partial":
+			tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
+			tag.tok = tok
+			tag.name = name.text
+			original_expr := parse_expr(p, lhs)
+			expr := ast.unparen_expr(original_expr)
+			switch t in &expr.derived {
+			case ast.Comp_Lit:
+				t.tag = tag
+			case ast.Array_Type:
+				t.tag = tag
+				error(p, tok.pos, "#%s has been replaced with #sparse for non-contiguous enumerated array types", name.text)
+			case:
+				error(p, tok.pos, "expected a compound literal after #%s", name.text)
+
+			}
+			return original_expr
+
+		case "sparse":
 			tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
 			tag.tok = tok
 			tag.name = name.text
@@ -2319,7 +2337,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			return rt
 
 		case "force_inline", "force_no_inline":
-			return parse_inlining_operand(p, lhs, tok)
+			return parse_inlining_operand(p, lhs, name)
 		case:
 			expr := parse_expr(p, lhs)
 			te := ast.new(ast.Tag_Expr, tok.pos, expr.pos)

+ 10 - 2
core/os/os.odin

@@ -206,11 +206,19 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 		}
 	}
 
-	aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
+	aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
 		if p == nil {
 			return nil, nil
 		}
-		return aligned_alloc(new_size, new_alignment, p)
+
+		new_memory = aligned_alloc(new_size, new_alignment, p) or_return
+		
+		// NOTE: heap_resize does not zero the new memory, so we do it
+		if new_size > old_size {
+			new_region := mem.raw_data(new_memory[old_size:])
+			mem.zero(new_region, new_size - old_size)
+		}
+		return
 	}
 
 	switch mode {

+ 82 - 5
core/os/os_darwin.odin

@@ -290,13 +290,21 @@ foreign libc {
 	@(link_name="fstat64")          _unix_fstat         :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
 	@(link_name="readlink")         _unix_readlink      :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
 	@(link_name="access")           _unix_access        :: proc(path: cstring, mask: int) -> int ---
-	@(link_name="fdopendir$INODE64") _unix_fdopendir    :: proc(fd: Handle) -> Dir ---
+
+	@(link_name="fdopendir$INODE64") _unix_fdopendir_amd64 :: proc(fd: Handle) -> Dir ---
+	@(link_name="readdir_r$INODE64") _unix_readdir_r_amd64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+	@(link_name="fdopendir")         _unix_fdopendir_arm64 :: proc(fd: Handle) -> Dir ---
+	@(link_name="readdir_r")         _unix_readdir_r_arm64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
 	@(link_name="closedir")         _unix_closedir      :: proc(dirp: Dir) -> c.int ---
 	@(link_name="rewinddir")        _unix_rewinddir     :: proc(dirp: Dir) ---
-	@(link_name="readdir_r$INODE64") _unix_readdir_r    :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+	
 	@(link_name="fcntl")            _unix_fcntl         :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
 
-	@(link_name="fchmod") _unix_fchmod :: proc(fildes: Handle, mode: u16) -> c.int ---;
+	@(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int ---
+	@(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int ---
+
+	@(link_name="fchmod") _unix_fchmod :: proc(fildes: Handle, mode: u16) -> c.int ---
 
 	@(link_name="malloc")   _unix_malloc   :: proc(size: int) -> rawptr ---
 	@(link_name="calloc")   _unix_calloc   :: proc(num, size: int) -> rawptr ---
@@ -307,11 +315,19 @@ foreign libc {
 	@(link_name="chdir")    _unix_chdir    :: proc(buf: cstring) -> c.int ---
 	@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
 
-	@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---;
+	@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
 
 	@(link_name="exit")    _unix_exit :: proc(status: c.int) -> ! ---
 }
 
+when ODIN_ARCH != "arm64" {
+	_unix_fdopendir :: proc {_unix_fdopendir_amd64}
+	_unix_readdir_r :: proc {_unix_readdir_r_amd64}
+} else {
+	_unix_fdopendir :: proc {_unix_fdopendir_arm64}
+	_unix_readdir_r :: proc {_unix_readdir_r_arm64}
+}
+
 foreign dl {
 	@(link_name="dlopen")  _unix_dlopen  :: proc(filename: cstring, flags: int) -> rawptr ---
 	@(link_name="dlsym")   _unix_dlsym   :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
@@ -324,7 +340,7 @@ get_last_error :: proc() -> int {
 }
 
 get_last_error_string :: proc() -> string {
-	return cast(string)_darwin_string_error(cast(c.int)get_last_error());
+	return cast(string)_darwin_string_error(cast(c.int)get_last_error())
 }
 
 open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
@@ -412,6 +428,65 @@ is_path_separator :: proc(r: rune) -> bool {
 	return r == '/'
 }
 
+is_file_handle :: proc(fd: Handle) -> bool {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISREG(cast(u32)s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+	s: OS_Stat
+	err: Errno
+	if follow_links {
+		s, err = _stat(path)
+	} else {
+		s, err = _lstat(path)
+	}
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISREG(cast(u32)s.mode)
+}
+
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISDIR(cast(u32)s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+	s: OS_Stat
+	err: Errno
+	if follow_links {
+		s, err = _stat(path)
+	} else {
+		s, err = _lstat(path)
+	}
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISDIR(cast(u32)s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+
+rename :: proc(old: string, new: string) -> bool {
+	old_cstr := strings.clone_to_cstring(old, context.temp_allocator)
+	new_cstr := strings.clone_to_cstring(new, context.temp_allocator)
+	return _unix_rename(old_cstr, new_cstr) != -1 
+}
+
+remove :: proc(path: string) -> bool {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	return _unix_remove(path_cstr) != -1 
+}
 
 @private
 _stat :: proc(path: string) -> (OS_Stat, Errno) {
@@ -553,6 +628,8 @@ heap_alloc :: proc(size: int) -> rawptr {
 	return _unix_calloc(1, size)
 }
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+	// POSIX platforms. Ensure your caller takes this into account.
 	return _unix_realloc(ptr, new_size)
 }
 heap_free :: proc(ptr: rawptr) {

+ 2 - 0
core/os/os_freebsd.odin

@@ -378,6 +378,8 @@ heap_alloc :: proc(size: int) -> rawptr {
 }
 
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+	// POSIX platforms. Ensure your caller takes this into account.
 	return _unix_realloc(ptr, c.size_t(new_size));
 }
 

+ 2 - 0
core/os/os_linux.odin

@@ -727,6 +727,8 @@ heap_alloc :: proc(size: int) -> rawptr {
 }
 
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+	// POSIX platforms. Ensure your caller takes this into account.
 	return _unix_realloc(ptr, c.size_t(new_size))
 }
 

+ 1 - 2
core/path/filepath/match.odin

@@ -227,11 +227,10 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
 		return m[:], .None
 	}
 
-	temp_buf: [8]byte
-
 	dir, file := split(pattern)
 	volume_len := 0
 	when ODIN_OS == .Windows {
+		temp_buf: [8]byte
 		volume_len, dir = clean_glob_path_windows(dir, temp_buf[:])
 	} else {
 		dir = clean_glob_path(dir)

+ 1 - 1
core/path/filepath/walk.odin

@@ -71,7 +71,7 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_
 
 @(private)
 read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) {
-	f, err := os.open(dir_name)
+	f, err := os.open(dir_name, os.O_RDONLY)
 	if err != 0 {
 		return nil, err
 	}

+ 1 - 1
core/reflect/reflect.odin

@@ -234,7 +234,7 @@ is_nil :: proc(v: any) -> bool {
 		return true
 	}
 	data := as_bytes(v)
-	if data != nil {
+	if data == nil {
 		return true
 	}
 	for v in data {

+ 3 - 0
core/reflect/types.odin

@@ -472,6 +472,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
 		write_type(w, info.elem,             &n) or_return
 
 	case Type_Info_Enumerated_Array:
+		if info.is_sparse {
+			io.write_string(w, "#sparse", &n) or_return
+		}
 		io.write_string(w, "[",   &n) or_return
 		write_type(w, info.index, &n) or_return
 		io.write_string(w, "]",   &n) or_return

+ 1 - 0
core/runtime/core.odin

@@ -95,6 +95,7 @@ Type_Info_Enumerated_Array :: struct {
 	count:     int,
 	min_value: Type_Info_Enum_Value,
 	max_value: Type_Info_Enum_Value,
+	is_sparse: bool,
 }
 Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int}
 Type_Info_Slice         :: struct {elem: ^Type_Info, elem_size: int}

+ 4 - 0
core/runtime/core_builtin.odin

@@ -614,6 +614,10 @@ raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_str
 @(disabled=ODIN_DISABLE_ASSERT)
 assert :: proc(condition: bool, message := "", loc := #caller_location) {
 	if !condition {
+		// NOTE(bill): This is wrapped in a procedure call
+		// to improve performance to make the CPU not
+		// execute speculatively, making it about an order of
+		// magnitude faster
 		proc(message: string, loc: Source_Code_Location) {
 			p := context.assertion_failure_proc
 			if p == nil {

+ 2 - 4
core/runtime/internal.odin

@@ -37,10 +37,8 @@ bswap_64 :: proc "contextless" (x: u64) -> u64 {
 
 bswap_128 :: proc "contextless" (x: u128) -> u128 {
 	z := transmute([4]u32)x
-	z[0] = bswap_32(z[3])
-	z[1] = bswap_32(z[2])
-	z[2] = bswap_32(z[1])
-	z[3] = bswap_32(z[0])
+	z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
+	z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
 	return transmute(u128)z
 }
 

+ 18 - 5
core/runtime/print.odin

@@ -143,11 +143,21 @@ print_int     :: proc "contextless" (x: int)     { print_i64(i64(x)) }
 
 print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
 	print_string(file_path)
-	print_byte('(')
-	print_u64(u64(line))
-	print_byte(':')
-	print_u64(u64(column))
-	print_byte(')')
+	when ODIN_ERROR_POS_STYLE == .Default {
+		print_byte('(')
+		print_u64(u64(line))
+		print_byte(':')
+		print_u64(u64(column))
+		print_byte(')')
+	} else when ODIN_ERROR_POS_STYLE == .Unix {
+		print_byte(':')
+		print_u64(u64(line))
+		print_byte(':')
+		print_u64(u64(column))
+		print_byte(':')
+	} else {
+		#panic("unhandled ODIN_ERROR_POS_STYLE")
+	}
 }
 print_typeid :: proc "contextless" (id: typeid) {
 	if id == nil {
@@ -250,6 +260,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
 		print_type(info.elem)
 
 	case Type_Info_Enumerated_Array:
+		if info.is_sparse {
+			print_string("#sparse")
+		}
 		print_byte('[')
 		print_type(info.index)
 		print_byte(']')

+ 12 - 12
core/slice/slice.odin

@@ -305,21 +305,21 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -
 }
 
 scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U)->V, allocator := context.allocator) -> []V {
-  if len(s) == 0 { return {} }
+	if len(s) == 0 { return {} }
 
-  res := make([]V, len(s), allocator)
-  p := as_ptr(s)
-  q := as_ptr(res)
-  r := initializer
+	res := make([]V, len(s), allocator)
+	p := as_ptr(s)
+	q := as_ptr(res)
+	r := initializer
 
-  for l := len(s); l > 0; l -= 1 {
-    r = f(r, p[0])
-    q[0] = r
-    p = p[1:]
-    q = q[1:]
-  }
+	for l := len(s); l > 0; l -= 1 {
+		r = f(r, p[0])
+		q[0] = r
+		p = p[1:]
+		q = q[1:]
+	}
 
-  return res
+	return res
 }
 
 

+ 31 - 488
core/slice/sort.odin

@@ -1,10 +1,5 @@
 package slice
 
-import "core:intrinsics"
-_ :: intrinsics
-
-ORD :: intrinsics.type_is_ordered
-
 Ordering :: enum {
 	Less    = -1,
 	Equal   =  0,
@@ -38,7 +33,7 @@ cmp_proc :: proc($E: typeid) -> (proc(E, E) -> Ordering) where ORD(E) {
 sort :: proc(data: $T/[]$E) where ORD(E) {
 	when size_of(E) != 0 {
 		if n := len(data); n > 1 {
-			_quick_sort(data, 0, n, _max_depth(n))
+			_quick_sort_general(data, 0, n, _max_depth(n), struct{}{}, .Ordered)
 		}
 	}
 }
@@ -48,7 +43,7 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
 sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
 	when size_of(E) != 0 {
 		if n := len(data); n > 1 {
-			_quick_sort_less(data, 0, n, _max_depth(n), less)
+			_quick_sort_general(data, 0, n, _max_depth(n), less, .Less)
 		}
 	}
 }
@@ -56,7 +51,33 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
 sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
 	when size_of(E) != 0 {
 		if n := len(data); n > 1 {
-			_quick_sort_cmp(data, 0, n, _max_depth(n), cmp)
+			_quick_sort_general(data, 0, n, _max_depth(n), cmp, .Cmp)
+		}
+	}
+}
+
+// stable_sort sorts a slice
+stable_sort :: proc(data: $T/[]$E) where ORD(E) {
+	when size_of(E) != 0 {
+		if n := len(data); n > 1 {
+			_stable_sort_general(data, struct{}{}, .Ordered)
+		}
+	}
+}
+
+// stable_sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
+stable_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
+	when size_of(E) != 0 {
+		if n := len(data); n > 1 {
+			_stable_sort_general(data, less, .Less)
+		}
+	}
+}
+
+stable_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
+	when size_of(E) != 0 {
+		if n := len(data); n > 1 {
+			_stable_sort_general(data, cmp, .Cmp)
 		}
 	}
 }
@@ -79,6 +100,7 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
 	return true
 }
 
+is_sorted_by_cmp :: is_sorted_cmp
 is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
 	for i := len(array)-1; i > 0; i -= 1 {
 		if cmp(array[i], array[i-1]) == .Equal {
@@ -140,489 +162,10 @@ is_sorted_by_key :: proc(array: $T/[]$E, key: proc(E) -> $K) -> bool where ORD(K
 	return true
 }
 
-
-
 @(private)
-_max_depth :: proc(n: int) -> int { // 2*ceil(log2(n+1))
-	depth: int
+_max_depth :: proc(n: int) -> (depth: int) { // 2*ceil(log2(n+1))
 	for i := n; i > 0; i >>= 1 {
 		depth += 1
 	}
 	return depth * 2
 }
-
-@(private)
-_quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) #no_bounds_check {
-	median3 :: proc(data: T, m1, m0, m2: int) #no_bounds_check {
-		if data[m1] < data[m0] {
-			swap(data, m1, m0)
-		}
-		if data[m2] < data[m1] {
-			swap(data, m2, m1)
-			if data[m1] < data[m0] {
-				swap(data, m1, m0)
-			}
-		}
-	}
-
-	do_pivot :: proc(data: T, lo, hi: int) -> (midlo, midhi: int) #no_bounds_check {
-		m := int(uint(lo+hi)>>1)
-		if hi-lo > 40 {
-			s := (hi-lo)/8
-			median3(data, lo, lo+s, lo+s*2)
-			median3(data, m, m-s, m+s)
-			median3(data, hi-1, hi-1-s, hi-1-s*2)
-		}
-		median3(data, lo, m, hi-1)
-
-
-		pivot := lo
-		a, c := lo+1, hi-1
-
-		for ; a < c && data[a] < data[pivot]; a += 1 {
-		}
-		b := a
-
-		for {
-			for ; b < c && !(data[pivot] < data[b]); b += 1 { // data[b] <= pivot
-			}
-			for ; b < c && data[pivot] < data[c-1]; c -=1 { // data[c-1] > pivot
-			}
-			if b >= c {
-				break
-			}
-
-			swap(data, b, c-1)
-			b += 1
-			c -= 1
-		}
-
-		protect := hi-c < 5
-		if !protect && hi-c < (hi-lo)/4 {
-			dups := 0
-			if !(data[pivot] < data[hi-1]) {
-				swap(data, c, hi-1)
-				c += 1
-				dups += 1
-			}
-			if !(data[b-1] < data[pivot]) {
-				b -= 1
-				dups += 1
-			}
-
-			if !(data[m] < data[pivot]) {
-				swap(data, m, b-1)
-				b -= 1
-				dups += 1
-			}
-			protect = dups > 1
-		}
-		if protect {
-			for {
-				for ; a < b && !(data[b-1] < data[pivot]); b -= 1 {
-				}
-				for ; a < b && data[a] < data[pivot]; a += 1 {
-				}
-				if a >= b {
-					break
-				}
-				swap(data, a, b-1)
-				a += 1
-				b -= 1
-			}
-		}
-		swap(data, pivot, b-1)
-		return b-1, c
-	}
-
-
-	a, b, max_depth := a, b, max_depth
-
-	if b-a > 12 { // only use shell sort for lengths <= 12
-		if max_depth == 0 {
-			_heap_sort(data, a, b)
-			return
-		}
-		max_depth -= 1
-		mlo, mhi := do_pivot(data, a, b)
-		if mlo-a < b-mhi {
-			_quick_sort(data, a, mlo, max_depth)
-			a = mhi
-		} else {
-			_quick_sort(data, mhi, b, max_depth)
-			b = mlo
-		}
-	}
-	if b-a > 1 {
-		// Shell short with gap 6
-		for i in a+6..<b {
-			if data[i] < data[i-6] {
-				swap(data, i, i-6)
-			}
-		}
-		_insertion_sort(data, a, b)
-	}
-}
-
-@(private)
-_insertion_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check {
-	for i in a+1..<b {
-		for j := i; j > a && data[j] < data[j-1]; j -= 1 {
-			swap(data, j, j-1)
-		}
-	}
-}
-
-@(private)
-_heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check {
-	sift_down :: proc(data: T, lo, hi, first: int) #no_bounds_check {
-		root := lo
-		for {
-			child := 2*root + 1
-			if child >= hi {
-				break
-			}
-			if child+1 < hi && data[first+child] < data[first+child+1] {
-				child += 1
-			}
-			if !(data[first+root] < data[first+child]) {
-				return
-			}
-			swap(data, first+root, first+child)
-			root = child
-		}
-	}
-
-
-	first, lo, hi := a, 0, b-a
-
-	for i := (hi-1)/2; i >= 0; i -= 1 {
-		sift_down(data, i, hi, first)
-	}
-
-	for i := hi-1; i >= 0; i -= 1 {
-		swap(data, first, first+i)
-		sift_down(data, lo, i, first)
-	}
-}
-
-
-
-
-
-
-@(private)
-_quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) #no_bounds_check {
-	median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) #no_bounds_check {
-		if less(data[m1], data[m0]) {
-			swap(data, m1, m0)
-		}
-		if less(data[m2], data[m1]) {
-			swap(data, m2, m1)
-			if less(data[m1], data[m0]) {
-				swap(data, m1, m0)
-			}
-		}
-	}
-
-	do_pivot :: proc(data: T, lo, hi: int, less: proc(i, j: E) -> bool) -> (midlo, midhi: int) #no_bounds_check {
-		m := int(uint(lo+hi)>>1)
-		if hi-lo > 40 {
-			s := (hi-lo)/8
-			median3(data, lo, lo+s, lo+s*2, less)
-			median3(data, m, m-s, m+s, less)
-			median3(data, hi-1, hi-1-s, hi-1-s*2, less)
-		}
-		median3(data, lo, m, hi-1, less)
-
-		pivot := lo
-		a, c := lo+1, hi-1
-
-		for ; a < c && less(data[a], data[pivot]); a += 1 {
-		}
-		b := a
-
-		for {
-			for ; b < c && !less(data[pivot], data[b]); b += 1 { // data[b] <= pivot
-			}
-			for ; b < c && less(data[pivot], data[c-1]); c -=1 { // data[c-1] > pivot
-			}
-			if b >= c {
-				break
-			}
-
-			swap(data, b, c-1)
-			b += 1
-			c -= 1
-		}
-
-		protect := hi-c < 5
-		if !protect && hi-c < (hi-lo)/4 {
-			dups := 0
-			if !less(data[pivot], data[hi-1]) {
-				swap(data, c, hi-1)
-				c += 1
-				dups += 1
-			}
-			if !less(data[b-1], data[pivot]) {
-				b -= 1
-				dups += 1
-			}
-
-			if !less(data[m], data[pivot]) {
-				swap(data, m, b-1)
-				b -= 1
-				dups += 1
-			}
-			protect = dups > 1
-		}
-		if protect {
-			for {
-				for ; a < b && !less(data[b-1], data[pivot]); b -= 1 {
-				}
-				for ; a < b && less(data[a], data[pivot]); a += 1 {
-				}
-				if a >= b {
-					break
-				}
-				swap(data, a, b-1)
-				a += 1
-				b -= 1
-			}
-		}
-		swap(data, pivot, b-1)
-		return b-1, c
-	}
-
-
-	a, b, max_depth := a, b, max_depth
-
-	if b-a > 12 { // only use shell sort for lengths <= 12
-		if max_depth == 0 {
-			_heap_sort_less(data, a, b, less)
-			return
-		}
-		max_depth -= 1
-		mlo, mhi := do_pivot(data, a, b, less)
-		if mlo-a < b-mhi {
-			_quick_sort_less(data, a, mlo, max_depth, less)
-			a = mhi
-		} else {
-			_quick_sort_less(data, mhi, b, max_depth, less)
-			b = mlo
-		}
-	}
-	if b-a > 1 {
-		// Shell short with gap 6
-		for i in a+6..<b {
-			if less(data[i], data[i-6]) {
-				swap(data, i, i-6)
-			}
-		}
-		_insertion_sort_less(data, a, b, less)
-	}
-}
-
-@(private)
-_insertion_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check {
-	for i in a+1..<b {
-		for j := i; j > a && less(data[j], data[j-1]); j -= 1 {
-			swap(data, j, j-1)
-		}
-	}
-}
-
-@(private)
-_heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check {
-	sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) #no_bounds_check {
-		root := lo
-		for {
-			child := 2*root + 1
-			if child >= hi {
-				break
-			}
-			if child+1 < hi && less(data[first+child], data[first+child+1]) {
-				child += 1
-			}
-			if !less(data[first+root], data[first+child]) {
-				return
-			}
-			swap(data, first+root, first+child)
-			root = child
-		}
-	}
-
-
-	first, lo, hi := a, 0, b-a
-
-	for i := (hi-1)/2; i >= 0; i -= 1 {
-		sift_down(data, i, hi, first, less)
-	}
-
-	for i := hi-1; i >= 0; i -= 1 {
-		swap(data, first, first+i)
-		sift_down(data, lo, i, first, less)
-	}
-}
-
-
-
-
-
-
-@(private)
-_quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
-	median3 :: proc(data: T, m1, m0, m2: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
-		if cmp(data[m1], data[m0]) == .Less {
-			swap(data, m1, m0)
-		}
-		if cmp(data[m2], data[m1]) == .Less {
-			swap(data, m2, m1)
-			if cmp(data[m1], data[m0]) == .Less {
-				swap(data, m1, m0)
-			}
-		}
-	}
-
-	do_pivot :: proc(data: T, lo, hi: int, cmp: proc(i, j: E) -> Ordering) -> (midlo, midhi: int) #no_bounds_check {
-		m := int(uint(lo+hi)>>1)
-		if hi-lo > 40 {
-			s := (hi-lo)/8
-			median3(data, lo, lo+s, lo+s*2, cmp)
-			median3(data, m, m-s, m+s, cmp)
-			median3(data, hi-1, hi-1-s, hi-1-s*2, cmp)
-		}
-		median3(data, lo, m, hi-1, cmp)
-
-		pivot := lo
-		a, c := lo+1, hi-1
-
-		for ; a < c && cmp(data[a], data[pivot]) == .Less; a += 1 {
-		}
-		b := a
-
-		for {
-			for ; b < c && cmp(data[pivot], data[b]) >= .Equal; b += 1 { // data[b] <= pivot
-			}
-			for ; b < c && cmp(data[pivot], data[c-1]) == .Less; c -=1 { // data[c-1] > pivot
-			}
-			if b >= c {
-				break
-			}
-
-			swap(data, b, c-1)
-			b += 1
-			c -= 1
-		}
-
-		protect := hi-c < 5
-		if !protect && hi-c < (hi-lo)/4 {
-			dups := 0
-			if cmp(data[pivot], data[hi-1]) != .Less {
-				swap(data, c, hi-1)
-				c += 1
-				dups += 1
-			}
-			if cmp(data[b-1], data[pivot]) != .Less {
-				b -= 1
-				dups += 1
-			}
-
-			if cmp(data[m], data[pivot]) != .Less {
-				swap(data, m, b-1)
-				b -= 1
-				dups += 1
-			}
-			protect = dups > 1
-		}
-		if protect {
-			for {
-				for ; a < b && cmp(data[b-1], data[pivot]) >= .Equal; b -= 1 {
-				}
-				for ; a < b && cmp(data[a], data[pivot]) == .Less; a += 1 {
-				}
-				if a >= b {
-					break
-				}
-				swap(data, a, b-1)
-				a += 1
-				b -= 1
-			}
-		}
-		swap(data, pivot, b-1)
-		return b-1, c
-	}
-
-
-	a, b, max_depth := a, b, max_depth
-
-	if b-a > 12 { // only use shell sort for lengths <= 12
-		if max_depth == 0 {
-			_heap_sort_cmp(data, a, b, cmp)
-			return
-		}
-		max_depth -= 1
-		mlo, mhi := do_pivot(data, a, b, cmp)
-		if mlo-a < b-mhi {
-			_quick_sort_cmp(data, a, mlo, max_depth, cmp)
-			a = mhi
-		} else {
-			_quick_sort_cmp(data, mhi, b, max_depth, cmp)
-			b = mlo
-		}
-	}
-	if b-a > 1 {
-		// Shell short with gap 6
-		for i in a+6..<b {
-			if cmp(data[i], data[i-6]) == .Less {
-				swap(data, i, i-6)
-			}
-		}
-		_insertion_sort_cmp(data, a, b, cmp)
-	}
-}
-
-@(private)
-_insertion_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
-	for i in a+1..<b {
-		for j := i; j > a && cmp(data[j], data[j-1]) == .Less; j -= 1 {
-			swap(data, j, j-1)
-		}
-	}
-}
-
-@(private)
-_heap_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
-	sift_down :: proc(data: T, lo, hi, first: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
-		root := lo
-		for {
-			child := 2*root + 1
-			if child >= hi {
-				break
-			}
-			if child+1 < hi && cmp(data[first+child], data[first+child+1]) == .Less {
-				child += 1
-			}
-			if cmp(data[first+root], data[first+child]) >= .Equal {
-				return
-			}
-			swap(data, first+root, first+child)
-			root = child
-		}
-	}
-
-
-	first, lo, hi := a, 0, b-a
-
-	for i := (hi-1)/2; i >= 0; i -= 1 {
-		sift_down(data, i, hi, first, cmp)
-	}
-
-	for i := hi-1; i >= 0; i -= 1 {
-		swap(data, first, first+i)
-		sift_down(data, lo, i, first, cmp)
-	}
-}
-
-
-

+ 200 - 0
core/slice/sort_private.odin

@@ -0,0 +1,200 @@
+//+private
+package slice
+
+import "core:intrinsics"
+_ :: intrinsics
+
+ORD :: intrinsics.type_is_ordered
+
+Sort_Kind :: enum {
+	Ordered,
+	Less,
+	Cmp,
+}
+
+_quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
+	less :: #force_inline proc(a, b: E, call: P) -> bool {
+		when KIND == .Ordered {
+			return a < b
+		} else when KIND == .Less {
+			return call(a, b)
+		} else when KIND == .Cmp {
+			return call(a, b) == .Less
+		} else {
+			#panic("unhandled Sort_Kind")
+		}
+	}
+
+	insertion_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check {
+		for i in a+1..<b {
+			for j := i; j > a && less(data[j], data[j-1], call); j -= 1 {
+				swap(data, j, j-1)
+			}
+		}
+	}
+
+	heap_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check {
+		sift_down :: proc(data: T, lo, hi, first: int, call: P) #no_bounds_check {
+			root := lo
+			for {
+				child := 2*root + 1
+				if child >= hi {
+					break
+				}
+				if child+1 < hi && less(data[first+child], data[first+child+1], call) {
+					child += 1
+				}
+				if !less(data[first+root], data[first+child], call) {
+					return
+				}
+				swap(data, first+root, first+child)
+				root = child
+			}
+		}
+
+
+		first, lo, hi := a, 0, b-a
+
+		for i := (hi-1)/2; i >= 0; i -= 1 {
+			sift_down(data, i, hi, first, call)
+		}
+
+		for i := hi-1; i >= 0; i -= 1 {
+			swap(data, first, first+i)
+			sift_down(data, lo, i, first, call)
+		}
+	}
+
+	median3 :: proc(data: T, m1, m0, m2: int, call: P) #no_bounds_check {
+		if less(data[m1], data[m0], call) {
+			swap(data, m1, m0)
+		}
+		if less(data[m2], data[m1], call) {
+			swap(data, m2, m1)
+			if less(data[m1], data[m0], call) {
+				swap(data, m1, m0)
+			}
+		}
+	}
+
+	do_pivot :: proc(data: T, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check {
+		m := int(uint(lo+hi)>>1)
+		if hi-lo > 40 {
+			s := (hi-lo)/8
+			median3(data, lo, lo+s, lo+s*2, call)
+			median3(data, m, m-s, m+s, call)
+			median3(data, hi-1, hi-1-s, hi-1-s*2, call)
+		}
+		median3(data, lo, m, hi-1, call)
+
+		pivot := lo
+		a, c := lo+1, hi-1
+
+
+		for ; a < c && less(data[a], data[pivot], call); a += 1 {
+		}
+		b := a
+
+		for {
+			for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot
+			}
+			for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot
+			}
+			if b >= c {
+				break
+			}
+
+			swap(data, b, c-1)
+			b += 1
+			c -= 1
+		}
+
+		protect := hi-c < 5
+		if !protect && hi-c < (hi-lo)/4 {
+			dups := 0
+			if !less(data[pivot], data[hi-1], call) {
+				swap(data, c, hi-1)
+				c += 1
+				dups += 1
+			}
+			if !less(data[b-1], data[pivot], call) {
+				b -= 1
+				dups += 1
+			}
+
+			if !less(data[m], data[pivot], call) {
+				swap(data, m, b-1)
+				b -= 1
+				dups += 1
+			}
+			protect = dups > 1
+		}
+		if protect {
+			for {
+				for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 {
+				}
+				for ; a < b && less(data[a], data[pivot], call); a += 1 {
+				}
+				if a >= b {
+					break
+				}
+				swap(data, a, b-1)
+				a += 1
+				b -= 1
+			}
+		}
+		swap(data, pivot, b-1)
+		return b-1, c
+	}
+
+
+	a, b, max_depth := a, b, max_depth
+
+	if b-a > 12 { // only use shell sort for lengths <= 12
+		if max_depth == 0 {
+			heap_sort(data, a, b, call)
+			return
+		}
+		max_depth -= 1
+		mlo, mhi := do_pivot(data, a, b, call)
+		if mlo-a < b-mhi {
+			_quick_sort_general(data, a, mlo, max_depth, call, KIND)
+			a = mhi
+		} else {
+			_quick_sort_general(data, mhi, b, max_depth, call, KIND)
+			b = mlo
+		}
+	}
+	if b-a > 1 {
+		// Shell short with gap 6
+		for i in a+6..<b {
+			if less(data[i], data[i-6], call) {
+				swap(data, i, i-6)
+			}
+		}
+		insertion_sort(data, a, b, call)
+	}
+}
+
+
+// merge sort
+_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
+	less :: #force_inline proc(a, b: E, call: P) -> bool {
+		when KIND == .Ordered {
+			return a < b
+		} else when KIND == .Less {
+			return call(a, b)
+		} else when KIND == .Cmp {
+			return call(a, b) == .Less
+		} else {
+			#panic("unhandled Sort_Kind")
+		}
+	}
+
+	n := len(data)
+	for i in 1..<n {
+		for j := i; j > 0 && less(data[j], data[j-1], call); j -= 1 {
+			swap(data, j, j-1)
+		}
+	}
+}

+ 65 - 30
core/strings/strings.odin

@@ -298,13 +298,7 @@ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) ->
 
 
 @private
-_split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: string, ok: bool) {
-	s, n := s, n
-
-	if n == 0 {
-		return
-	}
-
+_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
 	if sep == "" {
 		res = s[:]
 		ok = true
@@ -312,47 +306,88 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: stri
 		return
 	}
 
-	if n < 0 {
-		n = count(s^, sep) + 1
-	}
-
-	n -= 1
-
-	i := 0
-	for ; i < n; i += 1 {
-		m := index(s^, sep)
-		if m < 0 {
-			break
-		}
+	m := index(s^, sep)
+	if m < 0 {
+		// not found
+		res = s[:]
+		ok = res != ""
+		s^ = s[len(s):]
+	} else {
 		res = s[:m+sep_save]
 		ok = true
 		s^ = s[m+len(sep):]
-		return
 	}
-	res = s[:]
-	ok = res != ""
-	s^ = s[len(s):]
 	return
 }
 
 
 split_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
-	return _split_iterator(s, sep, 0, -1)
+	return _split_iterator(s, sep, 0)
 }
 
-split_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
-	return _split_iterator(s, sep, 0, n)
+split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
+	return _split_iterator(s, sep, len(sep))
 }
 
-split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
-	return _split_iterator(s, sep, len(sep), -1)
+
+@(private)
+_trim_cr :: proc(s: string) -> string {
+	n := len(s)
+	if n > 0 {
+		if s[n-1] == '\r' {
+			return s[:n-1]
+		}
+	}
+	return s
+}
+
+split_lines :: proc(s: string, allocator := context.allocator) -> []string {
+	sep :: "\n"
+	lines := _split(s, sep, 0, -1, allocator)
+	for line in &lines {
+		line = _trim_cr(line)
+	}
+	return lines
 }
 
-split_after_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
-	return _split_iterator(s, sep, len(sep), n)
+split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string {
+	sep :: "\n"
+	lines := _split(s, sep, 0, n, allocator)
+	for line in &lines {
+		line = _trim_cr(line)
+	}
+	return lines
 }
 
+split_lines_after :: proc(s: string, allocator := context.allocator) -> []string {
+	sep :: "\n"
+	lines := _split(s, sep, len(sep), -1, allocator)
+	for line in &lines {
+		line = _trim_cr(line)
+	}
+	return lines
+}
 
+split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string {
+	sep :: "\n"
+	lines := _split(s, sep, len(sep), n, allocator)
+	for line in &lines {
+		line = _trim_cr(line)
+	}
+	return lines
+}
+
+split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
+	sep :: "\n"
+	line = _split_iterator(s, sep, 0) or_return
+	return _trim_cr(line), true
+}
+
+split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
+	sep :: "\n"
+	line = _split_iterator(s, sep, len(sep)) or_return
+	return _trim_cr(line), true
+}
 
 
 

+ 1 - 1
core/testing/runner_other.odin

@@ -6,7 +6,7 @@ import "core:time"
 
 run_internal_test :: proc(t: ^T, it: Internal_Test) {
 	// TODO(bill): Catch panics on other platforms
-	it.p(t);
+	it.p(t)
 }
 
 _fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {

+ 0 - 2
core/thread/thread_unix.odin

@@ -167,8 +167,6 @@ _join_multiple :: proc(threads: ..^Thread) {
 
 _destroy :: proc(t: ^Thread) {
 	_join(t)
-	sync.condition_destroy(&t.start_gate)
-	sync.mutex_destroy(&t.start_mutex)
 	t.unix_thread = {}
 	free(t, t.creation_allocator)
 }

+ 82 - 4
examples/all/all_main.odin

@@ -5,25 +5,65 @@ package all
 
 import bufio          "core:bufio"
 import bytes          "core:bytes"
+
 import c              "core:c"
 import libc           "core:c/libc"
+
 import compress       "core:compress"
 import gzip           "core:compress/gzip"
 import zlib           "core:compress/zlib"
-import container      "core:container"
+
+import bit_array      "core:container/bit_array"
+import priority_queue "core:container/priority_queue"
+import queue          "core:container/queue"
+import small_array    "core:container/small_array"
+import lru            "core:container/lru"
+
+import crypto           "core:crypto"
+import blake            "core:crypto/blake"
+import blake2b          "core:crypto/blake2b"
+import blake2s          "core:crypto/blake2s"
+import chacha20         "core:crypto/chacha20"
+import chacha20poly1305 "core:crypto/chacha20poly1305"
+import gost             "core:crypto/gost"
+import groestl           "core:crypto/groestl"
+import haval            "core:crypto/haval"
+import jh               "core:crypto/jh"
+import keccak           "core:crypto/keccak"
+import md2              "core:crypto/md2"
+import md4              "core:crypto/md4"
+import md5              "core:crypto/md5"
+import poly1305         "core:crypto/poly1305"
+import ripemd           "core:crypto/ripemd"
+import sha1             "core:crypto/sha1"
+import sha2             "core:crypto/sha2"
+import sha3             "core:crypto/sha3"
+import shake            "core:crypto/shake"
+import sm3              "core:crypto/sm3"
+import streebog         "core:crypto/streebog"
+import tiger            "core:crypto/tiger"
+import tiger2           "core:crypto/tiger2"
+import crypto_util      "core:crypto/util"
+import whirlpool        "core:crypto/whirlpool"
+import x25519           "core:crypto/x25519"
+
 import dynlib         "core:dynlib"
-import encoding       "core:encoding"
+
 import base32         "core:encoding/base32"
 import base64         "core:encoding/base64"
 import csv            "core:encoding/csv"
 import hxa            "core:encoding/hxa"
 import json           "core:encoding/json"
+
 import fmt            "core:fmt"
 import hash           "core:hash"
+
 import image          "core:image"
 import png            "core:image/png"
+
 import io             "core:io"
 import log            "core:log"
+
 import math           "core:math"
 import big            "core:math/big"
 import bits           "core:math/bits"
@@ -32,16 +72,22 @@ import linalg         "core:math/linalg"
 import glm            "core:math/linalg/glsl"
 import hlm            "core:math/linalg/hlsl"
 import rand           "core:math/rand"
+
 import mem            "core:mem"
+// import virtual        "core:mem/virtual"
+
 import ast            "core:odin/ast"
 import doc_format     "core:odin/doc-format"
 import odin_format    "core:odin/format"
 import odin_parser    "core:odin/parser"
 import odin_printer   "core:odin/printer"
 import odin_tokenizer "core:odin/tokenizer"
+
 import os             "core:os"
+
 import slashpath      "core:path/slashpath"
 import filepath       "core:path/filepath"
+
 import reflect        "core:reflect"
 import runtime        "core:runtime"
 import slice          "core:slice"
@@ -50,9 +96,11 @@ import strconv        "core:strconv"
 import strings        "core:strings"
 import sync           "core:sync"
 import sync2          "core:sync/sync2"
+import testing        "core:testing"
 import scanner        "core:text/scanner"
 import thread         "core:thread"
 import time           "core:time"
+
 import unicode        "core:unicode"
 import utf8           "core:unicode/utf8"
 import utf16          "core:unicode/utf16"
@@ -67,9 +115,38 @@ _ :: libc
 _ :: compress
 _ :: gzip
 _ :: zlib
-_ :: container
+_ :: bit_array
+_ :: priority_queue
+_ :: queue
+_ :: small_array
+_ :: crypto
+_ :: blake
+_ :: blake2b
+_ :: blake2s
+_ :: chacha20
+_ :: chacha20poly1305
+_ :: gost
+_ :: groestl
+_ :: haval
+_ :: jh
+_ :: keccak
+_ :: md2
+_ :: md4
+_ :: md5
+_ :: poly1305
+_ :: ripemd
+_ :: sha1
+_ :: sha2
+_ :: sha3
+_ :: shake
+_ :: sm3
+_ :: streebog
+_ :: tiger
+_ :: tiger2
+_ :: crypto_util
+_ :: whirlpool
+_ :: x25519
 _ :: dynlib
-_ :: encoding
 _ :: base32
 _ :: base64
 _ :: csv
@@ -107,6 +184,7 @@ _ :: strconv
 _ :: strings
 _ :: sync
 _ :: sync2
+_ :: testing
 _ :: scanner
 _ :: thread
 _ :: time

+ 33 - 12
examples/all/all_vendor.odin

@@ -1,26 +1,47 @@
 //+build windows
 package all
 
-import glfw   "vendor:glfw"
-import gl     "vendor:OpenGL"
-import rl     "vendor:raylib"
-import PM     "vendor:portmidi"
+
+import botan     "vendor:botan"
+import ENet      "vendor:ENet"
+import gl        "vendor:OpenGL"
+import glfw      "vendor:glfw"
+import microui   "vendor:microui"
+import miniaudio "vendor:miniaudio"
+import PM        "vendor:portmidi"
+import rl        "vendor:raylib"
+
 import SDL    "vendor:sdl2"
-import IMG    "vendor:sdl2/image"
 import SDLNet "vendor:sdl2/net"
+import IMG    "vendor:sdl2/image"
 import MIX    "vendor:sdl2/mixer"
 import TTF    "vendor:sdl2/ttf"
-import vk     "vendor:vulkan"
-import ENet   "vendor:ENet"
 
-_ :: glfw
+import stb_easy_font "vendor:stb/easy_font"
+import stbi          "vendor:stb/image"
+import stbrp         "vendor:stb/rect_pack"
+import stbtt         "vendor:stb/truetype"
+import stb_vorbis    "vendor:stb/vorbis"
+
+import vk "vendor:vulkan"
+
+
+_ :: botan
+_ :: ENet
 _ :: gl
-_ :: rl
+_ :: glfw
+_ :: microui
+_ :: miniaudio
 _ :: PM
+_ :: rl
 _ :: SDL
-_ :: IMG
 _ :: SDLNet
+_ :: IMG
 _ :: MIX
 _ :: TTF
-_ :: vk
-_ :: ENet
+_ :: stb_easy_font
+_ :: stbi
+_ :: stbrp
+_ :: stbtt
+_ :: stb_vorbis
+_ :: vk

+ 8 - 8
examples/demo/demo.odin

@@ -1921,14 +1921,14 @@ constant_literal_expressions :: proc() {
 
 	fmt.println("-------")
 
-	Partial_Baz :: enum{A=5, B, C, D=16}
-	#assert(len(Partial_Baz) < len(#partial [Partial_Baz]int))
-	PARTIAL_ENUM_ARRAY_CONST :: #partial [Partial_Baz]int{.A ..= .C = 1, .D = 16}
-
-	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.A])
-	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.B])
-	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.C])
-	fmt.println(PARTIAL_ENUM_ARRAY_CONST[.D])
+	Sparse_Baz :: enum{A=5, B, C, D=16}
+	#assert(len(Sparse_Baz) < len(#sparse[Sparse_Baz]int))
+	SPARSE_ENUM_ARRAY_CONST :: #sparse[Sparse_Baz]int{.A ..= .C = 1, .D = 16}
+
+	fmt.println(SPARSE_ENUM_ARRAY_CONST[.A])
+	fmt.println(SPARSE_ENUM_ARRAY_CONST[.B])
+	fmt.println(SPARSE_ENUM_ARRAY_CONST[.C])
+	fmt.println(SPARSE_ENUM_ARRAY_CONST[.D])
 
 	fmt.println("-------")
 

+ 52 - 1
src/build_settings.cpp

@@ -165,6 +165,13 @@ enum TimingsExportFormat : i32 {
 	TimingsExportCSV         = 2,
 };
 
+enum ErrorPosStyle {
+	ErrorPosStyle_Default, // path(line:column) msg
+	ErrorPosStyle_Unix,    // path:line:column: msg
+
+	ErrorPosStyle_COUNT
+};
+
 // This stores the information for the specify architecture of this build
 struct BuildContext {
 	// Constants
@@ -175,7 +182,9 @@ struct BuildContext {
 	String ODIN_ROOT;    // Odin ROOT
 	bool   ODIN_DEBUG;   // Odin in debug mode
 	bool   ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
-bool   ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+	bool   ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+
+	ErrorPosStyle ODIN_ERROR_POS_STYLE;
 
 	TargetEndianKind endian_kind;
 
@@ -254,6 +263,7 @@ bool   ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil
 	isize      thread_count;
 
 	PtrMap<char const *, ExactValue> defined_values;
+
 };
 
 
@@ -843,6 +853,22 @@ bool has_asm_extension(String const &path) {
 	return false;
 }
 
+// temporary
+char *token_pos_to_string(TokenPos const &pos) {
+	gbString s = gb_string_make_reserve(temporary_allocator(), 128);
+	String file = get_file_path_string(pos.file_id);
+	switch (build_context.ODIN_ERROR_POS_STYLE) {
+	default: /*fallthrough*/
+	case ErrorPosStyle_Default:
+		s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
+		break;
+	case ErrorPosStyle_Unix:
+		s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column);
+		break;
+	}
+	return s;
+}
+
 
 void init_build_context(TargetMetrics *cross_target) {
 	BuildContext *bc = &build_context;
@@ -855,6 +881,31 @@ void init_build_context(TargetMetrics *cross_target) {
 	bc->ODIN_VENDOR  = str_lit("odin");
 	bc->ODIN_VERSION = ODIN_VERSION;
 	bc->ODIN_ROOT    = odin_root_dir();
+
+	{
+		char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator());
+		if (found) {
+			ErrorPosStyle kind = ErrorPosStyle_Default;
+			String style = make_string_c(found);
+			style = string_trim_whitespace(style);
+			if (style == "" || style == "default" || style == "odin") {
+				kind = ErrorPosStyle_Default;
+			} else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") {
+				kind = ErrorPosStyle_Unix;
+			} else {
+				gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style));
+				gb_printf_err("Valid formats:\n");
+				gb_printf_err("\t\"default\" or \"odin\"\n");
+				gb_printf_err("\t\tpath(line:column) message\n");
+				gb_printf_err("\t\"unix\"\n");
+				gb_printf_err("\t\tpath:line:column: message\n");
+				gb_exit(1);
+			}
+
+			build_context.ODIN_ERROR_POS_STYLE = kind;
+		}
+	}
+
 	
 	bc->copy_file_contents = true;
 

+ 39 - 4
src/check_builtin.cpp

@@ -2183,9 +2183,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		}
 		
 		operand->mode = Addressing_Value;
-		if (is_type_array(t)) {
+		if (t->kind == Type_Array) {
+			i32 rank = type_math_rank(t);
 			// Do nothing
-			operand->type = x.type;			
+			operand->type = x.type;
+			if (rank > 2) {
+				gbString s = type_to_string(x.type);
+				error(call, "'%.*s' expects a matrix or array with a rank of 2, got %s of rank %d", LIT(builtin_name), s, rank);
+				gb_string_free(s);
+				return false;
+			} else if (rank == 2) {
+				Type *inner = base_type(t->Array.elem);
+				GB_ASSERT(inner->kind == Type_Array);
+				Type *elem = inner->Array.elem;
+				Type *array_inner = alloc_type_array(elem, t->Array.count);
+				Type *array_outer = alloc_type_array(array_inner, inner->Array.count);
+				operand->type = array_outer;
+
+				i64 elements = t->Array.count*inner->Array.count;
+				i64 size = type_size_of(operand->type);
+				if (!is_type_valid_for_matrix_elems(elem)) {
+					gbString s = type_to_string(x.type);
+					error(call, "'%.*s' expects a matrix or array with a base element type of an integer, float, or complex number, got %s", LIT(builtin_name), s);
+					gb_string_free(s);
+				} else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+					gbString s = type_to_string(x.type);
+					error(call, "'%.*s' expects a matrix or array with a maximum of %d elements, got %s with %lld elements", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s, elements);
+					gb_string_free(s);
+				} else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+					gbString s = type_to_string(x.type);
+					error(call, "'%.*s' expects a matrix or array with non-zero elements, got %s", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s);
+					gb_string_free(s);
+				} else if (size > MATRIX_ELEMENT_MAX_SIZE) {
+					gbString s = type_to_string(x.type);
+					error(call, "Too large of a type for '%.*s', got %s of size %lld, maximum size %d", LIT(builtin_name), s, cast(long long)size, MATRIX_ELEMENT_MAX_SIZE);
+					gb_string_free(s);
+				}
+			}
 		} else {
 			GB_ASSERT(t->kind == Type_Matrix);
 			operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count);
@@ -3310,9 +3344,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_type_is_simple_compare:
 	case BuiltinProc_type_is_dereferenceable:
 	case BuiltinProc_type_is_valid_map_key:
+	case BuiltinProc_type_is_valid_matrix_elements:
 	case BuiltinProc_type_is_named:
 	case BuiltinProc_type_is_pointer:
 	case BuiltinProc_type_is_array:
+	case BuiltinProc_type_is_enumerated_array:
 	case BuiltinProc_type_is_slice:
 	case BuiltinProc_type_is_dynamic_array:
 	case BuiltinProc_type_is_map:
@@ -3320,10 +3356,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_type_is_union:
 	case BuiltinProc_type_is_enum:
 	case BuiltinProc_type_is_proc:
-	case BuiltinProc_type_is_bit_field:
-	case BuiltinProc_type_is_bit_field_value:
 	case BuiltinProc_type_is_bit_set:
 	case BuiltinProc_type_is_simd_vector:
+	case BuiltinProc_type_is_matrix:
 	case BuiltinProc_type_is_specialized_polymorphic_record:
 	case BuiltinProc_type_is_unspecialized_polymorphic_record:
 	case BuiltinProc_type_has_nil:

+ 89 - 2
src/check_decl.cpp

@@ -238,6 +238,51 @@ isize total_attribute_count(DeclInfo *decl) {
 	return attribute_count;
 }
 
+Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
+	// NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations
+	//
+	//         X :: enum {A, B, C}
+	//         Y :: distinct X
+	//
+	// To make Y be just like X, it will need to copy the elements of X and change their type
+	// so that they match Y rather than X.
+	GB_ASSERT(original_enum_type != nullptr);
+	GB_ASSERT(named_type != nullptr);
+	GB_ASSERT(original_enum_type->kind == Type_Enum);
+	GB_ASSERT(named_type->kind == Type_Named);
+
+	Scope *parent = original_enum_type->Enum.scope->parent;
+	Scope *scope = create_scope(nullptr, parent);
+
+
+	Type *et = alloc_type_enum();
+	et->Enum.base_type = original_enum_type->Enum.base_type;
+	et->Enum.min_value = original_enum_type->Enum.min_value;
+	et->Enum.max_value = original_enum_type->Enum.max_value;
+	et->Enum.min_value_index = original_enum_type->Enum.min_value_index;
+	et->Enum.max_value_index = original_enum_type->Enum.max_value_index;
+	et->Enum.scope = scope;
+
+	auto fields = array_make<Entity *>(permanent_allocator(), original_enum_type->Enum.fields.count);
+	for_array(i, fields) {
+		Entity *old = original_enum_type->Enum.fields[i];
+
+		Entity *e = alloc_entity_constant(scope, old->token, named_type, old->Constant.value);
+		e->file = old->file;
+		e->identifier = clone_ast(old->identifier);
+		e->flags |= EntityFlag_Visited;
+		e->state = EntityState_Resolved;
+		e->Constant.flags = old->Constant.flags;
+		e->Constant.docs = old->Constant.docs;
+		e->Constant.comment = old->Constant.comment;
+
+		fields[i] = e;
+		add_entity(ctx, scope, nullptr, e);
+		add_entity_use(ctx, e->identifier, e);
+	}
+	et->Enum.fields = fields;
+	return et;
+}
 
 void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
 	GB_ASSERT(e->type == nullptr);
@@ -258,7 +303,11 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 	Type *bt = check_type_expr(ctx, te, named);
 	check_type_path_pop(ctx);
 
-	named->Named.base = base_type(bt);
+	Type *base = base_type(bt);
+	if (is_distinct && bt->kind == Type_Named && base->kind == Type_Enum) {
+		base = clone_enum_type(ctx, base, named);
+	}
+	named->Named.base = base;
 
 	if (is_distinct && is_type_typeid(e->type)) {
 		error(init_expr, "'distinct' cannot be applied to 'typeid'");
@@ -385,7 +434,45 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
 	Operand operand = {};
 
 	if (init != nullptr) {
-		Entity *entity = nullptr;
+		Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
+		if (entity != nullptr && entity->kind == Entity_TypeName) {
+			// @TypeAliasingProblem
+			// NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
+			// being "confused" as constants
+			//
+			//         A :: B
+			//         C :: proc "c" (^A)
+			//         B :: struct {x: C}
+			//
+			//     A gets evaluated first, and then checks B.
+			//     B then checks C.
+			//     C then tries to check A which is unresolved but thought to be a constant.
+			//     Therefore within C's check, A errs as "not a type".
+			//
+			// This is because a const declaration may or may not be a type and this cannot
+			// be determined from a syntactical standpoint.
+			// This check allows the compiler to override the entity to be checked as a type.
+			//
+			// There is no problem if B is prefixed with the `#type` helper enforcing at
+			// both a syntax and semantic level that B must be a type.
+			//
+			//         A :: #type B
+			//
+			// This approach is not fool proof and can fail in case such as:
+			//
+			//         X :: type_of(x)
+			//         X :: Foo(int).Type
+			//
+			// Since even these kind of declarations may cause weird checking cycles.
+			// For the time being, these are going to be treated as an unfortunate error
+			// until there is a proper delaying system to try declaration again if they
+			// have failed.
+
+			e->kind = Entity_TypeName;
+			check_type_decl(ctx, e, init, named_type);
+			return;
+		}
+		entity = nullptr;
 		if (init->kind == Ast_Ident) {
 			entity = check_ident(ctx, &operand, init, nullptr, e->type, true);
 		} else if (init->kind == Ast_SelectorExpr) {

File diff suppressed because it is too large
+ 483 - 378
src/check_expr.cpp


+ 15 - 93
src/check_stmt.cpp

@@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) {
 			out &= ~StateFlag_no_bounds_check;
 		}
 
+		if (in & StateFlag_no_type_assert) {
+			out |= StateFlag_no_type_assert;
+			out &= ~StateFlag_type_assert;
+		} else if (in & StateFlag_type_assert) {
+			out |= StateFlag_type_assert;
+			out &= ~StateFlag_no_type_assert;
+		}
+
 		ctx->state_flags = out;
 	}
 
@@ -689,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
 	return true;
 }
 
-
-struct TypeAndToken {
-	Type *type;
-	Token token;
-};
-
-
-void add_constant_switch_case(CheckerContext *ctx, PtrMap<uintptr, TypeAndToken> *seen, Operand operand, bool use_expr = true) {
-	if (operand.mode != Addressing_Constant) {
-		return;
-	}
-	if (operand.value.kind == ExactValue_Invalid) {
-		return;
-	}
-	
-	uintptr key = hash_exact_value(operand.value);
-	TypeAndToken *found = map_get(seen, key);
-	if (found != nullptr) {
-		isize count = multi_map_count(seen, key);
-		TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
-
-		multi_map_get_all(seen, key, taps);
-		for (isize i = 0; i < count; i++) {
-			TypeAndToken tap = taps[i];
-			if (!are_types_identical(operand.type, tap.type)) {
-				continue;
-			}
-
-			TokenPos pos = tap.token.pos;
-			if (use_expr) {
-				gbString expr_str = expr_to_string(operand.expr);
-				error(operand.expr,
-				      "Duplicate case '%s'\n"
-				      "\tprevious case at %s",
-				      expr_str,
-				      token_pos_to_string(pos));
-				gb_string_free(expr_str);
-			} else {
-				error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
-			}
-			return;
-		}
-	}
-
-	TypeAndToken tap = {operand.type, ast_token(operand.expr)};
-	multi_map_insert(seen, key, tap);
-}
-
 void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 	ast_node(irs, UnrollRangeStmt, node);
 	check_open_scope(ctx, node);
@@ -961,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		}
 	}
 
-	PtrMap<uintptr, TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+	SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
 	map_init(&seen, heap_allocator());
 	defer (map_destroy(&seen));
 
@@ -1001,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 
 				TokenKind upper_op = Token_Invalid;
 				switch (be->op.kind) {
-				case Token_Ellipsis:  upper_op = Token_GtEq; break;
-				case Token_RangeFull: upper_op = Token_GtEq; break;
-				case Token_RangeHalf: upper_op = Token_Gt;   break;
+				case Token_Ellipsis:  upper_op = Token_LtEq; break;
+				case Token_RangeFull: upper_op = Token_LtEq; break;
+				case Token_RangeHalf: upper_op = Token_Lt;   break;
 				default: GB_PANIC("Invalid range operator"); break;
 				}
 
@@ -1024,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 				Operand b1 = rhs;
 				check_comparison(ctx, &a1, &b1, Token_LtEq);
 
-				if (is_type_enum(x.type)) {
-					// TODO(bill): Fix this logic so it's fast!!!
-
-					i64 v0 = exact_value_to_i64(lhs.value);
-					i64 v1 = exact_value_to_i64(rhs.value);
-					Operand v = {};
-					v.mode = Addressing_Constant;
-					v.type = x.type;
-					v.expr = x.expr;
-
-					Type *bt = base_type(x.type);
-					GB_ASSERT(bt->kind == Type_Enum);
-					for (i64 vi = v0; vi <= v1; vi++) {
-						if (upper_op != Token_GtEq && vi == v1) {
-							break;
-						}
-
-						bool found = false;
-						for_array(j, bt->Enum.fields) {
-							Entity *f = bt->Enum.fields[j];
-							GB_ASSERT(f->kind == Entity_Constant);
-
-							i64 fv = exact_value_to_i64(f->Constant.value);
-							if (fv == vi) {
-								found = true;
-								break;
-							}
-						}
-						if (found) {
-							v.value = exact_value_i64(vi);
-							add_constant_switch_case(ctx, &seen, v);
-						}
-					}
-				} else {
-					add_constant_switch_case(ctx, &seen, lhs);
-					if (upper_op == Token_GtEq) {
-						add_constant_switch_case(ctx, &seen, rhs);
-					}
-				}
+				add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
 
 				if (is_type_string(x.type)) {
 					// NOTE(bill): Force dependency for strings here
@@ -1107,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 						continue;
 					}
 					update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
-					add_constant_switch_case(ctx, &seen, y);
+					add_to_seen_map(ctx, &seen, y);
 				}
 			}
 		}
@@ -1143,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 			if (unhandled.count == 1) {
 				error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
 			} else {
-				error_no_newline(node, "Unhandled switch cases: ");
+				error(node, "Unhandled switch cases:");
 				for_array(i, unhandled) {
 					Entity *f = unhandled[i];
 					error_line("\t%.*s\n", LIT(f->token.string));

+ 29 - 17
src/check_type.cpp

@@ -120,6 +120,8 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
 		ast_node(p, Field, param);
 		Ast *type_expr = p->type;
 		Type *type = nullptr;
+		CommentGroup *docs = p->docs;
+		CommentGroup *comment = p->comment;
 
 		if (type_expr != nullptr) {
 			type = check_type_expr(ctx, type_expr, nullptr);
@@ -156,6 +158,14 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
 			Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
 			add_entity(ctx, ctx->scope, name, field);
 			field->Variable.field_group_index = field_group_index;
+
+			if (j == 0) {
+				field->Variable.docs = docs;
+			}
+			if (j+1 == p->names.count) {
+				field->Variable.comment = comment;
+			}
+
 			array_add(&fields_array, field);
 			String tag = p->tag.string;
 			if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
@@ -722,20 +732,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
 		Ast *ident = nullptr;
 		Ast *init = nullptr;
 		u32 entity_flags = 0;
-		if (field->kind == Ast_FieldValue) {
-			ast_node(fv, FieldValue, field);
-			if (fv->field == nullptr || fv->field->kind != Ast_Ident) {
-				error(field, "An enum field's name must be an identifier");
-				continue;
-			}
-			ident = fv->field;
-			init = fv->value;
-		} else if (field->kind == Ast_Ident) {
-			ident = field;
-		} else {
+		if (field->kind != Ast_EnumFieldValue) {
 			error(field, "An enum field's name must be an identifier");
 			continue;
 		}
+		ident = field->EnumFieldValue.name;
+		init = field->EnumFieldValue.value;
+		if (ident == nullptr || ident->kind != Ast_Ident) {
+			error(field, "An enum field's name must be an identifier");
+			continue;
+		}
+		CommentGroup *docs    = field->EnumFieldValue.docs;
+		CommentGroup *comment = field->EnumFieldValue.comment;
+
 		String name = ident->Ident.token.string;
 
 		if (init != nullptr) {
@@ -793,6 +802,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
 		e->flags |= EntityFlag_Visited;
 		e->state = EntityState_Resolved;
 		e->Constant.flags |= entity_flags;
+		e->Constant.docs = docs;
+		e->Constant.comment = comment;
 
 		if (scope_lookup_current(ctx->scope, name) != nullptr) {
 			error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
@@ -2702,29 +2713,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 
 				Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid);
 
-				bool is_partial = false;
+				bool is_sparse = false;
 				if (at->tag != nullptr) {
 					GB_ASSERT(at->tag->kind == Ast_BasicDirective);
 					String name = at->tag->BasicDirective.name.string;
-					if (name == "partial") {
-						is_partial = true;
+					if (name == "sparse") {
+						is_sparse = true;
 					} else {
 						error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
 					}
 				}
 
-				if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) {
+				if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
 					error(e, "Non-contiguous enumeration used as an index in an enumerated array");
 					long long ea_count   = cast(long long)t->EnumeratedArray.count;
 					long long enum_count = cast(long long)bt->Enum.fields.count;
 					error_line("\tenumerated array length: %lld\n", ea_count);
 					error_line("\tenum field count: %lld\n", enum_count);
-					error_line("\tSuggestion: prepend #partial to the enumerated array to allow for non-named elements\n");
+					error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
 					if (2*enum_count < ea_count) {
 						error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
-						error_line("\t         this warning will be removed if #partial is applied\n");
+						error_line("\t         this warning will be removed if #sparse is applied\n");
 					}
 				}
+				t->EnumeratedArray.is_sparse = is_sparse;
 
 				*type = t;
 

+ 134 - 24
src/checker.cpp

@@ -504,6 +504,7 @@ enum VettedEntityKind {
 
 	VettedEntity_Unused,
 	VettedEntity_Shadowed,
+	VettedEntity_Shadowed_And_Unused,
 };
 struct VettedEntity {
 	VettedEntityKind kind;
@@ -625,12 +626,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
 	MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
 		Entity *e = scope->elements.entries[i].value;
 		if (e == nullptr) continue;
-		VettedEntity ve = {};
-		if (vet_unused && check_vet_unused(c, e, &ve)) {
-			array_add(&vetted_entities, ve);
-		}
-		if (vet_shadowing && check_vet_shadowing(c, e, &ve)) {
-			array_add(&vetted_entities, ve);
+		VettedEntity ve_unused = {};
+		VettedEntity ve_shadowed = {};
+		bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+		bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
+		if (is_unused && is_shadowed) {
+			VettedEntity ve_both = ve_shadowed;
+			ve_both.kind = VettedEntity_Shadowed_And_Unused;
+			array_add(&vetted_entities, ve_both);
+		} else if (is_unused) {
+			array_add(&vetted_entities, ve_unused);
+		} else if (is_shadowed) {
+			array_add(&vetted_entities, ve_shadowed);
 		}
 	}
 
@@ -642,16 +649,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
 		Entity *other = ve.other;
 		String name = e->token.string;
 
-		if (build_context.vet) {
+		if (ve.kind == VettedEntity_Shadowed_And_Unused) {
+			error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
+		} else if (build_context.vet) {
 			switch (ve.kind) {
 			case VettedEntity_Unused:
 				error(e->token, "'%.*s' declared but not used", LIT(name));
 				break;
 			case VettedEntity_Shadowed:
 				if (e->flags&EntityFlag_Using) {
-					error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+					error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
 				} else {
-					error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+					error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
 				}
 				break;
 			default:
@@ -688,12 +697,17 @@ void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
 	ptr_set_add(&d->deps, e);
 	mutex_unlock(&info->deps_mutex);
 }
-void add_type_info_dependency(DeclInfo *d, Type *type) {
+void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type, bool require_mutex) {
 	if (d == nullptr) {
 		return;
 	}
-	// NOTE(bill): no mutex is required here because the only procedure calling it is wrapped in a mutex already
+	if (require_mutex) {
+		mutex_lock(&info->deps_mutex);
+	}
 	ptr_set_add(&d->type_info_deps, type);
+	if (require_mutex) {
+		mutex_unlock(&info->deps_mutex);
+	}
 }
 
 AstPackage *get_core_package(CheckerInfo *info, String name) {
@@ -919,6 +933,16 @@ void init_universal(void) {
 		add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
 	}
 
+	{
+		GlobalEnumValue values[ErrorPosStyle_COUNT] = {
+			{"Default", ErrorPosStyle_Default},
+			{"Unix",    ErrorPosStyle_Unix},
+		};
+
+		auto fields = add_global_enum_type(str_lit("Odin_Error_Pos_Style_Type"), values, gb_count_of(values));
+		add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
+	}
+
 
 	add_global_bool_constant("ODIN_DEBUG",                    bc->ODIN_DEBUG);
 	add_global_bool_constant("ODIN_DISABLE_ASSERT",           bc->ODIN_DISABLE_ASSERT);
@@ -1620,7 +1644,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 		return;
 	}
 
-	add_type_info_dependency(c->decl, t);
+	add_type_info_dependency(c->info, c->decl, t, false);
 
 	auto found = map_get(&c->info->type_info_map, t);
 	if (found != nullptr) {
@@ -1749,6 +1773,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 		} else {
 			add_type_info_type_internal(c, t_type_info_ptr);
 		}
+		add_type_info_type_internal(c, bt->Union.polymorphic_params);
 		for_array(i, bt->Union.variants) {
 			add_type_info_type_internal(c, bt->Union.variants[i]);
 		}
@@ -1772,6 +1797,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 				}
 			}
 		}
+		add_type_info_type_internal(c, bt->Struct.polymorphic_params);
 		for_array(i, bt->Struct.fields) {
 			Entity *f = bt->Struct.fields[i];
 			add_type_info_type_internal(c, f->type);
@@ -1965,6 +1991,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
 		} else {
 			add_min_dep_type_info(c, t_type_info_ptr);
 		}
+		add_min_dep_type_info(c, bt->Union.polymorphic_params);
 		for_array(i, bt->Union.variants) {
 			add_min_dep_type_info(c, bt->Union.variants[i]);
 		}
@@ -1988,6 +2015,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
 				}
 			}
 		}
+		add_min_dep_type_info(c, bt->Struct.polymorphic_params);
 		for_array(i, bt->Struct.fields) {
 			Entity *f = bt->Struct.fields[i];
 			add_min_dep_type_info(c, f->type);
@@ -3479,9 +3507,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 
 	if (entity_visibility_kind == EntityVisiblity_Public &&
 	    (c->scope->flags&ScopeFlag_File) &&
-	    c->scope->file &&
-	    (c->scope->file->flags & AstFile_IsPrivate)) {
-		entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+	    c->scope->file) {
+	    	if (c->scope->file->flags & AstFile_IsPrivateFile) {
+			entity_visibility_kind = EntityVisiblity_PrivateToFile;
+		} else if (c->scope->file->flags & AstFile_IsPrivatePkg) {
+			entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+	    	}
 	}
 
 	if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) {
@@ -3572,9 +3603,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 
 			if (is_ast_type(init)) {
 				e = alloc_entity_type_name(d->scope, token, nullptr);
-				// if (vd->type != nullptr) {
-				// 	error(name, "A type declaration cannot have an type parameter");
-				// }
 			} else if (init->kind == Ast_ProcLit) {
 				if (c->scope->flags&ScopeFlag_Type) {
 					error(name, "Procedure declarations are not allowed within a struct");
@@ -3677,6 +3705,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
 	check_collect_entities(&c, block->stmts);
 }
 
+bool correct_single_type_alias(CheckerContext *c, Entity *e) {
+	if (e->kind == Entity_Constant) {
+		DeclInfo *d = e->decl_info;
+		if (d != nullptr && d->init_expr != nullptr) {
+			Ast *init = d->init_expr;
+			Entity *alias_of = check_entity_from_ident_or_selector(c, init, true);
+			if (alias_of != nullptr && alias_of->kind == Entity_TypeName) {
+				e->kind = Entity_TypeName;
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
+	isize n = s->elements.entries.count;
+	bool correction = false;
+	for (isize i = n-1; i >= 0; i--) {
+		correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+	}
+	return correction;
+}
+bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
+	isize n = s->elements.entries.count;
+	bool correction = false;
+	for (isize i = 0; i < n; i++) {
+		correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+	}
+	return correction;
+}
+
+
+void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
+	// NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases
+	// of type aliases being "confused" as constants
+	//
+	//         A :: C
+	//         B :: A
+	//         C :: struct {b: ^B}
+	//
+	// See @TypeAliasingProblem for more information
+	for (;;) {
+		bool corrections = false;
+		corrections |= correct_type_alias_in_scope_backwards(c, s);
+		corrections |= correct_type_alias_in_scope_forwards(c, s);
+		if (!corrections) {
+			return;
+		}
+	}
+}
+
+
 // NOTE(bill): If file_scopes == nullptr, this will act like a local scope
 void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
 	AstFile *curr_file = nullptr;
@@ -3748,6 +3829,7 @@ void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
 		}
 	}
 
+	// correct_type_aliases(c);
 
 	// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
 	// declared after this stmt in source
@@ -4393,10 +4475,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
 
 	for_array(i, decls) {
 		if (collect_file_decl(ctx, decls[i])) {
+			correct_type_aliases_in_scope(ctx, ctx->scope);
 			return true;
 		}
 	}
-
+	correct_type_aliases_in_scope(ctx, ctx->scope);
 	return false;
 }
 
@@ -4666,6 +4749,15 @@ void check_import_entities(Checker *c) {
 			}
 			add_untyped_expressions(ctx.info, &untyped);
 		}
+
+		for_array(i, pkg->files) {
+			AstFile *f = pkg->files[i];
+			reset_checker_context(&ctx, f, &untyped);
+			ctx.collect_delayed_decls = false;
+
+			correct_type_aliases_in_scope(&ctx, pkg->scope);
+		}
+
 		for_array(i, pkg->files) {
 			AstFile *f = pkg->files[i];
 			reset_checker_context(&ctx, f, &untyped);
@@ -4887,6 +4979,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
 	bool bounds_check    = (pi->tags & ProcTag_bounds_check)    != 0;
 	bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
 
+	bool type_assert    = (pi->tags & ProcTag_type_assert)    != 0;
+	bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0;
+
 	if (bounds_check) {
 		ctx.state_flags |= StateFlag_bounds_check;
 		ctx.state_flags &= ~StateFlag_no_bounds_check;
@@ -4894,6 +4989,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
 		ctx.state_flags |= StateFlag_no_bounds_check;
 		ctx.state_flags &= ~StateFlag_bounds_check;
 	}
+
+	if (type_assert) {
+		ctx.state_flags |= StateFlag_type_assert;
+		ctx.state_flags &= ~StateFlag_no_type_assert;
+	} else if (no_type_assert) {
+		ctx.state_flags |= StateFlag_no_type_assert;
+		ctx.state_flags &= ~StateFlag_type_assert;
+	}
+
 	if (pi->body != nullptr && e != nullptr) {
 		GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0);
 	}
@@ -5307,12 +5411,18 @@ void check_unique_package_names(Checker *c) {
 			string_map_set(&pkgs, key, pkg);
 			continue;
 		}
+		auto *curr = pkg->files[0]->pkg_decl;
+		auto *prev = (*found)->files[0]->pkg_decl;
+		if (curr == prev) {
+			// NOTE(bill): A false positive was found, ignore it
+			continue;
+		}
 
-		error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name));
+		error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
 		error_line("\tA package name must be unique\n"
 		           "\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
 		           "\tA package name is required for link name prefixing to have a consistent ABI\n");
-		error((*found)->files[0]->pkg_decl, "found at previous location");
+		error(prev, "found at previous location");
 	}
 }
 
@@ -5504,9 +5614,6 @@ void check_parsed_files(Checker *c) {
 	TIME_SECTION("calculate global init order");
 	calculate_global_init_order(c);
 
-	TIME_SECTION("generate minimum dependency set");
-	generate_minimum_dependency_set(c, c->info.entry_point);
-
 	TIME_SECTION("check test procedures");
 	check_test_procedures(c);
 
@@ -5517,6 +5624,9 @@ void check_parsed_files(Checker *c) {
 	add_type_info_for_type_definitions(c);
 	check_merge_queues_into_arrays(c);
 
+	TIME_SECTION("generate minimum dependency set");
+	generate_minimum_dependency_set(c, c->info.entry_point);
+
 	TIME_SECTION("check entry point");
 	if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
 		Scope *s = c->info.init_scope;

+ 0 - 4
src/checker_builtin_procs.hpp

@@ -213,8 +213,6 @@ BuiltinProc__type_simple_boolean_begin,
 	BuiltinProc_type_is_union,
 	BuiltinProc_type_is_enum,
 	BuiltinProc_type_is_proc,
-	BuiltinProc_type_is_bit_field,
-	BuiltinProc_type_is_bit_field_value,
 	BuiltinProc_type_is_bit_set,
 	BuiltinProc_type_is_simd_vector,
 	BuiltinProc_type_is_matrix,
@@ -466,8 +464,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("type_is_union"),             1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_enum"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_proc"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-	{STR_LIT("type_is_bit_field"),         1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-	{STR_LIT("type_is_bit_field_value"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_bit_set"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_simd_vector"),       1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_matrix"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 15 - 5
src/docs_format.cpp

@@ -15,7 +15,7 @@ struct OdinDocVersionType {
 
 #define OdinDocVersionType_Major 0
 #define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 3
+#define OdinDocVersionType_Patch 4
 
 struct OdinDocHeaderBase {
 	u8                 magic[8];
@@ -154,6 +154,7 @@ enum OdinDocEntityKind : u32 {
 	OdinDocEntity_ProcGroup   = 5,
 	OdinDocEntity_ImportName  = 6,
 	OdinDocEntity_LibraryName = 7,
+	OdinDocEntity_Builtin     = 8,
 };
 
 enum OdinDocEntityFlag : u64 {
@@ -170,6 +171,9 @@ enum OdinDocEntityFlag : u64 {
 
 	OdinDocEntityFlag_Type_Alias = 1ull<<20,
 
+	OdinDocEntityFlag_Builtin_Pkg_Builtin    = 1ull<<30,
+	OdinDocEntityFlag_Builtin_Pkg_Intrinsics = 1ull<<31,
+
 	OdinDocEntityFlag_Var_Thread_Local = 1ull<<40,
 	OdinDocEntityFlag_Var_Static       = 1ull<<41,
 
@@ -185,8 +189,8 @@ struct OdinDocEntity {
 	OdinDocTypeIndex   type;
 	OdinDocString      init_string;
 	u32                reserved_for_init;
-	OdinDocString      comment;
-	OdinDocString      docs;
+	OdinDocString      comment; // line comment
+	OdinDocString      docs; // preceding comment
 	i32                field_group_index;
 	OdinDocEntityIndex foreign_library;
 	OdinDocString      link_name;
@@ -201,15 +205,21 @@ enum OdinDocPkgFlags : u32 {
 	OdinDocPkgFlag_Init    = 1<<2,
 };
 
+struct OdinDocScopeEntry {
+	OdinDocString      name;
+	OdinDocEntityIndex entity;
+};
+
 struct OdinDocPkg {
 	OdinDocString fullpath;
 	OdinDocString name;
 	u32           flags;
 	OdinDocString docs;
-	OdinDocArray<OdinDocFileIndex>   files;
-	OdinDocArray<OdinDocEntityIndex> entities;
+	OdinDocArray<OdinDocFileIndex>  files;
+	OdinDocArray<OdinDocScopeEntry> entries;
 };
 
+
 struct OdinDocHeader {
 	OdinDocHeaderBase base;
 

+ 41 - 21
src/docs_writer.cpp

@@ -811,8 +811,17 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 		comment = e->decl_info->comment;
 		docs = e->decl_info->docs;
 	}
+	if (e->kind == Entity_Variable) {
+		if (!comment) { comment          = e->Variable.comment; }
+		if (!docs)    { docs             = e->Variable.docs; }
+	} else if (e->kind == Entity_Constant) {
+		if (!comment) { comment          = e->Constant.comment; }
+		if (!docs)    { docs             = e->Constant.docs; }
+	}
 
+	String name = e->token.string;
 	String link_name = {};
+	TokenPos pos = e->token.pos;
 
 	OdinDocEntityKind kind = OdinDocEntity_Invalid;
 	u64 flags = 0;
@@ -827,6 +836,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 	case Entity_ProcGroup:   kind = OdinDocEntity_ProcGroup;   break;
 	case Entity_ImportName:  kind = OdinDocEntity_ImportName;  break;
 	case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
+	case Entity_Builtin:     kind = OdinDocEntity_Builtin;     break;
 	}
 
 	switch (e->kind) {
@@ -856,6 +866,23 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 		if (e->Procedure.is_export)  { flags |= OdinDocEntityFlag_Export;  }
 		link_name = e->Procedure.link_name;
 		break;
+	case Entity_Builtin:
+		{
+			auto bp = builtin_procs[e->Builtin.id];
+			pos = {};
+			name = bp.name;
+			switch (bp.pkg) {
+			case BuiltinProcPkg_builtin:
+				flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin;
+				break;
+			case BuiltinProcPkg_intrinsics:
+				flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics;
+				break;
+			default:
+				GB_PANIC("Unhandled BuiltinProcPkg");
+			}
+		}
+		break;
 	}
 
 	if (e->flags & EntityFlag_Param) {
@@ -891,8 +918,8 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 
 	doc_entity.kind = kind;
 	doc_entity.flags = flags;
-	doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
-	doc_entity.name = odin_doc_write_string(w, e->token.string);
+	doc_entity.pos = odin_doc_token_pos_cast(w, pos);
+	doc_entity.name = odin_doc_write_string(w, name);
 	doc_entity.type = 0; // Set later
 	doc_entity.init_string = init_string;
 	doc_entity.comment = odin_doc_comment_group_string(w, comment);
@@ -969,7 +996,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
 
 
 
-OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
+OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
 	if (pkg->scope == nullptr) {
 		return {};
 	}
@@ -977,14 +1004,14 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
 		return {};
 	}
 
-	auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
-	defer (array_free(&entities));
+	auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.entries.count);
+	defer (array_free(&entries));
 
 	for_array(i, pkg->scope->elements.entries) {
+		String name = pkg->scope->elements.entries[i].key.string;
 		Entity *e = pkg->scope->elements.entries[i].value;
 		switch (e->kind) {
 		case Entity_Invalid:
-		case Entity_Builtin:
 		case Entity_Nil:
 		case Entity_Label:
 			continue;
@@ -995,34 +1022,27 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
 		case Entity_ProcGroup:
 		case Entity_ImportName:
 		case Entity_LibraryName:
+		case Entity_Builtin:
 			// Fine
 			break;
 		}
-		array_add(&entities, e);
-	}
-	gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
-
-	auto entity_indices = array_make<OdinDocEntityIndex>(heap_allocator(), 0, w->entity_cache.entries.count);
-	defer (array_free(&entity_indices));
-
-	for_array(i, entities) {
-		Entity *e = entities[i];
 		if (e->pkg != pkg) {
 			continue;
 		}
-		if (!is_entity_exported(e)) {
+		if (!is_entity_exported(e, true)) {
 			continue;
 		}
 		if (e->token.string.len == 0) {
 			continue;
 		}
 
-		OdinDocEntityIndex doc_entity_index = 0;
-		doc_entity_index = odin_doc_add_entity(w, e);
-		array_add(&entity_indices, doc_entity_index);
+		OdinDocScopeEntry entry = {};
+		entry.name = odin_doc_write_string(w, name);
+		entry.entity = odin_doc_add_entity(w, e);
+		array_add(&entries, entry);
 	}
 
-	return odin_write_slice(w, entity_indices.data, entity_indices.count);
+	return odin_write_slice(w, entries.data, entries.count);
 }
 
 
@@ -1090,7 +1110,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
 		}
 
 		doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
-		doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
+		doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg);
 
 		if (dst) {
 			*dst = doc_pkg;

+ 5 - 1
src/entity.cpp

@@ -161,6 +161,8 @@ struct Entity {
 			ParameterValue param_value;
 			u32 flags;
 			i32 field_group_index;
+			CommentGroup *docs;
+			CommentGroup *comment;
 		} Constant;
 		struct {
 			Ast *init_expr; // only used for some variables within procedure bodies
@@ -175,6 +177,8 @@ struct Entity {
 			String     link_name;
 			String     link_prefix;
 			String     link_section;
+			CommentGroup *docs;
+			CommentGroup *comment;
 			bool       is_foreign;
 			bool       is_export;
 		} Variable;
@@ -241,7 +245,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
 	if (e->flags & EntityFlag_NotExported) {
 		return false;
 	}
-	if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
+	if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) {
 		return false;
 	}
 

+ 5 - 0
src/error.cpp

@@ -33,6 +33,10 @@ void init_global_error_collector(void) {
 }
 
 
+// temporary
+// defined in build_settings.cpp
+char *token_pos_to_string(TokenPos const &pos);
+
 bool set_file_path_string(i32 index, String const &path) {
 	bool ok = false;
 	GB_ASSERT(index >= 0);
@@ -403,6 +407,7 @@ void compiler_error(char const *fmt, ...) {
 	gb_printf_err("Internal Compiler Error: %s\n",
 	              gb_bprintf_va(fmt, va));
 	va_end(va);
+	GB_DEBUG_TRAP();
 	gb_exit(1);
 }
 

+ 1 - 0
src/llvm_backend.hpp

@@ -232,6 +232,7 @@ struct lbTargetList {
 
 enum lbProcedureFlag : u32 {
 	lbProcedureFlag_WithoutMemcpyPass = 1<<0,
+	lbProcedureFlag_DebugAllocaCopy = 1<<1,
 };
 
 struct lbCopyElisionHint {

+ 4 - 4
src/llvm_backend_const.cpp

@@ -115,8 +115,8 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
 
 
 lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
-	GB_ASSERT(is_type_pointer(value.type));
-	GB_ASSERT(is_type_pointer(t));
+	GB_ASSERT(is_type_internally_pointer_like(value.type));
+	GB_ASSERT(is_type_internally_pointer_like(t));
 	GB_ASSERT(lb_is_const(value));
 
 	lbValue res = {};
@@ -175,7 +175,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize
 }
 
 LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
-	GB_ASSERT(is_type_pointer(data.type));
+	GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type));
 	GB_ASSERT(are_types_identical(len.type, t_int));
 	LLVMValueRef vals[2] = {
 		data.value,
@@ -568,7 +568,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
 		}
 
 	case ExactValue_Integer:
-		if (is_type_pointer(type)) {
+		if (is_type_pointer(type) || is_type_multi_pointer(type)) {
 			LLVMTypeRef t = lb_type(m, original_type);
 			LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
 			res.value = LLVMConstIntToPtr(i, t);

+ 71 - 0
src/llvm_backend_debug.cpp

@@ -965,6 +965,77 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
 	LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
 }
 
+
+void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number) {
+	if (p->debug_info == nullptr) {
+		return;
+	}
+	if (type == nullptr) {
+		return;
+	}
+	if (type == t_invalid) {
+		return;
+	}
+	if (p->body == nullptr) {
+		return;
+	}
+
+	lbModule *m = p->module;
+	String const &name = token.string;
+	if (name == "" || name == "_") {
+		return;
+	}
+
+	if (lb_get_llvm_metadata(m, ptr) != nullptr) {
+		// Already been set
+		return;
+	}
+
+
+	AstFile *file = p->body->file();
+
+	LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
+	LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+	GB_ASSERT(llvm_scope != nullptr);
+	if (llvm_file == nullptr) {
+		llvm_file = LLVMDIScopeGetFile(llvm_scope);
+	}
+
+	if (llvm_file == nullptr) {
+		return;
+	}
+
+	LLVMDIFlags flags = LLVMDIFlagZero;
+	LLVMBool always_preserve = build_context.optimization_level == 0;
+
+	LLVMMetadataRef debug_type = lb_debug_type(m, type);
+
+	LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable(
+		m->debug_builder, llvm_scope,
+		cast(char const *)name.text, cast(size_t)name.len,
+		arg_number,
+		llvm_file, token.pos.line,
+		debug_type,
+		always_preserve, flags
+	);
+
+	LLVMValueRef storage = ptr;
+	LLVMValueRef instr = ptr;
+	LLVMBasicBlockRef block = p->decl_block->block;
+	LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
+	LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
+	lb_set_llvm_metadata(m, ptr, llvm_expr);
+	if (LLVMIsAAllocaInst(instr)) {
+		LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
+	} else {
+		// NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
+		// The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
+		// instruction "before" it.
+		LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
+	}
+}
+
+
 void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
 	if (!p->debug_info || !p->body) {
 		return;

+ 75 - 33
src/llvm_backend_expr.cpp

@@ -580,6 +580,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
 
 lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
 	if (is_type_array(m.type)) {
+		i32 rank = type_math_rank(m.type);
+		if (rank == 2) {
+			lbAddr addr = lb_add_local_generated(p, type, false);
+			lbValue dst = addr.addr;
+			lbValue src = m;
+			i32 n = cast(i32)get_array_type_count(m.type);
+			i32 m = cast(i32)get_array_type_count(type);
+			// m.type == [n][m]T
+			// type   == [m][n]T
+
+			for (i32 j = 0; j < m; j++) {
+				lbValue dst_col = lb_emit_struct_ep(p, dst, j);
+				for (i32 i = 0; i < n; i++) {
+					lbValue dst_row = lb_emit_struct_ep(p, dst_col, i);
+					lbValue src_col = lb_emit_struct_ev(p, src, i);
+					lbValue src_row = lb_emit_struct_ev(p, src_col, j);
+					lb_emit_store(p, dst_row, src_row);
+				}
+			}
+			return lb_addr_load(p, addr);
+		}
 		// no-op
 		m.type = type;
 		return m;
@@ -1834,6 +1855,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 				return lb_addr_load(p, parent);
 			}
 		}
+		if (dst->Union.variants.count == 1) {
+			Type *vt = dst->Union.variants[0];
+			if (internal_check_is_assignable_to(src, vt)) {
+				value = lb_emit_conv(p, value, vt);
+				lbAddr parent = lb_add_local_generated(p, t, true);
+				lb_emit_store_union_variant(p, parent.addr, value, vt);
+				return lb_addr_load(p, parent);
+			}
+		}
 	}
 
 	// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
@@ -2768,27 +2798,29 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 				Type *src_type = type_deref(v.type);
 				Type *dst_type = type;
 
-				lbValue src_tag = {};
-				lbValue dst_tag = {};
-				if (is_type_union_maybe_pointer(src_type)) {
-					src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
-					dst_tag = lb_const_bool(p->module, t_bool, true);
-				} else {
-					src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
-					dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
-				}
 
-				lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
-				auto args = array_make<lbValue>(permanent_allocator(), 6);
-				args[0] = ok;
+				if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+					lbValue src_tag = {};
+					lbValue dst_tag = {};
+					if (is_type_union_maybe_pointer(src_type)) {
+						src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+						dst_tag = lb_const_bool(p->module, t_bool, true);
+					} else {
+						src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+						dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+					}
+					lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+					auto args = array_make<lbValue>(permanent_allocator(), 6);
+					args[0] = ok;
 
-				args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
-				args[2] = lb_const_int(p->module, t_i32, pos.line);
-				args[3] = lb_const_int(p->module, t_i32, pos.column);
+					args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+					args[2] = lb_const_int(p->module, t_i32, pos.line);
+					args[3] = lb_const_int(p->module, t_i32, pos.column);
 
-				args[4] = lb_typeid(p->module, src_type);
-				args[5] = lb_typeid(p->module, dst_type);
-				lb_emit_runtime_call(p, "type_assertion_check", args);
+					args[4] = lb_typeid(p->module, src_type);
+					args[5] = lb_typeid(p->module, dst_type);
+					lb_emit_runtime_call(p, "type_assertion_check", args);
+				}
 
 				lbValue data_ptr = v;
 				return lb_emit_conv(p, data_ptr, tv.type);
@@ -2797,23 +2829,23 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
 				if (is_type_pointer(v.type)) {
 					v = lb_emit_load(p, v);
 				}
-
 				lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
-				lbValue any_id = lb_emit_struct_ev(p, v, 1);
-				lbValue id = lb_typeid(p->module, type);
+				if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+					lbValue any_id = lb_emit_struct_ev(p, v, 1);
 
+					lbValue id = lb_typeid(p->module, type);
+					lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+					auto args = array_make<lbValue>(permanent_allocator(), 6);
+					args[0] = ok;
 
-				lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
-				auto args = array_make<lbValue>(permanent_allocator(), 6);
-				args[0] = ok;
-
-				args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
-				args[2] = lb_const_int(p->module, t_i32, pos.line);
-				args[3] = lb_const_int(p->module, t_i32, pos.column);
+					args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+					args[2] = lb_const_int(p->module, t_i32, pos.line);
+					args[3] = lb_const_int(p->module, t_i32, pos.column);
 
-				args[4] = any_id;
-				args[5] = id;
-				lb_emit_runtime_call(p, "type_assertion_check", args);
+					args[4] = any_id;
+					args[5] = id;
+					lb_emit_runtime_call(p, "type_assertion_check", args);
+				}
 
 				return lb_emit_conv(p, data_ptr, tv.type);
 			} else {
@@ -2843,6 +2875,14 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 			out &= ~StateFlag_bounds_check;
 		}
 
+		if (in & StateFlag_type_assert) {
+			out |= StateFlag_type_assert;
+			out &= ~StateFlag_no_type_assert;
+		} else if (in & StateFlag_no_type_assert) {
+			out |= StateFlag_no_type_assert;
+			out &= ~StateFlag_type_assert;
+		}
+
 		p->state_flags = out;
 	}
 
@@ -3460,7 +3500,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 		GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
 
 		if (is_type_map(t)) {
-			lbValue map_val = lb_build_addr_ptr(p, ie->expr);
+			lbAddr map_addr = lb_build_addr(p, ie->expr);
+			lbValue map_val = lb_addr_load(p, map_addr);
 			if (deref) {
 				map_val = lb_emit_load(p, map_val);
 			}
@@ -3469,7 +3510,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 			key = lb_emit_conv(p, key, t->Map.key);
 
 			Type *result_type = type_of_expr(expr);
-			return lb_addr_map(map_val, key, t, result_type);
+			lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
+			return lb_addr_map(map_ptr, key, t, result_type);
 		}
 
 		switch (t->kind) {

+ 6 - 1
src/llvm_backend_general.cpp

@@ -271,6 +271,10 @@ lbAddr lb_addr(lbValue addr) {
 
 
 lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) {
+	GB_ASSERT(is_type_pointer(addr.type));
+	Type *mt = type_deref(addr.type);
+	GB_ASSERT(is_type_map(mt));
+
 	lbAddr v = {lbAddr_Map, addr};
 	v.map.key    = map_key;
 	v.map.type   = map_type;
@@ -1598,8 +1602,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 						return llvm_type;
 					}
 					llvm_type = LLVMStructCreateNamed(ctx, name);
+					LLVMTypeRef found_val = *found;
 					map_set(&m->types, type, llvm_type);
-					lb_clone_struct_type(llvm_type, *found);
+					lb_clone_struct_type(llvm_type, found_val);
 					return llvm_type;
 				}
 			}

+ 6 - 3
src/llvm_backend_proc.cpp

@@ -61,7 +61,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 	GB_ASSERT(entity != nullptr);
 	GB_ASSERT(entity->kind == Entity_Procedure);
 	if (!entity->Procedure.is_foreign) {
-		GB_ASSERT(entity->flags & EntityFlag_ProcBodyChecked);
+		GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type));
 	}
 
 	String link_name = {};
@@ -473,6 +473,8 @@ void lb_begin_procedure_body(lbProcedure *p) {
 				}
 
 				lbArgType *arg_type = &ft->args[param_index];
+				defer (param_index += 1);
+
 				if (arg_type->kind == lbArg_Ignore) {
 					continue;
 				} else if (arg_type->kind == lbArg_Direct) {
@@ -487,18 +489,19 @@ void lb_begin_procedure_body(lbProcedure *p) {
 						param.type = e->type;
 
 						lbValue ptr = lb_address_from_load_or_generate_local(p, param);
+						GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
 						lb_add_entity(p->module, e, ptr);
+						lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1);
 					}
 				} else if (arg_type->kind == lbArg_Indirect) {
 					if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
 						lbValue ptr = {};
 						ptr.value = LLVMGetParam(p->value, param_offset+param_index);
 						ptr.type = alloc_type_pointer(e->type);
-
 						lb_add_entity(p->module, e, ptr);
+						lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1);
 					}
 				}
-				param_index += 1;
 			}
 		}
 

+ 7 - 0
src/llvm_backend_stmt.cpp

@@ -1991,6 +1991,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
 			out |= StateFlag_no_bounds_check;
 			out &= ~StateFlag_bounds_check;
 		}
+		if (in & StateFlag_no_type_assert) {
+			out |= StateFlag_no_type_assert;
+			out &= ~StateFlag_type_assert;
+		} else if (in & StateFlag_type_assert) {
+			out |= StateFlag_type_assert;
+			out &= ~StateFlag_no_type_assert;
+		}
 
 		p->state_flags = out;
 	}

+ 8 - 7
src/llvm_backend_type.cpp

@@ -1,11 +1,10 @@
 isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
-	isize index = type_info_index(info, type, false);
+	auto *set = &info->minimum_dependency_type_info_set;
+	isize index = type_info_index(info, type, err_on_not_found);
 	if (index >= 0) {
-		auto *set = &info->minimum_dependency_type_info_set;
-		for_array(i, set->entries) {
-			if (set->entries[i].ptr == index) {
-				return i+1;
-			}
+		isize i = ptr_entry_index(set, index);
+		if (i >= 0) {
+			return i+1;
 		}
 	}
 	if (err_on_not_found) {
@@ -455,7 +454,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 		case Type_EnumeratedArray: {
 			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
 
-			LLVMValueRef vals[6] = {
+			LLVMValueRef vals[7] = {
 				lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
 				lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
 				lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
@@ -464,6 +463,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 				// Unions
 				LLVMConstNull(lb_type(m, t_type_info_enum_value)),
 				LLVMConstNull(lb_type(m, t_type_info_enum_value)),
+
+				lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value,
 			};
 
 			lbValue res = {};

+ 26 - 15
src/llvm_backend_utility.cpp

@@ -626,6 +626,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
 
 	lbValue value_  = lb_address_from_load_or_generate_local(p, value);
 
+	if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+		// just do a bit cast of the data at the front
+		lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type));
+		return lb_emit_load(p, ptr);
+	}
+
 	lbValue tag = {};
 	lbValue dst_tag = {};
 	lbValue cond = {};
@@ -666,23 +672,22 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
 	lb_start_block(p, end_block);
 
 	if (!is_tuple) {
-		{
-			// NOTE(bill): Panic on invalid conversion
-			Type *dst_type = tuple->Tuple.variables[0]->type;
+		GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
+		// NOTE(bill): Panic on invalid conversion
+		Type *dst_type = tuple->Tuple.variables[0]->type;
 
-			lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
-			auto args = array_make<lbValue>(permanent_allocator(), 7);
-			args[0] = ok;
+		lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+		auto args = array_make<lbValue>(permanent_allocator(), 7);
+		args[0] = ok;
 
-			args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
-			args[2] = lb_const_int(m, t_i32, pos.line);
-			args[3] = lb_const_int(m, t_i32, pos.column);
+		args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
+		args[2] = lb_const_int(m, t_i32, pos.line);
+		args[3] = lb_const_int(m, t_i32, pos.column);
 
-			args[4] = lb_typeid(m, src_type);
-			args[5] = lb_typeid(m, dst_type);
-			args[6] = lb_emit_conv(p, value_, t_rawptr);
-			lb_emit_runtime_call(p, "type_assertion_check2", args);
-		}
+		args[4] = lb_typeid(m, src_type);
+		args[5] = lb_typeid(m, dst_type);
+		args[6] = lb_emit_conv(p, value_, t_rawptr);
+		lb_emit_runtime_call(p, "type_assertion_check2", args);
 
 		return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
 	}
@@ -706,6 +711,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
 	}
 	Type *dst_type = tuple->Tuple.variables[0]->type;
 
+	if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+		// just do a bit cast of the data at the front
+		lbValue ptr = lb_emit_struct_ev(p, value, 0);
+		ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type));
+		return lb_addr(ptr);
+	}
+
 	lbAddr v = lb_add_local_generated(p, tuple, true);
 
 	lbValue dst_typeid = lb_typeid(m, dst_type);
@@ -731,7 +743,6 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
 
 	if (!is_tuple) {
 		// NOTE(bill): Panic on invalid conversion
-
 		lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
 		auto args = array_make<lbValue>(permanent_allocator(), 7);
 		args[0] = ok;

+ 2 - 1
src/main.cpp

@@ -485,7 +485,7 @@ i32 linker_stage(lbGenerator *gen) {
 				// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
 				//       make sure to also change the 'mtriple' param passed to 'opt'
 				#if defined(GB_CPU_ARM)
-				" -mmacosx-version-min=11.0.0 "
+				" -mmacosx-version-min=12.0.0 "
 				#else
 				" -mmacosx-version-min=10.8.0 "
 				#endif
@@ -1851,6 +1851,7 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(1, "          one must contain the program's entry point, all must be in the same package.");
 	} else if (command == "run") {
 		print_usage_line(1, "run       same as 'build', but also then runs the newly compiled executable.");
+		print_usage_line(1, "          append an empty flag and then the args, '-- <args>', to specify args for the output.");
 	} else if (command == "check") {
 		print_usage_line(1, "check     parse and type check .odin file(s)");
 	} else if (command == "test") {

+ 123 - 7
src/parser.cpp

@@ -183,6 +183,11 @@ Ast *clone_ast(Ast *node) {
 		n->FieldValue.value = clone_ast(n->FieldValue.value);
 		break;
 
+	case Ast_EnumFieldValue:
+		n->EnumFieldValue.name = clone_ast(n->EnumFieldValue.name);
+		n->EnumFieldValue.value = clone_ast(n->EnumFieldValue.value);
+		break;
+
 	case Ast_TernaryIfExpr:
 		n->TernaryIfExpr.x    = clone_ast(n->TernaryIfExpr.x);
 		n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond);
@@ -693,6 +698,16 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) {
 	return result;
 }
 
+
+Ast *ast_enum_field_value(AstFile *f, Ast *name, Ast *value, CommentGroup *docs, CommentGroup *comment) {
+	Ast *result = alloc_ast_node(f, Ast_EnumFieldValue);
+	result->EnumFieldValue.name = name;
+	result->EnumFieldValue.value = value;
+	result->EnumFieldValue.docs = docs;
+	result->EnumFieldValue.comment = comment;
+	return result;
+}
+
 Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> const &elems, Token open, Token close) {
 	Ast *result = alloc_ast_node(f, Ast_CompoundLit);
 	result->CompoundLit.type = type;
@@ -944,7 +959,7 @@ Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast *default_va
 	result->Field.default_value = default_value;
 	result->Field.flags         = flags;
 	result->Field.tag           = tag;
-	result->Field.docs = docs;
+	result->Field.docs          = docs;
 	result->Field.comment       = comment;
 	return result;
 }
@@ -1234,7 +1249,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
 	return comments;
 }
 
-void comsume_comment_groups(AstFile *f, Token prev) {
+void consume_comment_groups(AstFile *f, Token prev) {
 	if (f->curr_token.kind == Token_Comment) {
 		CommentGroup *comment = nullptr;
 		isize end_line = 0;
@@ -1278,7 +1293,7 @@ Token advance_token(AstFile *f) {
 	if (ok) {
 		switch (f->curr_token.kind) {
 		case Token_Comment:
-			comsume_comment_groups(f, prev);
+			consume_comment_groups(f, prev);
 			break;
 		case Token_Semicolon:
 			if (ignore_newlines(f) && f->curr_token.string == "\n") {
@@ -1689,6 +1704,46 @@ Array<Ast *> parse_element_list(AstFile *f) {
 
 	return elems;
 }
+CommentGroup *consume_line_comment(AstFile *f) {
+	CommentGroup *comment = f->line_comment;
+	if (f->line_comment == f->lead_comment) {
+		f->lead_comment = nullptr;
+	}
+	f->line_comment = nullptr;
+	return comment;
+
+}
+
+Array<Ast *> parse_enum_field_list(AstFile *f) {
+	auto elems = array_make<Ast *>(heap_allocator());
+
+	while (f->curr_token.kind != Token_CloseBrace &&
+	       f->curr_token.kind != Token_EOF) {
+		CommentGroup *docs = f->lead_comment;
+		CommentGroup *comment = nullptr;
+		Ast *name = parse_value(f);
+		Ast *value = nullptr;
+		if (f->curr_token.kind == Token_Eq) {
+			Token eq = expect_token(f, Token_Eq);
+			value = parse_value(f);
+		}
+
+		comment = consume_line_comment(f);
+
+		Ast *elem = ast_enum_field_value(f, name, value, docs, comment);
+		array_add(&elems, elem);
+
+		if (!allow_token(f, Token_Comma)) {
+			break;
+		}
+
+		if (!elem->EnumFieldValue.comment) {
+			elem->EnumFieldValue.comment = consume_line_comment(f);
+		}
+	}
+
+	return elems;
+}
 
 Ast *parse_literal_value(AstFile *f, Ast *type) {
 	Array<Ast *> elems = {};
@@ -1793,6 +1848,8 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
 		ELSE_IF_ADD_TAG(require_results)
 		ELSE_IF_ADD_TAG(bounds_check)
 		ELSE_IF_ADD_TAG(no_bounds_check)
+		ELSE_IF_ADD_TAG(type_assert)
+		ELSE_IF_ADD_TAG(no_type_assert)
 		else {
 			syntax_error(tag_expr, "Unknown procedure type tag #%.*s", LIT(tag_name));
 		}
@@ -1803,6 +1860,10 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
 	if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) {
 		syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
 	}
+
+	if ((*tags & ProcTag_type_assert) && (*tags & ProcTag_no_type_assert)) {
+		syntax_error(f->curr_token, "You cannot apply both #type_assert and #no_type_assert to a procedure");
+	}
 }
 
 
@@ -1950,11 +2011,23 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta
 			syntax_error(tag_token, "#bounds_check and #no_bounds_check cannot be applied together");
 		}
 		break;
+	case StateFlag_type_assert:
+		if ((s->state_flags & StateFlag_no_type_assert) != 0) {
+			syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together");
+		}
+		break;
+	case StateFlag_no_type_assert:
+		if ((s->state_flags & StateFlag_type_assert) != 0) {
+			syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together");
+		}
+		break;
 	}
 
 	switch (state_flag) {
 	case StateFlag_bounds_check:
 	case StateFlag_no_bounds_check:
+	case StateFlag_type_assert:
+	case StateFlag_no_type_assert:
 		switch (s->kind) {
 		case Ast_BlockStmt:
 		case Ast_IfStmt:
@@ -2062,6 +2135,22 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 			}
 			return original_type;
 		} else if (name.string == "partial") {
+			Ast *tag = ast_basic_directive(f, token, name);
+			Ast *original_expr = parse_expr(f, lhs);
+			Ast *expr = unparen_expr(original_expr);
+			switch (expr->kind) {
+			case Ast_ArrayType:
+				syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types");
+				break;
+			case Ast_CompoundLit:
+				expr->CompoundLit.tag = tag;
+				break;
+			default:
+				syntax_error(expr, "Expected a compound literal after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[expr->kind]));
+				break;
+			}
+			return original_expr;
+		} else if (name.string == "sparse") {
 			Ast *tag = ast_basic_directive(f, token, name);
 			Ast *original_type = parse_type(f);
 			Ast *type = unparen_expr(original_type);
@@ -2078,6 +2167,12 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		} else if (name.string == "no_bounds_check") {
 			Ast *operand = parse_expr(f, lhs);
 			return parse_check_directive_for_statement(operand, name, StateFlag_no_bounds_check);
+		} else if (name.string == "type_assert") {
+			Ast *operand = parse_expr(f, lhs);
+			return parse_check_directive_for_statement(operand, name, StateFlag_type_assert);
+		} else if (name.string == "no_type_assert") {
+			Ast *operand = parse_expr(f, lhs);
+			return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert);
 		} else if (name.string == "relative") {
 			Ast *tag = ast_basic_directive(f, token, name);
 			tag = parse_call_expr(f, tag);
@@ -2174,6 +2269,12 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 			if (tags & ProcTag_bounds_check) {
 				body->state_flags |= StateFlag_bounds_check;
 			}
+			if (tags & ProcTag_no_type_assert) {
+				body->state_flags |= StateFlag_no_type_assert;
+			}
+			if (tags & ProcTag_type_assert) {
+				body->state_flags |= StateFlag_type_assert;
+			}
 
 			return ast_proc_lit(f, type, body, tags, where_token, where_clauses);
 		} else if (allow_token(f, Token_do)) {
@@ -2449,7 +2550,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		skip_possible_newline_for_literal(f);
 		Token open = expect_token(f, Token_OpenBrace);
 
-		Array<Ast *> values = parse_element_list(f);
+		Array<Ast *> values = parse_enum_field_list(f);
 		Token close = expect_closing_brace_of_field_list(f);
 
 		return ast_enum_type(f, token, base_type, values);
@@ -4561,6 +4662,12 @@ Ast *parse_stmt(AstFile *f) {
 		} else if (tag == "no_bounds_check") {
 			s = parse_stmt(f);
 			return parse_check_directive_for_statement(s, name, StateFlag_no_bounds_check);
+		} else if (tag == "type_assert") {
+			s = parse_stmt(f);
+			return parse_check_directive_for_statement(s, name, StateFlag_type_assert);
+		} else if (tag == "no_type_assert") {
+			s = parse_stmt(f);
+			return parse_check_directive_for_statement(s, name, StateFlag_no_type_assert);
 		} else if (tag == "partial") {
 			s = parse_stmt(f);
 			switch (s->kind) {
@@ -5398,7 +5505,7 @@ bool parse_file(Parser *p, AstFile *f) {
 	String filepath = f->tokenizer.fullpath;
 	String base_dir = dir_from_path(filepath);
 	if (f->curr_token.kind == Token_Comment) {
-		comsume_comment_groups(f, f->prev_token);
+		consume_comment_groups(f, f->prev_token);
 	}
 
 	CommentGroup *docs = f->lead_comment;
@@ -5444,8 +5551,17 @@ bool parse_file(Parser *p, AstFile *f) {
 						if (!parse_build_tag(tok, lc)) {
 							return false;
 						}
-					} else if (lc == "+private") {
-						f->flags |= AstFile_IsPrivate;
+					} else if (string_starts_with(lc, str_lit("+private"))) {
+						f->flags |= AstFile_IsPrivatePkg;
+						String command = string_trim_starts_with(lc, str_lit("+private "));
+						command = string_trim_whitespace(command);
+						if (lc == "+private") {
+							f->flags |= AstFile_IsPrivatePkg;
+						} else if (command == "package") {
+							f->flags |= AstFile_IsPrivatePkg;
+						} else if (command == "file") {
+							f->flags |= AstFile_IsPrivateFile;
+						}
 					} else if (lc == "+lazy") {
 						if (build_context.ignore_lazy) {
 							// Ignore

+ 16 - 3
src/parser.hpp

@@ -78,9 +78,11 @@ struct ImportedFile {
 };
 
 enum AstFileFlag : u32 {
-	AstFile_IsPrivate = 1<<0,
-	AstFile_IsTest    = 1<<1,
-	AstFile_IsLazy    = 1<<2,
+	AstFile_IsPrivatePkg = 1<<0,
+	AstFile_IsPrivateFile = 1<<1,
+
+	AstFile_IsTest    = 1<<3,
+	AstFile_IsLazy    = 1<<4,
 };
 
 enum AstDelayQueueKind {
@@ -226,6 +228,8 @@ enum ProcInlining {
 enum ProcTag {
 	ProcTag_bounds_check    = 1<<0,
 	ProcTag_no_bounds_check = 1<<1,
+	ProcTag_type_assert     = 1<<2,
+	ProcTag_no_type_assert  = 1<<3,
 
 	ProcTag_require_results = 1<<4,
 	ProcTag_optional_ok     = 1<<5,
@@ -258,6 +262,8 @@ ProcCallingConvention default_calling_convention(void) {
 enum StateFlag : u8 {
 	StateFlag_bounds_check    = 1<<0,
 	StateFlag_no_bounds_check = 1<<1,
+	StateFlag_type_assert     = 1<<2,
+	StateFlag_no_type_assert  = 1<<3,
 
 	StateFlag_BeenHandled = 1<<7,
 };
@@ -344,6 +350,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
 		Slice<Ast *> elems; \
 		Token open, close; \
 		i64 max_count; \
+		Ast *tag; \
 	}) \
 AST_KIND(_ExprBegin,  "",  bool) \
 	AST_KIND(BadExpr,      "bad expression",         struct { Token begin, end; }) \
@@ -383,6 +390,12 @@ AST_KIND(_ExprBegin,  "",  bool) \
 		void *sce_temp_data; \
 	}) \
 	AST_KIND(FieldValue,      "field value",              struct { Token eq; Ast *field, *value; }) \
+	AST_KIND(EnumFieldValue,  "enum field value",         struct { \
+		Ast *name;          \
+		Ast *value;         \
+		CommentGroup *docs; \
+		CommentGroup *comment; \
+	}) \
 	AST_KIND(TernaryIfExpr,   "ternary if expression",    struct { Ast *x, *cond, *y; }) \
 	AST_KIND(TernaryWhenExpr, "ternary when expression",  struct { Ast *x, *cond, *y; }) \
 	AST_KIND(OrElseExpr,      "or_else expression",       struct { Ast *x; Token token; Ast *y; }) \

+ 6 - 0
src/parser_pos.cpp

@@ -39,6 +39,7 @@ Token ast_token(Ast *node) {
 	case Ast_SliceExpr:          return node->SliceExpr.open;
 	case Ast_Ellipsis:           return node->Ellipsis.token;
 	case Ast_FieldValue:         return node->FieldValue.eq;
+	case Ast_EnumFieldValue:     return ast_token(node->EnumFieldValue.name);
 	case Ast_DerefExpr:          return node->DerefExpr.op;
 	case Ast_TernaryIfExpr:      return ast_token(node->TernaryIfExpr.x);
 	case Ast_TernaryWhenExpr:    return ast_token(node->TernaryWhenExpr.x);
@@ -178,6 +179,11 @@ Token ast_end_token(Ast *node) {
 		}
 		return node->Ellipsis.token;
 	case Ast_FieldValue:         return ast_end_token(node->FieldValue.value);
+	case Ast_EnumFieldValue:
+		if (node->EnumFieldValue.value) {
+			return ast_end_token(node->EnumFieldValue.value);
+		}
+		return ast_end_token(node->EnumFieldValue.name);
 	case Ast_DerefExpr:          return node->DerefExpr.op;
 	case Ast_TernaryIfExpr:      return ast_end_token(node->TernaryIfExpr.y);
 	case Ast_TernaryWhenExpr:    return ast_end_token(node->TernaryWhenExpr.y);

+ 9 - 0
src/ptr_set.cpp

@@ -138,6 +138,15 @@ gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
 	return index != MAP_SENTINEL;
 }
 
+template <typename T>
+gb_inline isize ptr_entry_index(PtrSet<T> *s, T ptr) {
+	isize index = ptr_set__find(s, ptr).entry_index;
+	if (index != MAP_SENTINEL) {
+		return index;
+	}
+	return -1;
+}
+
 // Returns true if it already exists
 template <typename T>
 T ptr_set_add(PtrSet<T> *s, T ptr) {

+ 10 - 2
src/string.cpp

@@ -195,8 +195,6 @@ template <isize N> bool operator >  (String const &a, char const (&b)[N]) { retu
 template <isize N> bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
 template <isize N> bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
 
-
-
 gb_inline bool string_starts_with(String const &s, String const &prefix) {
 	if (prefix.len > s.len) {
 		return false;
@@ -230,6 +228,16 @@ gb_inline bool string_ends_with(String const &s, u8 suffix) {
 	return s[s.len-1] == suffix;
 }
 
+
+
+gb_inline String string_trim_starts_with(String const &s, String const &prefix) {
+	if (string_starts_with(s, prefix)) {
+		return substring(s, prefix.len, s.len);
+	}
+	return s;
+}
+
+
 gb_inline isize string_extension_position(String const &str) {
 	isize dot_pos = -1;
 	isize i = str.len;

Some files were not shown because too many files changed in this diff