소스 검색

Merge branch 'master' into windows-llvm-11.1.0

gingerBill 3 년 전
부모
커밋
36ea22c046

+ 37 - 4
core/math/linalg/glsl/linalg_glsl.odin

@@ -769,6 +769,31 @@ step_dvec2 :: proc "c" (edge, x: dvec2) -> dvec2 { return {step(edge.x, x.x), st
 step_dvec3 :: proc "c" (edge, x: dvec3) -> dvec3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
 step_dvec4 :: proc "c" (edge, x: dvec4) -> dvec4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
 
+smoothstep :: proc{
+	smoothstep_f32,
+	smoothstep_f64,
+	smoothstep_vec2,
+	smoothstep_vec3,
+	smoothstep_vec4,
+	smoothstep_dvec2,
+	smoothstep_dvec3,
+	smoothstep_dvec4,
+}
+smoothstep_f32 :: proc "c" (edge0, edge1, x: f32) -> f32 { 
+	y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
+	return y * y * (3 - 2*y)
+}
+smoothstep_f64 :: proc "c" (edge0, edge1, x: f64) -> f64 { 
+	y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
+	return y * y * (3 - 2*y)
+}
+smoothstep_vec2  :: proc "c" (edge0, edge1, x: vec2) -> vec2   { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+smoothstep_vec3  :: proc "c" (edge0, edge1, x: vec3) -> vec3   { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+smoothstep_vec4  :: proc "c" (edge0, edge1, x: vec4) -> vec4   { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+smoothstep_dvec2 :: proc "c" (edge0, edge1, x: dvec2) -> dvec2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+smoothstep_dvec3 :: proc "c" (edge0, edge1, x: dvec3) -> dvec3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+smoothstep_dvec4 :: proc "c" (edge0, edge1, x: dvec4) -> dvec4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+
 
 abs :: proc{
 	abs_i32,
@@ -1765,16 +1790,24 @@ dquatMulDvec3 :: proc "c" (q: dquat, v: dvec3) -> dvec3 {
 
 
 
-inverse_mat2 :: proc "c" (m: mat2) -> mat2 { return builtin.inverse(m) }
-inverse_mat3 :: proc "c" (m: mat3) -> mat3 { return builtin.inverse(m) }
-inverse_mat4 :: proc "c" (m: mat4) -> mat4 { return builtin.inverse(m) }
-inverse_quat :: proc "c" (q: quat) -> quat { return 1/q }
+inverse_mat2  :: proc "c" (m: mat2)  -> mat2  { return builtin.inverse(m) }
+inverse_mat3  :: proc "c" (m: mat3)  -> mat3  { return builtin.inverse(m) }
+inverse_mat4  :: proc "c" (m: mat4)  -> mat4  { return builtin.inverse(m) }
+inverse_dmat2 :: proc "c" (m: dmat2) -> dmat2 { return builtin.inverse(m) }
+inverse_dmat3 :: proc "c" (m: dmat3) -> dmat3 { return builtin.inverse(m) }
+inverse_dmat4 :: proc "c" (m: dmat4) -> dmat4 { return builtin.inverse(m) }
+inverse_quat  :: proc "c" (q: quat)  -> quat  { return 1/q }
+inverse_dquat :: proc "c" (q: dquat) -> dquat { return 1/q }
 
 inverse :: proc{
 	inverse_mat2,
 	inverse_mat3,
 	inverse_mat4,
+	inverse_dmat2,
+	inverse_dmat3,
+	inverse_dmat4,
 	inverse_quat,
+	inverse_dquat,
 }
 
 transpose         :: builtin.transpose

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

@@ -0,0 +1,1772 @@
+// core:math/linalg/hlsl implements a HLSL-like mathematics library plus numerous other utility procedures
+package math_linalg_hlsl
+
+import "core:builtin"
+
+TAU :: 6.28318530717958647692528676655900576
+PI  :: 3.14159265358979323846264338327950288
+E   :: 2.71828182845904523536
+τ   :: TAU
+π   :: PI
+e   :: E
+
+SQRT_TWO   :: 1.41421356237309504880168872420969808
+SQRT_THREE :: 1.73205080756887729352744634150587236
+SQRT_FIVE  :: 2.23606797749978969640917366873127623
+
+LN2  :: 0.693147180559945309417232121458176568
+LN10 :: 2.30258509299404568401799145468436421
+
+FLOAT_EPSILON :: 1e-7
+DOUBLE_EPSILON :: 1e-15
+
+// Aliases (not distinct) of types
+float  :: f32
+double :: f64
+int    :: builtin.i32
+uint   :: builtin.u32
+
+// Odin matrices are stored internally as Column-Major, which matches the internal layout of HLSL by default
+float1x1 :: distinct matrix[1, 1]float
+float2x2 :: distinct matrix[2, 2]float
+float3x3 :: distinct matrix[3, 3]float
+float4x4 :: distinct matrix[4, 4]float
+
+float1x2 :: distinct matrix[1, 2]float
+float1x3 :: distinct matrix[1, 3]float
+float1x4 :: distinct matrix[1, 4]float
+float2x1 :: distinct matrix[2, 1]float
+float2x3 :: distinct matrix[2, 3]float
+float2x4 :: distinct matrix[2, 4]float
+float3x1 :: distinct matrix[3, 1]float
+float3x2 :: distinct matrix[3, 2]float
+float3x4 :: distinct matrix[3, 4]float
+float4x1 :: distinct matrix[4, 1]float
+float4x2 :: distinct matrix[4, 2]float
+float4x3 :: distinct matrix[4, 3]float
+
+float2 :: distinct [2]float
+float3 :: distinct [3]float
+float4 :: distinct [4]float
+
+int2 :: distinct [2]int
+int3 :: distinct [3]int
+int4 :: distinct [4]int
+
+uint2 :: distinct [2]uint
+uint3 :: distinct [3]uint
+uint4 :: distinct [4]uint
+
+bool2 :: distinct [2]bool
+bool3 :: distinct [3]bool
+bool4 :: distinct [4]bool
+
+// Double Precision (double) Floating Point Types 
+
+double1x1 :: distinct matrix[1, 1]double
+double2x2 :: distinct matrix[2, 2]double
+double3x3 :: distinct matrix[3, 3]double
+double4x4 :: distinct matrix[4, 4]double
+
+double1x2 :: distinct matrix[1, 2]double
+double1x3 :: distinct matrix[1, 3]double
+double1x4 :: distinct matrix[1, 4]double
+double2x1 :: distinct matrix[2, 1]double
+double2x3 :: distinct matrix[2, 3]double
+double2x4 :: distinct matrix[2, 4]double
+double3x1 :: distinct matrix[3, 1]double
+double3x2 :: distinct matrix[3, 2]double
+double3x4 :: distinct matrix[3, 4]double
+double4x1 :: distinct matrix[4, 1]double
+double4x2 :: distinct matrix[4, 2]double
+double4x3 :: distinct matrix[4, 3]double
+
+double2 :: distinct [2]double
+double3 :: distinct [3]double
+double4 :: distinct [4]double
+
+
+int1x1 :: distinct matrix[1, 1]int
+int2x2 :: distinct matrix[2, 2]int
+int3x3 :: distinct matrix[3, 3]int
+int4x4 :: distinct matrix[4, 4]int
+
+int1x2 :: distinct matrix[1, 2]int
+int1x3 :: distinct matrix[1, 3]int
+int1x4 :: distinct matrix[1, 4]int
+int2x1 :: distinct matrix[2, 1]int
+int2x3 :: distinct matrix[2, 3]int
+int2x4 :: distinct matrix[2, 4]int
+int3x1 :: distinct matrix[3, 1]int
+int3x2 :: distinct matrix[3, 2]int
+int3x4 :: distinct matrix[3, 4]int
+int4x1 :: distinct matrix[4, 1]int
+int4x2 :: distinct matrix[4, 2]int
+int4x3 :: distinct matrix[4, 3]int
+
+cos :: proc{
+	cos_float,
+	cos_double,
+	cos_float2,
+	cos_float3,
+	cos_float4,
+	cos_double2,
+	cos_double3,
+	cos_double4,
+}
+cos_float2 :: proc "c" (x: float2) -> float2 { return {cos(x.x), cos(x.y)} }
+cos_float3 :: proc "c" (x: float3) -> float3 { return {cos(x.x), cos(x.y), cos(x.z)} }
+cos_float4 :: proc "c" (x: float4) -> float4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
+cos_double2 :: proc "c" (x: double2) -> double2 { return {cos(x.x), cos(x.y)} }
+cos_double3 :: proc "c" (x: double3) -> double3 { return {cos(x.x), cos(x.y), cos(x.z)} }
+cos_double4 :: proc "c" (x: double4) -> double4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
+
+sin :: proc{
+	sin_float,
+	sin_double,
+	sin_float2,
+	sin_float3,
+	sin_float4,
+	sin_double2,
+	sin_double3,
+	sin_double4,
+}
+sin_float2 :: proc "c" (x: float2) -> float2 { return {sin(x.x), sin(x.y)} }
+sin_float3 :: proc "c" (x: float3) -> float3 { return {sin(x.x), sin(x.y), sin(x.z)} }
+sin_float4 :: proc "c" (x: float4) -> float4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
+sin_double2 :: proc "c" (x: double2) -> double2 { return {sin(x.x), sin(x.y)} }
+sin_double3 :: proc "c" (x: double3) -> double3 { return {sin(x.x), sin(x.y), sin(x.z)} }
+sin_double4 :: proc "c" (x: double4) -> double4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
+
+tan :: proc{
+	tan_float,
+	tan_double,
+	tan_float2,
+	tan_float3,
+	tan_float4,
+	tan_double2,
+	tan_double3,
+	tan_double4,
+}
+tan_float2 :: proc "c" (x: float2) -> float2 { return {tan(x.x), tan(x.y)} }
+tan_float3 :: proc "c" (x: float3) -> float3 { return {tan(x.x), tan(x.y), tan(x.z)} }
+tan_float4 :: proc "c" (x: float4) -> float4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
+tan_double2 :: proc "c" (x: double2) -> double2 { return {tan(x.x), tan(x.y)} }
+tan_double3 :: proc "c" (x: double3) -> double3 { return {tan(x.x), tan(x.y), tan(x.z)} }
+tan_double4 :: proc "c" (x: double4) -> double4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
+
+acos :: proc{
+	acos_float,
+	acos_double,
+	acos_float2,
+	acos_float3,
+	acos_float4,
+	acos_double2,
+	acos_double3,
+	acos_double4,
+}
+acos_float2 :: proc "c" (x: float2) -> float2 { return {acos(x.x), acos(x.y)} }
+acos_float3 :: proc "c" (x: float3) -> float3 { return {acos(x.x), acos(x.y), acos(x.z)} }
+acos_float4 :: proc "c" (x: float4) -> float4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
+acos_double2 :: proc "c" (x: double2) -> double2 { return {acos(x.x), acos(x.y)} }
+acos_double3 :: proc "c" (x: double3) -> double3 { return {acos(x.x), acos(x.y), acos(x.z)} }
+acos_double4 :: proc "c" (x: double4) -> double4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
+
+asin :: proc{
+	asin_float,
+	asin_double,
+	asin_float2,
+	asin_float3,
+	asin_float4,
+	asin_double2,
+	asin_double3,
+	asin_double4,
+}
+asin_float2 :: proc "c" (x: float2) -> float2 { return {asin(x.x), asin(x.y)} }
+asin_float3 :: proc "c" (x: float3) -> float3 { return {asin(x.x), asin(x.y), asin(x.z)} }
+asin_float4 :: proc "c" (x: float4) -> float4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
+asin_double2 :: proc "c" (x: double2) -> double2 { return {asin(x.x), asin(x.y)} }
+asin_double3 :: proc "c" (x: double3) -> double3 { return {asin(x.x), asin(x.y), asin(x.z)} }
+asin_double4 :: proc "c" (x: double4) -> double4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
+
+atan :: proc{
+	atan_float,
+	atan_double,
+	atan_float2,
+	atan_float3,
+	atan_float4,
+	atan_double2,
+	atan_double3,
+	atan_double4,
+	atan2_float,
+	atan2_double,
+	atan2_float2,
+	atan2_float3,
+	atan2_float4,
+	atan2_double2,
+	atan2_double3,
+	atan2_double4,
+}
+atan_float2 :: proc "c" (x: float2) -> float2 { return {atan(x.x), atan(x.y)} }
+atan_float3 :: proc "c" (x: float3) -> float3 { return {atan(x.x), atan(x.y), atan(x.z)} }
+atan_float4 :: proc "c" (x: float4) -> float4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
+atan_double2 :: proc "c" (x: double2) -> double2 { return {atan(x.x), atan(x.y)} }
+atan_double3 :: proc "c" (x: double3) -> double3 { return {atan(x.x), atan(x.y), atan(x.z)} }
+atan_double4 :: proc "c" (x: double4) -> double4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
+
+atan2 :: proc{
+	atan2_float,
+	atan2_double,
+	atan2_float2,
+	atan2_float3,
+	atan2_float4,
+	atan2_double2,
+	atan2_double3,
+	atan2_double4,
+}
+atan2_float2 :: proc "c" (y, x: float2) -> float2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
+atan2_float3 :: proc "c" (y, x: float3) -> float3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
+atan2_float4 :: proc "c" (y, x: float4) -> float4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
+atan2_double2 :: proc "c" (y, x: double2) -> double2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
+atan2_double3 :: proc "c" (y, x: double3) -> double3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
+atan2_double4 :: proc "c" (y, x: double4) -> double4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
+
+
+
+cosh :: proc{
+	cosh_float,
+	cosh_double,
+	cosh_float2,
+	cosh_float3,
+	cosh_float4,
+	cosh_double2,
+	cosh_double3,
+	cosh_double4,
+}
+cosh_float2 :: proc "c" (x: float2) -> float2 { return {cosh(x.x), cosh(x.y)} }
+cosh_float3 :: proc "c" (x: float3) -> float3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
+cosh_float4 :: proc "c" (x: float4) -> float4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
+cosh_double2 :: proc "c" (x: double2) -> double2 { return {cosh(x.x), cosh(x.y)} }
+cosh_double3 :: proc "c" (x: double3) -> double3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
+cosh_double4 :: proc "c" (x: double4) -> double4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
+
+
+sinh :: proc{
+	sinh_float,
+	sinh_double,
+	sinh_float2,
+	sinh_float3,
+	sinh_float4,
+	sinh_double2,
+	sinh_double3,
+	sinh_double4,
+}
+sinh_float2 :: proc "c" (x: float2) -> float2 { return {sinh(x.x), sinh(x.y)} }
+sinh_float3 :: proc "c" (x: float3) -> float3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
+sinh_float4 :: proc "c" (x: float4) -> float4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
+sinh_double2 :: proc "c" (x: double2) -> double2 { return {sinh(x.x), sinh(x.y)} }
+sinh_double3 :: proc "c" (x: double3) -> double3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
+sinh_double4 :: proc "c" (x: double4) -> double4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
+
+tanh :: proc{
+	tanh_float,
+	tanh_double,
+	tanh_float2,
+	tanh_float3,
+	tanh_float4,
+	tanh_double2,
+	tanh_double3,
+	tanh_double4,
+}
+tanh_float2 :: proc "c" (x: float2) -> float2 { return {tanh(x.x), tanh(x.y)} }
+tanh_float3 :: proc "c" (x: float3) -> float3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
+tanh_float4 :: proc "c" (x: float4) -> float4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
+tanh_double2 :: proc "c" (x: double2) -> double2 { return {tanh(x.x), tanh(x.y)} }
+tanh_double3 :: proc "c" (x: double3) -> double3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
+tanh_double4 :: proc "c" (x: double4) -> double4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
+
+acosh :: proc{
+	acosh_float,
+	acosh_double,
+	acosh_float2,
+	acosh_float3,
+	acosh_float4,
+	acosh_double2,
+	acosh_double3,
+	acosh_double4,
+}
+acosh_float2 :: proc "c" (x: float2) -> float2 { return {acosh(x.x), acosh(x.y)} }
+acosh_float3 :: proc "c" (x: float3) -> float3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
+acosh_float4 :: proc "c" (x: float4) -> float4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
+acosh_double2 :: proc "c" (x: double2) -> double2 { return {acosh(x.x), acosh(x.y)} }
+acosh_double3 :: proc "c" (x: double3) -> double3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
+acosh_double4 :: proc "c" (x: double4) -> double4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
+
+asinh :: proc{
+	asinh_float,
+	asinh_double,
+	asinh_float2,
+	asinh_float3,
+	asinh_float4,
+	asinh_double2,
+	asinh_double3,
+	asinh_double4,
+}
+asinh_float2 :: proc "c" (x: float2) -> float2 { return {asinh(x.x), asinh(x.y)} }
+asinh_float3 :: proc "c" (x: float3) -> float3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
+asinh_float4 :: proc "c" (x: float4) -> float4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
+asinh_double2 :: proc "c" (x: double2) -> double2 { return {asinh(x.x), asinh(x.y)} }
+asinh_double3 :: proc "c" (x: double3) -> double3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
+asinh_double4 :: proc "c" (x: double4) -> double4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
+
+atanh :: proc{
+	atanh_float,
+	atanh_double,
+	atanh_float2,
+	atanh_float3,
+	atanh_float4,
+	atanh_double2,
+	atanh_double3,
+	atanh_double4,
+}
+atanh_float2 :: proc "c" (x: float2) -> float2 { return {atanh(x.x), atanh(x.y)} }
+atanh_float3 :: proc "c" (x: float3) -> float3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
+atanh_float4 :: proc "c" (x: float4) -> float4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
+atanh_double2 :: proc "c" (x: double2) -> double2 { return {atanh(x.x), atanh(x.y)} }
+atanh_double3 :: proc "c" (x: double3) -> double3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
+atanh_double4 :: proc "c" (x: double4) -> double4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
+
+sqrt :: proc{
+	sqrt_float,
+	sqrt_double,
+	sqrt_float2,
+	sqrt_float3,
+	sqrt_float4,
+	sqrt_double2,
+	sqrt_double3,
+	sqrt_double4,
+}
+sqrt_float2 :: proc "c" (x: float2) -> float2 { return {sqrt(x.x), sqrt(x.y)} }
+sqrt_float3 :: proc "c" (x: float3) -> float3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
+sqrt_float4 :: proc "c" (x: float4) -> float4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
+sqrt_double2 :: proc "c" (x: double2) -> double2 { return {sqrt(x.x), sqrt(x.y)} }
+sqrt_double3 :: proc "c" (x: double3) -> double3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
+sqrt_double4 :: proc "c" (x: double4) -> double4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
+
+rsqrt :: proc{
+	rsqrt_float,
+	rsqrt_double,
+	rsqrt_float2,
+	rsqrt_float3,
+	rsqrt_float4,
+	rsqrt_double2,
+	rsqrt_double3,
+	rsqrt_double4,
+}
+rsqrt_float2 :: proc "c" (x: float2) -> float2 { return {rsqrt(x.x), rsqrt(x.y)} }
+rsqrt_float3 :: proc "c" (x: float3) -> float3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} }
+rsqrt_float4 :: proc "c" (x: float4) -> float4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} }
+rsqrt_double2 :: proc "c" (x: double2) -> double2 { return {rsqrt(x.x), rsqrt(x.y)} }
+rsqrt_double3 :: proc "c" (x: double3) -> double3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} }
+rsqrt_double4 :: proc "c" (x: double4) -> double4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} }
+
+rcp :: proc{
+	rcp_float,
+	rcp_double,
+	rcp_float2,
+	rcp_float3,
+	rcp_float4,
+	rcp_double2,
+	rcp_double3,
+	rcp_double4,
+}
+rcp_float2 :: proc "c" (x: float2) -> float2 { return {rcp(x.x), rcp(x.y)} }
+rcp_float3 :: proc "c" (x: float3) -> float3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} }
+rcp_float4 :: proc "c" (x: float4) -> float4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} }
+rcp_double2 :: proc "c" (x: double2) -> double2 { return {rcp(x.x), rcp(x.y)} }
+rcp_double3 :: proc "c" (x: double3) -> double3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} }
+rcp_double4 :: proc "c" (x: double4) -> double4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} }
+
+
+pow :: proc{
+	pow_float,
+	pow_double,
+	pow_float2,
+	pow_float3,
+	pow_float4,
+	pow_double2,
+	pow_double3,
+	pow_double4,
+}
+pow_float2 :: proc "c" (x, y: float2) -> float2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
+pow_float3 :: proc "c" (x, y: float3) -> float3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
+pow_float4 :: proc "c" (x, y: float4) -> float4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
+pow_double2 :: proc "c" (x, y: double2) -> double2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
+pow_double3 :: proc "c" (x, y: double3) -> double3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
+pow_double4 :: proc "c" (x, y: double4) -> double4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
+
+
+
+exp :: proc{
+	exp_float,
+	exp_double,
+	exp_float2,
+	exp_float3,
+	exp_float4,
+	exp_double2,
+	exp_double3,
+	exp_double4,
+}
+exp_float2 :: proc "c" (x: float2) -> float2 { return {exp(x.x), exp(x.y)} }
+exp_float3 :: proc "c" (x: float3) -> float3 { return {exp(x.x), exp(x.y), exp(x.z)} }
+exp_float4 :: proc "c" (x: float4) -> float4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
+exp_double2 :: proc "c" (x: double2) -> double2 { return {exp(x.x), exp(x.y)} }
+exp_double3 :: proc "c" (x: double3) -> double3 { return {exp(x.x), exp(x.y), exp(x.z)} }
+exp_double4 :: proc "c" (x: double4) -> double4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
+
+
+
+log :: proc{
+	log_float,
+	log_double,
+	log_float2,
+	log_float3,
+	log_float4,
+	log_double2,
+	log_double3,
+	log_double4,
+}
+log_float2 :: proc "c" (x: float2) -> float2 { return {log(x.x), log(x.y)} }
+log_float3 :: proc "c" (x: float3) -> float3 { return {log(x.x), log(x.y), log(x.z)} }
+log_float4 :: proc "c" (x: float4) -> float4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
+log_double2 :: proc "c" (x: double2) -> double2 { return {log(x.x), log(x.y)} }
+log_double3 :: proc "c" (x: double3) -> double3 { return {log(x.x), log(x.y), log(x.z)} }
+log_double4 :: proc "c" (x: double4) -> double4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
+
+
+log2 :: proc{
+	log2_float,
+	log2_double,
+	log2_float2,
+	log2_float3,
+	log2_float4,
+	log2_double2,
+	log2_double3,
+	log2_double4,
+}
+log2_float2 :: proc "c" (x: float2) -> float2 { return {log2(x.x), log2(x.y)} }
+log2_float3 :: proc "c" (x: float3) -> float3 { return {log2(x.x), log2(x.y), log2(x.z)} }
+log2_float4 :: proc "c" (x: float4) -> float4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} }
+log2_double2 :: proc "c" (x: double2) -> double2 { return {log2(x.x), log2(x.y)} }
+log2_double3 :: proc "c" (x: double3) -> double3 { return {log2(x.x), log2(x.y), log2(x.z)} }
+log2_double4 :: proc "c" (x: double4) -> double4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} }
+
+
+
+log10 :: proc{
+	log10_float,
+	log10_double,
+	log10_float2,
+	log10_float3,
+	log10_float4,
+	log10_double2,
+	log10_double3,
+	log10_double4,
+}
+log10_float2 :: proc "c" (x: float2) -> float2 { return {log10(x.x), log10(x.y)} }
+log10_float3 :: proc "c" (x: float3) -> float3 { return {log10(x.x), log10(x.y), log10(x.z)} }
+log10_float4 :: proc "c" (x: float4) -> float4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} }
+log10_double2 :: proc "c" (x: double2) -> double2 { return {log10(x.x), log10(x.y)} }
+log10_double3 :: proc "c" (x: double3) -> double3 { return {log10(x.x), log10(x.y), log10(x.z)} }
+log10_double4 :: proc "c" (x: double4) -> double4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} }
+
+
+
+
+exp2 :: proc{
+	exp2_float,
+	exp2_double,
+	exp2_float2,
+	exp2_float3,
+	exp2_float4,
+	exp2_double2,
+	exp2_double3,
+	exp2_double4,
+}
+exp2_float2 :: proc "c" (x: float2) -> float2 { return {exp2(x.x), exp2(x.y)} }
+exp2_float3 :: proc "c" (x: float3) -> float3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
+exp2_float4 :: proc "c" (x: float4) -> float4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
+exp2_double2 :: proc "c" (x: double2) -> double2 { return {exp2(x.x), exp2(x.y)} }
+exp2_double3 :: proc "c" (x: double3) -> double3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
+exp2_double4 :: proc "c" (x: double4) -> double4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
+
+
+sign :: proc{
+	sign_int,
+	sign_uint,
+	sign_float,
+	sign_double,
+	sign_float2,
+	sign_float3,
+	sign_float4,
+	sign_double2,
+	sign_double3,
+	sign_double4,
+	sign_int2,
+	sign_int3,
+	sign_int4,
+	sign_uint2,
+	sign_uint3,
+	sign_uint4,
+}
+sign_int :: proc "c" (x: int) -> int { return -1 if x < 0 else +1 if x > 0 else 0 }
+sign_uint :: proc "c" (x: uint) -> uint { return +1 if x > 0 else 0 }
+sign_float2 :: proc "c" (x: float2) -> float2 { return {sign(x.x), sign(x.y)} }
+sign_float3 :: proc "c" (x: float3) -> float3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+sign_float4 :: proc "c" (x: float4) -> float4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+sign_double2 :: proc "c" (x: double2) -> double2 { return {sign(x.x), sign(x.y)} }
+sign_double3 :: proc "c" (x: double3) -> double3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+sign_double4 :: proc "c" (x: double4) -> double4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+sign_int2 :: proc "c" (x: int2) -> int2 { return {sign(x.x), sign(x.y)} }
+sign_int3 :: proc "c" (x: int3) -> int3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+sign_int4 :: proc "c" (x: int4) -> int4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+sign_uint2 :: proc "c" (x: uint2) -> uint2 { return {sign(x.x), sign(x.y)} }
+sign_uint3 :: proc "c" (x: uint3) -> uint3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+sign_uint4 :: proc "c" (x: uint4) -> uint4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+
+floor :: proc{
+	floor_float,
+	floor_double,
+	floor_float2,
+	floor_float3,
+	floor_float4,
+	floor_double2,
+	floor_double3,
+	floor_double4,
+}
+floor_float2 :: proc "c" (x: float2) -> float2 { return {floor(x.x), floor(x.y)} }
+floor_float3 :: proc "c" (x: float3) -> float3 { return {floor(x.x), floor(x.y), floor(x.z)} }
+floor_float4 :: proc "c" (x: float4) -> float4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+floor_double2 :: proc "c" (x: double2) -> double2 { return {floor(x.x), floor(x.y)} }
+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)} }
+
+
+ceil :: proc{
+	ceil_float,
+	ceil_double,
+	ceil_float2,
+	ceil_float3,
+	ceil_float4,
+	ceil_double2,
+	ceil_double3,
+	ceil_double4,
+}
+ceil_float2 :: proc "c" (x: float2) -> float2 { return {ceil(x.x), ceil(x.y)} }
+ceil_float3 :: proc "c" (x: float3) -> float3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
+ceil_float4 :: proc "c" (x: float4) -> float4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
+ceil_double2 :: proc "c" (x: double2) -> double2 { return {ceil(x.x), ceil(x.y)} }
+ceil_double3 :: proc "c" (x: double3) -> double3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
+ceil_double4 :: proc "c" (x: double4) -> double4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
+
+
+fmod :: proc{
+	fmod_float,
+	fmod_double,
+	fmod_float2,
+	fmod_float3,
+	fmod_float4,
+	fmod_double2,
+	fmod_double3,
+	fmod_double4,
+}
+fmod_float2 :: proc "c" (x, y: float2) -> float2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} }
+fmod_float3 :: proc "c" (x, y: float3) -> float3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} }
+fmod_float4 :: proc "c" (x, y: float4) -> float4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} }
+fmod_double2 :: proc "c" (x, y: double2) -> double2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} }
+fmod_double3 :: proc "c" (x, y: double3) -> double3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} }
+fmod_double4 :: proc "c" (x, y: double4) -> double4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} }
+
+
+frac :: proc{
+	frac_float,
+	frac_double,
+	frac_float2,
+	frac_float3,
+	frac_float4,
+	frac_double2,
+	frac_double3,
+	frac_double4,
+}
+frac_float2 :: proc "c" (x: float2) -> float2 { return {frac(x.x), frac(x.y)} }
+frac_float3 :: proc "c" (x: float3) -> float3 { return {frac(x.x), frac(x.y), frac(x.z)} }
+frac_float4 :: proc "c" (x: float4) -> float4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} }
+frac_double2 :: proc "c" (x: double2) -> double2 { return {frac(x.x), frac(x.y)} }
+frac_double3 :: proc "c" (x: double3) -> double3 { return {frac(x.x), frac(x.y), frac(x.z)} }
+frac_double4 :: proc "c" (x: double4) -> double4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} }
+
+
+
+radians :: proc{
+	radians_float,
+	radians_double,
+	radians_float2,
+	radians_float3,
+	radians_float4,
+	radians_double2,
+	radians_double3,
+	radians_double4,
+}
+radians_float  :: proc "c" (degrees: float)  -> float  { return degrees * TAU / 360.0 }
+radians_double  :: proc "c" (degrees: double)  -> double  { return degrees * TAU / 360.0 }
+radians_float2 :: proc "c" (degrees: float2) -> float2 { return degrees * TAU / 360.0 }
+radians_float3 :: proc "c" (degrees: float3) -> float3 { return degrees * TAU / 360.0 }
+radians_float4 :: proc "c" (degrees: float4) -> float4 { return degrees * TAU / 360.0 }
+radians_double2 :: proc "c" (degrees: double2) -> double2 { return degrees * TAU / 360.0 }
+radians_double3 :: proc "c" (degrees: double3) -> double3 { return degrees * TAU / 360.0 }
+radians_double4 :: proc "c" (degrees: double4) -> double4 { return degrees * TAU / 360.0 }
+
+
+degrees :: proc{
+	degrees_float,
+	degrees_double,
+	degrees_float2,
+	degrees_float3,
+	degrees_float4,
+	degrees_double2,
+	degrees_double3,
+	degrees_double4,
+}
+degrees_float  :: proc "c" (radians: float)  -> float  { return radians * 360.0 / TAU }
+degrees_double  :: proc "c" (radians: double)  -> double  { return radians * 360.0 / TAU }
+degrees_float2 :: proc "c" (radians: float2) -> float2 { return radians * 360.0 / TAU }
+degrees_float3 :: proc "c" (radians: float3) -> float3 { return radians * 360.0 / TAU }
+degrees_float4 :: proc "c" (radians: float4) -> float4 { return radians * 360.0 / TAU }
+degrees_double2 :: proc "c" (radians: double2) -> double2 { return radians * 360.0 / TAU }
+degrees_double3 :: proc "c" (radians: double3) -> double3 { return radians * 360.0 / TAU }
+degrees_double4 :: proc "c" (radians: double4) -> double4 { return radians * 360.0 / TAU }
+
+min :: proc{
+	min_int,  
+	min_uint,  
+	min_float,  
+	min_double,
+	min_float2, 
+	min_float3, 
+	min_float4, 
+	min_double2, 
+	min_double3, 
+	min_double4, 
+	min_int2,
+	min_int3,
+	min_int4,
+	min_uint2,
+	min_uint3,
+	min_uint4,
+}
+min_int  :: proc "c" (x, y: int) -> int   { return builtin.min(x, y) }
+min_uint  :: proc "c" (x, y: uint) -> uint   { return builtin.min(x, y) }
+min_float  :: proc "c" (x, y: float) -> float   { return builtin.min(x, y) }
+min_double  :: proc "c" (x, y: double) -> double   { return builtin.min(x, y) }
+min_float2 :: proc "c" (x, y: float2) -> float2 { return {min(x.x, y.x), min(x.y, y.y)} }
+min_float3 :: proc "c" (x, y: float3) -> float3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+min_float4 :: proc "c" (x, y: float4) -> float4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+min_double2 :: proc "c" (x, y: double2) -> double2 { return {min(x.x, y.x), min(x.y, y.y)} }
+min_double3 :: proc "c" (x, y: double3) -> double3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+min_double4 :: proc "c" (x, y: double4) -> double4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+min_int2 :: proc "c" (x, y: int2) -> int2 { return {min(x.x, y.x), min(x.y, y.y)} }
+min_int3 :: proc "c" (x, y: int3) -> int3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+min_int4 :: proc "c" (x, y: int4) -> int4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+min_uint2 :: proc "c" (x, y: uint2) -> uint2 { return {min(x.x, y.x), min(x.y, y.y)} }
+min_uint3 :: proc "c" (x, y: uint3) -> uint3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+min_uint4 :: proc "c" (x, y: uint4) -> uint4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+
+
+max :: proc{
+	max_int,  
+	max_uint,  
+	max_float,  
+	max_double,
+	max_float2, 
+	max_float3, 
+	max_float4, 
+	max_double2, 
+	max_double3, 
+	max_double4, 
+	max_int2,
+	max_int3,
+	max_int4,
+	max_uint2,
+	max_uint3,
+	max_uint4,
+}
+max_int  :: proc "c" (x, y: int) -> int   { return builtin.max(x, y) }
+max_uint  :: proc "c" (x, y: uint) -> uint   { return builtin.max(x, y) }
+max_float  :: proc "c" (x, y: float) -> float   { return builtin.max(x, y) }
+max_double  :: proc "c" (x, y: double) -> double   { return builtin.max(x, y) }
+max_float2 :: proc "c" (x, y: float2) -> float2 { return {max(x.x, y.x), max(x.y, y.y)} }
+max_float3 :: proc "c" (x, y: float3) -> float3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+max_float4 :: proc "c" (x, y: float4) -> float4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+max_double2 :: proc "c" (x, y: double2) -> double2 { return {max(x.x, y.x), max(x.y, y.y)} }
+max_double3 :: proc "c" (x, y: double3) -> double3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+max_double4 :: proc "c" (x, y: double4) -> double4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+max_int2 :: proc "c" (x, y: int2) -> int2 { return {max(x.x, y.x), max(x.y, y.y)} }
+max_int3 :: proc "c" (x, y: int3) -> int3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+max_int4 :: proc "c" (x, y: int4) -> int4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+max_uint2 :: proc "c" (x, y: uint2) -> uint2 { return {max(x.x, y.x), max(x.y, y.y)} }
+max_uint3 :: proc "c" (x, y: uint3) -> uint3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+max_uint4 :: proc "c" (x, y: uint4) -> uint4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+
+
+
+clamp :: proc{
+	clamp_int, 
+	clamp_uint, 
+	clamp_float,  
+	clamp_double,
+	clamp_float2, 
+	clamp_float3, 
+	clamp_float4, 
+	clamp_double2, 
+	clamp_double3, 
+	clamp_double4, 
+	clamp_int2,
+	clamp_int3,
+	clamp_int4,
+	clamp_uint2,
+	clamp_uint3,
+	clamp_uint4,
+}
+clamp_int  :: proc "c" (x, y, z: int) -> int   { return builtin.clamp(x, y, z) }
+clamp_uint  :: proc "c" (x, y, z: uint) -> uint   { return builtin.clamp(x, y, z) }
+clamp_float  :: proc "c" (x, y, z: float) -> float   { return builtin.clamp(x, y, z) }
+clamp_double  :: proc "c" (x, y, z: double) -> double   { return builtin.clamp(x, y, z) }
+clamp_float2 :: proc "c" (x, y, z: float2) -> float2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+clamp_float3 :: proc "c" (x, y, z: float3) -> float3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+clamp_float4 :: proc "c" (x, y, z: float4) -> float4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+clamp_double2 :: proc "c" (x, y, z: double2) -> double2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+clamp_double3 :: proc "c" (x, y, z: double3) -> double3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+clamp_double4 :: proc "c" (x, y, z: double4) -> double4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+clamp_int2 :: proc "c" (x, y, z: int2) -> int2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+clamp_int3 :: proc "c" (x, y, z: int3) -> int3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+clamp_int4 :: proc "c" (x, y, z: int4) -> int4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+clamp_uint2 :: proc "c" (x, y, z: uint2) -> uint2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+clamp_uint3 :: proc "c" (x, y, z: uint3) -> uint3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+clamp_uint4 :: proc "c" (x, y, z: uint4) -> uint4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+
+saturate :: proc{
+	saturate_int,
+	saturate_uint,
+	saturate_float,
+	saturate_double,
+	saturate_float2,
+	saturate_float3,
+	saturate_float4,
+	saturate_double2,
+	saturate_double3,
+	saturate_double4,
+	saturate_int2,
+	saturate_int3,
+	saturate_int4,
+	saturate_uint2,
+	saturate_uint3,
+	saturate_uint4,
+}
+saturate_int  :: proc "c" (x, y, z: int) -> int   { return builtin.clamp(x, 0, 1) }
+saturate_uint  :: proc "c" (x, y, z: uint) -> uint   { return builtin.clamp(x, 0, 1) }
+saturate_float  :: proc "c" (x, y, z: float) -> float   { return builtin.clamp(x, 0, 1) }
+saturate_double  :: proc "c" (x, y, z: double) -> double   { return builtin.clamp(x, 0, 1) }
+saturate_float2 :: proc "c" (x, y, z: float2) -> float2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
+saturate_float3 :: proc "c" (x, y, z: float3) -> float3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
+saturate_float4 :: proc "c" (x, y, z: float4) -> float4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
+saturate_double2 :: proc "c" (x, y, z: double2) -> double2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
+saturate_double3 :: proc "c" (x, y, z: double3) -> double3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
+saturate_double4 :: proc "c" (x, y, z: double4) -> double4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
+saturate_int2 :: proc "c" (x, y, z: int2) -> int2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
+saturate_int3 :: proc "c" (x, y, z: int3) -> int3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
+saturate_int4 :: proc "c" (x, y, z: int4) -> int4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
+saturate_uint2 :: proc "c" (x, y, z: uint2) -> uint2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
+saturate_uint3 :: proc "c" (x, y, z: uint3) -> uint3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
+saturate_uint4 :: proc "c" (x, y, z: uint4) -> uint4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
+
+
+lerp :: proc{
+	lerp_float,
+	lerp_double,
+	lerp_float2,
+	lerp_float3,
+	lerp_float4,
+	lerp_double2,
+	lerp_double3,
+	lerp_double4,
+}
+lerp_float  :: proc "c" (x, y, t: float) -> float   { return x*(1-t) + y*t }
+lerp_double  :: proc "c" (x, y, t: double) -> double   { return x*(1-t) + y*t }
+lerp_float2 :: proc "c" (x, y, t: float2) -> float2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
+lerp_float3 :: proc "c" (x, y, t: float3) -> float3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
+lerp_float4 :: proc "c" (x, y, t: float4) -> float4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
+lerp_double2 :: proc "c" (x, y, t: double2) -> double2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
+lerp_double3 :: proc "c" (x, y, t: double3) -> double3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
+lerp_double4 :: proc "c" (x, y, t: double4) -> double4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
+
+
+step :: proc{
+	step_float,
+	step_double,
+	step_float2,
+	step_float3,
+	step_float4,
+	step_double2,
+	step_double3,
+	step_double4,
+}
+step_float  :: proc "c" (edge, x: float) -> float   { return 0 if x < edge else 1 }
+step_double  :: proc "c" (edge, x: double) -> double   { return 0 if x < edge else 1 }
+step_float2 :: proc "c" (edge, x: float2) -> float2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
+step_float3 :: proc "c" (edge, x: float3) -> float3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
+step_float4 :: proc "c" (edge, x: float4) -> float4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
+step_double2 :: proc "c" (edge, x: double2) -> double2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
+step_double3 :: proc "c" (edge, x: double3) -> double3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
+step_double4 :: proc "c" (edge, x: double4) -> double4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
+
+smoothstep :: proc{
+	smoothstep_float,
+	smoothstep_double,
+	smoothstep_float2,
+	smoothstep_float3,
+	smoothstep_float4,
+	smoothstep_double2,
+	smoothstep_double3,
+	smoothstep_double4,
+}
+smoothstep_float :: proc "c" (edge0, edge1, x: float) -> float { 
+	y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
+	return y * y * (3 - 2*y)
+}
+smoothstep_double :: proc "c" (edge0, edge1, x: double) -> double { 
+	y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
+	return y * y * (3 - 2*y)
+}
+smoothstep_float2  :: proc "c" (edge0, edge1, x: float2) -> float2   { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+smoothstep_float3  :: proc "c" (edge0, edge1, x: float3) -> float3   { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+smoothstep_float4  :: proc "c" (edge0, edge1, x: float4) -> float4   { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+smoothstep_double2 :: proc "c" (edge0, edge1, x: double2) -> double2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+smoothstep_double3 :: proc "c" (edge0, edge1, x: double3) -> double3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+smoothstep_double4 :: proc "c" (edge0, edge1, x: double4) -> double4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+
+
+abs :: proc{
+	abs_int,
+	abs_uint,
+	abs_float,
+	abs_double,
+	abs_float2,
+	abs_float3,
+	abs_float4,
+	abs_double2,
+	abs_double3,
+	abs_double4,
+	abs_int2,
+	abs_int3,
+	abs_int4,
+	abs_uint2,
+	abs_uint3,
+	abs_uint4,
+}
+abs_int  :: proc "c" (x: int)  -> int  { return builtin.abs(x) }
+abs_uint  :: proc "c" (x: uint)  -> uint  { return x }
+abs_float  :: proc "c" (x: float)  -> float  { return builtin.abs(x) }
+abs_double  :: proc "c" (x: double)  -> double  { return builtin.abs(x) }
+abs_float2 :: proc "c" (x: float2) -> float2 { return {abs(x.x), abs(x.y)} }
+abs_float3 :: proc "c" (x: float3) -> float3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+abs_float4 :: proc "c" (x: float4) -> float4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+abs_double2 :: proc "c" (x: double2) -> double2 { return {abs(x.x), abs(x.y)} }
+abs_double3 :: proc "c" (x: double3) -> double3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+abs_double4 :: proc "c" (x: double4) -> double4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+abs_int2 :: proc "c" (x: int2) -> int2 { return {abs(x.x), abs(x.y)} }
+abs_int3 :: proc "c" (x: int3) -> int3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+abs_int4 :: proc "c" (x: int4) -> int4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+abs_uint2 :: proc "c" (x: uint2) -> uint2 { return x }
+abs_uint3 :: proc "c" (x: uint3) -> uint3 { return x }
+abs_uint4 :: proc "c" (x: uint4) -> uint4 { return x }
+
+dot :: proc{
+	dot_int,
+	dot_uint,
+	dot_float,
+	dot_double,
+	dot_float2,
+	dot_float3,
+	dot_float4,
+	dot_double2,
+	dot_double3,
+	dot_double4,
+	dot_int2,
+	dot_int3,
+	dot_int4,
+	dot_uint2,
+	dot_uint3,
+	dot_uint4,
+}
+dot_int  :: proc "c" (a, b: int)  -> int { return a*b }
+dot_uint  :: proc "c" (a, b: uint)  -> uint { return a*b }
+dot_float  :: proc "c" (a, b: float)  -> float { return a*b }
+dot_double  :: proc "c" (a, b: double)  -> double { return a*b }
+dot_float2 :: proc "c" (a, b: float2) -> float { return a.x*b.x + a.y*b.y }
+dot_float3 :: proc "c" (a, b: float3) -> float { return a.x*b.x + a.y*b.y + a.z*b.z }
+dot_float4 :: proc "c" (a, b: float4) -> float { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+dot_double2 :: proc "c" (a, b: double2) -> double { return a.x*b.x + a.y*b.y }
+dot_double3 :: proc "c" (a, b: double3) -> double { return a.x*b.x + a.y*b.y + a.z*b.z }
+dot_double4 :: proc "c" (a, b: double4) -> double { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+dot_int2 :: proc "c" (a, b: int2) -> int { return a.x*b.x + a.y*b.y }
+dot_int3 :: proc "c" (a, b: int3) -> int { return a.x*b.x + a.y*b.y + a.z*b.z }
+dot_int4 :: proc "c" (a, b: int4) -> int { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+dot_uint2 :: proc "c" (a, b: uint2) -> uint { return a.x*b.x + a.y*b.y }
+dot_uint3 :: proc "c" (a, b: uint3) -> uint { return a.x*b.x + a.y*b.y + a.z*b.z }
+dot_uint4 :: proc "c" (a, b: uint4) -> uint { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+
+length :: proc{
+	length_float,
+	length_double,
+	length_float2,
+	length_float3,
+	length_float4,
+	length_double2,
+	length_double3,
+	length_double4,
+}
+length_float  :: proc "c" (x: float)  -> float { return builtin.abs(x) }
+length_double  :: proc "c" (x: double)  -> double { return builtin.abs(x) }
+length_float2 :: proc "c" (x: float2) -> float { return sqrt(x.x*x.x + x.y*x.y) }
+length_float3 :: proc "c" (x: float3) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
+length_float4 :: proc "c" (x: float4) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+length_double2 :: proc "c" (x: double2) -> double { return sqrt(x.x*x.x + x.y*x.y) }
+length_double3 :: proc "c" (x: double3) -> double { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
+length_double4 :: proc "c" (x: double4) -> double { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+
+
+distance :: proc{
+	distance_float,
+	distance_double,
+	distance_float2,
+	distance_float3,
+	distance_float4,
+	distance_double2,
+	distance_double3,
+	distance_double4,
+}
+distance_float  :: proc "c" (x, y: float)  -> float { return length(y-x) }
+distance_double  :: proc "c" (x, y: double)  -> double { return length(y-x) }
+distance_float2 :: proc "c" (x, y: float2) -> float { return length(y-x) }
+distance_float3 :: proc "c" (x, y: float3) -> float { return length(y-x) }
+distance_float4 :: proc "c" (x, y: float4) -> float { return length(y-x) }
+distance_double2 :: proc "c" (x, y: double2) -> double { return length(y-x) }
+distance_double3 :: proc "c" (x, y: double3) -> double { return length(y-x) }
+distance_double4 :: proc "c" (x, y: double4) -> double { return length(y-x) }
+
+
+cross :: proc{
+	cross_float3,
+	cross_double3,
+	cross_int3,
+}
+
+cross_float3 :: proc "c" (a, b: float3) -> (c: float3) {
+	c.x = a.y*b.z - b.y*a.z
+	c.y = a.z*b.x - b.z*a.x
+	c.z = a.x*b.y - b.x*a.y
+	return
+}
+cross_double3 :: proc "c" (a, b: double3) -> (c: double3) {
+	c.x = a.y*b.z - b.y*a.z
+	c.y = a.z*b.x - b.z*a.x
+	c.z = a.x*b.y - b.x*a.y
+	return
+}
+cross_int3 :: proc "c" (a, b: int3) -> (c: int3) {
+	c.x = a.y*b.z - b.y*a.z
+	c.y = a.z*b.x - b.z*a.x
+	c.z = a.x*b.y - b.x*a.y
+	return
+}
+
+normalize :: proc{
+	normalize_float,
+	normalize_double,
+	normalize_float2,
+	normalize_float3,
+	normalize_float4,
+	normalize_double2,
+	normalize_double3,
+	normalize_double4,
+}
+normalize_float  :: proc "c" (x: float)  -> float  { return 1.0 }
+normalize_double  :: proc "c" (x: double)  -> double  { return 1.0 }
+normalize_float2 :: proc "c" (x: float2) -> float2 { return x / length(x) }
+normalize_float3 :: proc "c" (x: float3) -> float3 { return x / length(x) }
+normalize_float4 :: proc "c" (x: float4) -> float4 { return x / length(x) }
+normalize_double2 :: proc "c" (x: double2) -> double2 { return x / length(x) }
+normalize_double3 :: proc "c" (x: double3) -> double3 { return x / length(x) }
+normalize_double4 :: proc "c" (x: double4) -> double4 { return x / length(x) }
+
+
+faceforward :: proc{
+	faceforward_float,
+	faceforward_double,
+	faceforward_float2,
+	faceforward_float3,
+	faceforward_float4,
+	faceforward_double2,
+	faceforward_double3,
+	faceforward_double4,
+}
+faceforward_float  :: proc "c" (N, I, Nref: float)  -> float  { return N if dot(I, Nref) < 0 else -N }
+faceforward_double  :: proc "c" (N, I, Nref: double)  -> double  { return N if dot(I, Nref) < 0 else -N }
+faceforward_float2 :: proc "c" (N, I, Nref: float2) -> float2 { return N if dot(I, Nref) < 0 else -N }
+faceforward_float3 :: proc "c" (N, I, Nref: float3) -> float3 { return N if dot(I, Nref) < 0 else -N }
+faceforward_float4 :: proc "c" (N, I, Nref: float4) -> float4 { return N if dot(I, Nref) < 0 else -N }
+faceforward_double2 :: proc "c" (N, I, Nref: double2) -> double2 { return N if dot(I, Nref) < 0 else -N }
+faceforward_double3 :: proc "c" (N, I, Nref: double3) -> double3 { return N if dot(I, Nref) < 0 else -N }
+faceforward_double4 :: proc "c" (N, I, Nref: double4) -> double4 { return N if dot(I, Nref) < 0 else -N }
+
+
+reflect :: proc{
+	reflect_float,
+	reflect_double,
+	reflect_float2,
+	reflect_float3,
+	reflect_float4,
+	reflect_double2,
+	reflect_double3,
+	reflect_double4,
+}
+reflect_float  :: proc "c" (I, N: float)  -> float  { return I - 2*N*dot(N, I) }
+reflect_double  :: proc "c" (I, N: double)  -> double  { return I - 2*N*dot(N, I) }
+reflect_float2 :: proc "c" (I, N: float2) -> float2 { return I - 2*N*dot(N, I) }
+reflect_float3 :: proc "c" (I, N: float3) -> float3 { return I - 2*N*dot(N, I) }
+reflect_float4 :: proc "c" (I, N: float4) -> float4 { return I - 2*N*dot(N, I) }
+reflect_double2 :: proc "c" (I, N: double2) -> double2 { return I - 2*N*dot(N, I) }
+reflect_double3 :: proc "c" (I, N: double3) -> double3 { return I - 2*N*dot(N, I) }
+reflect_double4 :: proc "c" (I, N: double4) -> double4 { return I - 2*N*dot(N, I) }
+
+
+
+
+refract :: proc{
+	refract_float,
+	refract_double,
+	refract_float2,
+	refract_float3,
+	refract_float4,
+	refract_double2,
+	refract_double3,
+	refract_double4,
+}
+refract_float  :: proc "c" (i, n, eta: float) -> float { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * float(int(cost2 > 0))
+}
+refract_double  :: proc "c" (i, n, eta: double) -> double { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * double(int(cost2 > 0))
+}
+refract_float2  :: proc "c" (i, n, eta: float2) -> float2 { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * float2{float(int(cost2.x > 0)), float(int(cost2.y > 0))}
+}
+refract_float3  :: proc "c" (i, n, eta: float3) -> float3 { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * float3{float(int(cost2.x > 0)), float(int(cost2.y > 0)), float(int(cost2.z > 0))}
+}
+refract_float4  :: proc "c" (i, n, eta: float4) -> float4 { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * float4{float(int(cost2.x > 0)), float(int(cost2.y > 0)), float(int(cost2.z > 0)), float(int(cost2.w > 0))}
+}
+refract_double2  :: proc "c" (i, n, eta: double2) -> double2 { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * double2{double(int(cost2.x > 0)), double(int(cost2.y > 0))}
+}
+refract_double3  :: proc "c" (i, n, eta: double3) -> double3 { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * double3{double(int(cost2.x > 0)), double(int(cost2.y > 0)), double(int(cost2.z > 0))}
+}
+refract_double4  :: proc "c" (i, n, eta: double4) -> double4 { 
+	cosi := dot(-i, n)
+	cost2 := 1 - eta*eta*(1 - cosi*cosi)
+	t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
+	return t * double4{double(int(cost2.x > 0)), double(int(cost2.y > 0)), double(int(cost2.z > 0)), double(int(cost2.w > 0))}
+}
+
+scalarTripleProduct :: proc{
+	scalarTripleProduct_float3,
+	scalarTripleProduct_double3,
+	scalarTripleProduct_int3,
+}
+scalarTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float  { return dot(a, cross(b, c)) }
+scalarTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double  { return dot(a, cross(b, c)) }
+scalarTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int  { return dot(a, cross(b, c)) }
+
+vectorTripleProduct :: proc {
+	vectorTripleProduct_float3,
+	vectorTripleProduct_double3,
+	vectorTripleProduct_int3,	
+}
+vectorTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float3 { return cross(a, cross(b, c)) }
+vectorTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double3 { return cross(a, cross(b, c)) }
+vectorTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int3 { return cross(a, cross(b, c)) }
+
+
+// Vector Relational Procedures
+
+lessThan :: proc{
+	lessThan_float,
+	lessThan_double,
+	lessThan_int,
+	lessThan_uint,
+	lessThan_float2,
+	lessThan_double2,
+	lessThan_int2,
+	lessThan_uint2,
+	lessThan_float3,
+	lessThan_double3,
+	lessThan_int3,
+	lessThan_uint3,
+	lessThan_float4,
+	lessThan_double4,
+	lessThan_int4,
+	lessThan_uint4,
+}
+lessThan_float   :: proc "c" (a, b: float) -> bool { return a < b }
+lessThan_double   :: proc "c" (a, b: double) -> bool { return a < b }
+lessThan_int   :: proc "c" (a, b: int) -> bool { return a < b }
+lessThan_uint   :: proc "c" (a, b: uint) -> bool { return a < b }
+lessThan_float2  :: proc "c" (a, b: float2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+lessThan_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+lessThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+lessThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+lessThan_float3  :: proc "c" (a, b: float3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+lessThan_double3  :: proc "c" (a, b: double3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+lessThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+lessThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+lessThan_float4  :: proc "c" (a, b: float4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+lessThan_double4  :: proc "c" (a, b: double4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+lessThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+lessThan_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+
+
+lessThanEqual :: proc{
+	lessThanEqual_float,
+	lessThanEqual_double,
+	lessThanEqual_int,
+	lessThanEqual_uint,
+	lessThanEqual_float2,
+	lessThanEqual_double2,
+	lessThanEqual_int2,
+	lessThanEqual_uint2,
+	lessThanEqual_float3,
+	lessThanEqual_double3,
+	lessThanEqual_int3,
+	lessThanEqual_uint3,
+	lessThanEqual_float4,
+	lessThanEqual_double4,
+	lessThanEqual_int4,
+	lessThanEqual_uint4,
+}
+lessThanEqual_float   :: proc "c" (a, b: float) -> bool { return a <= b }
+lessThanEqual_double   :: proc "c" (a, b: double) -> bool { return a <= b }
+lessThanEqual_int   :: proc "c" (a, b: int) -> bool { return a <= b }
+lessThanEqual_uint   :: proc "c" (a, b: uint) -> bool { return a <= b }
+lessThanEqual_float2  :: proc "c" (a, b: float2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+lessThanEqual_double2  :: proc "c" (a, b: double2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+lessThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+lessThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+lessThanEqual_float3  :: proc "c" (a, b: float3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+lessThanEqual_double3  :: proc "c" (a, b: double3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+lessThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+lessThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+lessThanEqual_float4  :: proc "c" (a, b: float4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+lessThanEqual_double4  :: proc "c" (a, b: double4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+lessThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+lessThanEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+
+
+greaterThan :: proc{
+	greaterThan_float,
+	greaterThan_double,
+	greaterThan_int,
+	greaterThan_uint,
+	greaterThan_float2,
+	greaterThan_double2,
+	greaterThan_int2,
+	greaterThan_uint2,
+	greaterThan_float3,
+	greaterThan_double3,
+	greaterThan_int3,
+	greaterThan_uint3,
+	greaterThan_float4,
+	greaterThan_double4,
+	greaterThan_int4,
+	greaterThan_uint4,
+}
+greaterThan_float   :: proc "c" (a, b: float) -> bool { return a > b }
+greaterThan_double   :: proc "c" (a, b: double) -> bool { return a > b }
+greaterThan_int   :: proc "c" (a, b: int) -> bool { return a > b }
+greaterThan_uint   :: proc "c" (a, b: uint) -> bool { return a > b }
+greaterThan_float2  :: proc "c" (a, b: float2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+greaterThan_double2  :: proc "c" (a, b: double2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+greaterThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+greaterThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+greaterThan_float3  :: proc "c" (a, b: float3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+greaterThan_double3  :: proc "c" (a, b: double3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+greaterThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+greaterThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+greaterThan_float4  :: proc "c" (a, b: float4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+greaterThan_double4  :: proc "c" (a, b: double4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+greaterThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+greaterThan_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+
+
+greaterThanEqual :: proc{
+	greaterThanEqual_float,
+	greaterThanEqual_double,
+	greaterThanEqual_int,
+	greaterThanEqual_uint,
+	greaterThanEqual_float2,
+	greaterThanEqual_double2,
+	greaterThanEqual_int2,
+	greaterThanEqual_uint2,
+	greaterThanEqual_float3,
+	greaterThanEqual_double3,
+	greaterThanEqual_int3,
+	greaterThanEqual_uint3,
+	greaterThanEqual_float4,
+	greaterThanEqual_double4,
+	greaterThanEqual_int4,
+	greaterThanEqual_uint4,
+}
+greaterThanEqual_float   :: proc "c" (a, b: float) -> bool { return a >= b }
+greaterThanEqual_double   :: proc "c" (a, b: double) -> bool { return a >= b }
+greaterThanEqual_int   :: proc "c" (a, b: int) -> bool { return a >= b }
+greaterThanEqual_uint   :: proc "c" (a, b: uint) -> bool { return a >= b }
+greaterThanEqual_float2  :: proc "c" (a, b: float2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+greaterThanEqual_double2  :: proc "c" (a, b: double2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+greaterThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+greaterThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+greaterThanEqual_float3  :: proc "c" (a, b: float3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+greaterThanEqual_double3  :: proc "c" (a, b: double3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+greaterThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+greaterThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+greaterThanEqual_float4  :: proc "c" (a, b: float4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+greaterThanEqual_double4  :: proc "c" (a, b: double4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+greaterThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+greaterThanEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+
+
+equal :: proc{
+	equal_float,
+	equal_double,
+	equal_int,
+	equal_uint,
+	equal_float2,
+	equal_double2,
+	equal_int2,
+	equal_uint2,
+	equal_float3,
+	equal_double3,
+	equal_int3,
+	equal_uint3,
+	equal_float4,
+	equal_double4,
+	equal_int4,
+	equal_uint4,
+}
+equal_float   :: proc "c" (a, b: float) -> bool { return a == b }
+equal_double   :: proc "c" (a, b: double) -> bool { return a == b }
+equal_int   :: proc "c" (a, b: int) -> bool { return a == b }
+equal_uint   :: proc "c" (a, b: uint) -> bool { return a == b }
+equal_float2  :: proc "c" (a, b: float2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+equal_double2  :: proc "c" (a, b: double2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+equal_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+equal_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+equal_float3  :: proc "c" (a, b: float3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+equal_double3  :: proc "c" (a, b: double3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+equal_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+equal_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+equal_float4  :: proc "c" (a, b: float4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+equal_double4  :: proc "c" (a, b: double4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+equal_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+equal_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+
+notEqual :: proc{
+	notEqual_float,
+	notEqual_double,
+	notEqual_int,
+	notEqual_uint,
+	notEqual_float2,
+	notEqual_double2,
+	notEqual_int2,
+	notEqual_uint2,
+	notEqual_float3,
+	notEqual_double3,
+	notEqual_int3,
+	notEqual_uint3,
+	notEqual_float4,
+	notEqual_double4,
+	notEqual_int4,
+	notEqual_uint4,
+}
+notEqual_float   :: proc "c" (a, b: float) -> bool { return a != b }
+notEqual_double   :: proc "c" (a, b: double) -> bool { return a != b }
+notEqual_int   :: proc "c" (a, b: int) -> bool { return a != b }
+notEqual_uint   :: proc "c" (a, b: uint) -> bool { return a != b }
+notEqual_float2  :: proc "c" (a, b: float2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+notEqual_double2  :: proc "c" (a, b: double2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+notEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+notEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+notEqual_float3  :: proc "c" (a, b: float3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+notEqual_double3  :: proc "c" (a, b: double3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+notEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+notEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+notEqual_float4  :: proc "c" (a, b: float4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+notEqual_double4  :: proc "c" (a, b: double4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+notEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+notEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+
+
+any :: proc{
+	any_bool,
+	any_bool2,
+	any_bool3,
+	any_bool4,
+}
+any_bool  :: proc "c" (v: bool) -> bool  { return v }
+any_bool2 :: proc "c" (v: bool2) -> bool { return v.x || v.y }
+any_bool3 :: proc "c" (v: bool3) -> bool { return v.x || v.y || v.z }
+any_bool4 :: proc "c" (v: bool4) -> bool { return v.x || v.y || v.z || v.w }
+
+all :: proc{
+	all_bool,
+	all_bool2,
+	all_bool3,
+	all_bool4,
+}
+all_bool  :: proc "c" (v: bool) -> bool  { return v }
+all_bool2 :: proc "c" (v: bool2) -> bool { return v.x && v.y }
+all_bool3 :: proc "c" (v: bool3) -> bool { return v.x && v.y && v.z }
+all_bool4 :: proc "c" (v: bool4) -> bool { return v.x && v.y && v.z && v.w }
+
+not :: proc{
+	not_bool,
+	not_bool2,
+	not_bool3,
+	not_bool4,
+}
+not_bool  :: proc "c" (v: bool) -> bool { return !v }
+not_bool2 :: proc "c" (v: bool2) -> bool2 { return {!v.x, !v.y} }
+not_bool3 :: proc "c" (v: bool3) -> bool3 { return {!v.x, !v.y, !v.z} }
+not_bool4 :: proc "c" (v: bool4) -> bool4 { return {!v.x, !v.y, !v.z, !v.w} }
+
+
+
+
+inverse_float1x1  :: proc "c" (m: float1x1)  -> float1x1  { return builtin.inverse(m) }
+inverse_float2x2  :: proc "c" (m: float2x2)  -> float2x2  { return builtin.inverse(m) }
+inverse_float3x3  :: proc "c" (m: float3x3)  -> float3x3  { return builtin.inverse(m) }
+inverse_float4x4  :: proc "c" (m: float4x4)  -> float4x4  { return builtin.inverse(m) }
+inverse_double1x1 :: proc "c" (m: double1x1) -> double1x1 { return builtin.inverse(m) }
+inverse_double2x2 :: proc "c" (m: double2x2) -> double2x2 { return builtin.inverse(m) }
+inverse_double3x3 :: proc "c" (m: double3x3) -> double3x3 { return builtin.inverse(m) }
+inverse_double4x4 :: proc "c" (m: double4x4) -> double4x4 { return builtin.inverse(m) }
+
+inverse :: proc{
+	inverse_float1x1,
+	inverse_float2x2,
+	inverse_float3x3,
+	inverse_float4x4,
+	inverse_double1x1,
+	inverse_double2x2,
+	inverse_double3x3,
+	inverse_double4x4,
+}
+
+transpose         :: builtin.transpose
+inverse_transpose :: builtin.inverse_transpose
+adjugate          :: builtin.adjugate
+hermitian_adjoint :: builtin.hermitian_adjoint
+minor             :: builtin.matrix_minor
+determinant       :: builtin.determinant
+trace             :: builtin.matrix_trace
+
+asfloat :: proc{
+	asfloat_float,
+	asfloat_double,
+	asfloat_int,
+	asfloat_uint,
+	asfloat_float1x1,
+	asfloat_float2x2,
+	asfloat_float3x3,
+	asfloat_float4x4,
+	asfloat_float1x2,
+	asfloat_float1x3,
+	asfloat_float1x4,
+	asfloat_float2x1,
+	asfloat_float2x3,
+	asfloat_float2x4,
+	asfloat_float3x1,
+	asfloat_float3x2,
+	asfloat_float3x4,
+	asfloat_float4x1,
+	asfloat_float4x2,
+	asfloat_float4x3,
+	asfloat_float2,
+	asfloat_float3,
+	asfloat_float4,
+	asfloat_int2,
+	asfloat_int3,
+	asfloat_int4,
+	asfloat_uint2,
+	asfloat_uint3,
+	asfloat_uint4,
+	asfloat_bool2,
+	asfloat_bool3,
+	asfloat_bool4,
+	asfloat_double1x1,
+	asfloat_double2x2,
+	asfloat_double3x3,
+	asfloat_double4x4,
+	asfloat_double1x2,
+	asfloat_double1x3,
+	asfloat_double1x4,
+	asfloat_double2x1,
+	asfloat_double2x3,
+	asfloat_double2x4,
+	asfloat_double3x1,
+	asfloat_double3x2,
+	asfloat_double3x4,
+	asfloat_double4x1,
+	asfloat_double4x2,
+	asfloat_double4x3,
+	asfloat_double2,
+	asfloat_double3,
+	asfloat_double4,
+}
+asfloat_float     :: proc "c" (v: float)     -> float    { return float(v) }
+asfloat_double    :: proc "c" (v: double)    -> float    { return float(v) }
+asfloat_int       :: proc "c" (v: int)       -> float    { return float(v) }
+asfloat_uint      :: proc "c" (v: uint)      -> float    { return float(v) }
+asfloat_float1x1  :: proc "c" (v: float1x1)  -> float1x1 { return float1x1(v) }
+asfloat_float2x2  :: proc "c" (v: float2x2)  -> float2x2 { return float2x2(v) }
+asfloat_float3x3  :: proc "c" (v: float3x3)  -> float3x3 { return float3x3(v) }
+asfloat_float4x4  :: proc "c" (v: float4x4)  -> float4x4 { return float4x4(v) }
+asfloat_float1x2  :: proc "c" (v: float1x2)  -> float1x2 { return float1x2(v) }
+asfloat_float1x3  :: proc "c" (v: float1x3)  -> float1x3 { return float1x3(v) }
+asfloat_float1x4  :: proc "c" (v: float1x4)  -> float1x4 { return float1x4(v) }
+asfloat_float2x1  :: proc "c" (v: float2x1)  -> float2x1 { return float2x1(v) }
+asfloat_float2x3  :: proc "c" (v: float2x3)  -> float2x3 { return float2x3(v) }
+asfloat_float2x4  :: proc "c" (v: float2x4)  -> float2x4 { return float2x4(v) }
+asfloat_float3x1  :: proc "c" (v: float3x1)  -> float3x1 { return float3x1(v) }
+asfloat_float3x2  :: proc "c" (v: float3x2)  -> float3x2 { return float3x2(v) }
+asfloat_float3x4  :: proc "c" (v: float3x4)  -> float3x4 { return float3x4(v) }
+asfloat_float4x1  :: proc "c" (v: float4x1)  -> float4x1 { return float4x1(v) }
+asfloat_float4x2  :: proc "c" (v: float4x2)  -> float4x2 { return float4x2(v) }
+asfloat_float4x3  :: proc "c" (v: float4x3)  -> float4x3 { return float4x3(v) }
+asfloat_float2    :: proc "c" (v: float2)    -> float2   { return float2(v) }
+asfloat_float3    :: proc "c" (v: float3)    -> float3   { return float3(v) }
+asfloat_float4    :: proc "c" (v: float4)    -> float4   { return float4(v) }
+asfloat_int2      :: proc "c" (v: int2)      -> float2   { return float2{float(v.x), float(v.y)} }
+asfloat_int3      :: proc "c" (v: int3)      -> float3   { return float3{float(v.x), float(v.y), float(v.z)} }
+asfloat_int4      :: proc "c" (v: int4)      -> float4   { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
+asfloat_uint2     :: proc "c" (v: uint2)     -> float2   { return float2{float(v.x), float(v.y)} }
+asfloat_uint3     :: proc "c" (v: uint3)     -> float3   { return float3{float(v.x), float(v.y), float(v.z)} }
+asfloat_uint4     :: proc "c" (v: uint4)     -> float4   { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
+asfloat_bool2     :: proc "c" (v: bool2)     -> float2   { return float2{float(int(v.x)), float(int(v.y))} }
+asfloat_bool3     :: proc "c" (v: bool3)     -> float3   { return float3{float(int(v.x)), float(int(v.y)), float(int(v.z))} }
+asfloat_bool4     :: proc "c" (v: bool4)     -> float4   { return float4{float(int(v.x)), float(int(v.y)), float(int(v.z)), float(int(v.w))} }
+asfloat_double1x1 :: proc "c" (v: double1x1) -> float1x1 { return float1x1(v) }
+asfloat_double2x2 :: proc "c" (v: double2x2) -> float2x2 { return float2x2(v) }
+asfloat_double3x3 :: proc "c" (v: double3x3) -> float3x3 { return float3x3(v) }
+asfloat_double4x4 :: proc "c" (v: double4x4) -> float4x4 { return float4x4(v) }
+asfloat_double1x2 :: proc "c" (v: double1x2) -> float1x2 { return float1x2(v) }
+asfloat_double1x3 :: proc "c" (v: double1x3) -> float1x3 { return float1x3(v) }
+asfloat_double1x4 :: proc "c" (v: double1x4) -> float1x4 { return float1x4(v) }
+asfloat_double2x1 :: proc "c" (v: double2x1) -> float2x1 { return float2x1(v) }
+asfloat_double2x3 :: proc "c" (v: double2x3) -> float2x3 { return float2x3(v) }
+asfloat_double2x4 :: proc "c" (v: double2x4) -> float2x4 { return float2x4(v) }
+asfloat_double3x1 :: proc "c" (v: double3x1) -> float3x1 { return float3x1(v) }
+asfloat_double3x2 :: proc "c" (v: double3x2) -> float3x2 { return float3x2(v) }
+asfloat_double3x4 :: proc "c" (v: double3x4) -> float3x4 { return float3x4(v) }
+asfloat_double4x1 :: proc "c" (v: double4x1) -> float4x1 { return float4x1(v) }
+asfloat_double4x2 :: proc "c" (v: double4x2) -> float4x2 { return float4x2(v) }
+asfloat_double4x3 :: proc "c" (v: double4x3) -> float4x3 { return float4x3(v) }
+asfloat_double2   :: proc "c" (v: double2)   -> float2   { return float2{float(v.x), float(v.y)} }
+asfloat_double3   :: proc "c" (v: double3)   -> float3   { return float3{float(v.x), float(v.y), float(v.z)} }
+asfloat_double4   :: proc "c" (v: double4)   -> float4   { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
+
+asdouble :: proc{
+	asdouble_float,
+	asdouble_double,
+	asdouble_int,
+	asdouble_uint,
+	asdouble_float1x1,
+	asdouble_float2x2,
+	asdouble_float3x3,
+	asdouble_float4x4,
+	asdouble_float1x2,
+	asdouble_float1x3,
+	asdouble_float1x4,
+	asdouble_float2x1,
+	asdouble_float2x3,
+	asdouble_float2x4,
+	asdouble_float3x1,
+	asdouble_float3x2,
+	asdouble_float3x4,
+	asdouble_float4x1,
+	asdouble_float4x2,
+	asdouble_float4x3,
+	asdouble_float2,
+	asdouble_float3,
+	asdouble_float4,
+	asdouble_int2,
+	asdouble_int3,
+	asdouble_int4,
+	asdouble_uint2,
+	asdouble_uint3,
+	asdouble_uint4,
+	asdouble_bool2,
+	asdouble_bool3,
+	asdouble_bool4,
+	asdouble_double1x1,
+	asdouble_double2x2,
+	asdouble_double3x3,
+	asdouble_double4x4,
+	asdouble_double1x2,
+	asdouble_double1x3,
+	asdouble_double1x4,
+	asdouble_double2x1,
+	asdouble_double2x3,
+	asdouble_double2x4,
+	asdouble_double3x1,
+	asdouble_double3x2,
+	asdouble_double3x4,
+	asdouble_double4x1,
+	asdouble_double4x2,
+	asdouble_double4x3,
+	asdouble_double2,
+	asdouble_double3,
+	asdouble_double4,
+}
+asdouble_float     :: proc "c" (v: float)     -> double    { return double(v) }
+asdouble_double    :: proc "c" (v: double)    -> double    { return double(v) }
+asdouble_int       :: proc "c" (v: int)       -> double    { return double(v) }
+asdouble_uint      :: proc "c" (v: uint)      -> double    { return double(v) }
+asdouble_float1x1  :: proc "c" (v: float1x1)  -> double1x1 { return double1x1(v) }
+asdouble_float2x2  :: proc "c" (v: float2x2)  -> double2x2 { return double2x2(v) }
+asdouble_float3x3  :: proc "c" (v: float3x3)  -> double3x3 { return double3x3(v) }
+asdouble_float4x4  :: proc "c" (v: float4x4)  -> double4x4 { return double4x4(v) }
+asdouble_float1x2  :: proc "c" (v: float1x2)  -> double1x2 { return double1x2(v) }
+asdouble_float1x3  :: proc "c" (v: float1x3)  -> double1x3 { return double1x3(v) }
+asdouble_float1x4  :: proc "c" (v: float1x4)  -> double1x4 { return double1x4(v) }
+asdouble_float2x1  :: proc "c" (v: float2x1)  -> double2x1 { return double2x1(v) }
+asdouble_float2x3  :: proc "c" (v: float2x3)  -> double2x3 { return double2x3(v) }
+asdouble_float2x4  :: proc "c" (v: float2x4)  -> double2x4 { return double2x4(v) }
+asdouble_float3x1  :: proc "c" (v: float3x1)  -> double3x1 { return double3x1(v) }
+asdouble_float3x2  :: proc "c" (v: float3x2)  -> double3x2 { return double3x2(v) }
+asdouble_float3x4  :: proc "c" (v: float3x4)  -> double3x4 { return double3x4(v) }
+asdouble_float4x1  :: proc "c" (v: float4x1)  -> double4x1 { return double4x1(v) }
+asdouble_float4x2  :: proc "c" (v: float4x2)  -> double4x2 { return double4x2(v) }
+asdouble_float4x3  :: proc "c" (v: float4x3)  -> double4x3 { return double4x3(v) }
+asdouble_float2    :: proc "c" (v: float2)    -> double2   { return double2{double(v.x), double(v.y)} }
+asdouble_float3    :: proc "c" (v: float3)    -> double3   { return double3{double(v.x), double(v.y), double(v.z)} }
+asdouble_float4    :: proc "c" (v: float4)    -> double4   { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+asdouble_int2      :: proc "c" (v: int2)      -> double2   { return double2{double(v.x), double(v.y)} }
+asdouble_int3      :: proc "c" (v: int3)      -> double3   { return double3{double(v.x), double(v.y), double(v.z)} }
+asdouble_int4      :: proc "c" (v: int4)      -> double4   { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+asdouble_uint2     :: proc "c" (v: uint2)     -> double2   { return double2{double(v.x), double(v.y)} }
+asdouble_uint3     :: proc "c" (v: uint3)     -> double3   { return double3{double(v.x), double(v.y), double(v.z)} }
+asdouble_uint4     :: proc "c" (v: uint4)     -> double4   { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+asdouble_bool2     :: proc "c" (v: bool2)     -> double2   { return double2{double(int(v.x)), double(int(v.y))} }
+asdouble_bool3     :: proc "c" (v: bool3)     -> double3   { return double3{double(int(v.x)), double(int(v.y)), double(int(v.z))} }
+asdouble_bool4     :: proc "c" (v: bool4)     -> double4   { return double4{double(int(v.x)), double(int(v.y)), double(int(v.z)), double(int(v.w))} }
+asdouble_double1x1 :: proc "c" (v: double1x1) -> double1x1 { return double1x1(v) }
+asdouble_double2x2 :: proc "c" (v: double2x2) -> double2x2 { return double2x2(v) }
+asdouble_double3x3 :: proc "c" (v: double3x3) -> double3x3 { return double3x3(v) }
+asdouble_double4x4 :: proc "c" (v: double4x4) -> double4x4 { return double4x4(v) }
+asdouble_double1x2 :: proc "c" (v: double1x2) -> double1x2 { return double1x2(v) }
+asdouble_double1x3 :: proc "c" (v: double1x3) -> double1x3 { return double1x3(v) }
+asdouble_double1x4 :: proc "c" (v: double1x4) -> double1x4 { return double1x4(v) }
+asdouble_double2x1 :: proc "c" (v: double2x1) -> double2x1 { return double2x1(v) }
+asdouble_double2x3 :: proc "c" (v: double2x3) -> double2x3 { return double2x3(v) }
+asdouble_double2x4 :: proc "c" (v: double2x4) -> double2x4 { return double2x4(v) }
+asdouble_double3x1 :: proc "c" (v: double3x1) -> double3x1 { return double3x1(v) }
+asdouble_double3x2 :: proc "c" (v: double3x2) -> double3x2 { return double3x2(v) }
+asdouble_double3x4 :: proc "c" (v: double3x4) -> double3x4 { return double3x4(v) }
+asdouble_double4x1 :: proc "c" (v: double4x1) -> double4x1 { return double4x1(v) }
+asdouble_double4x2 :: proc "c" (v: double4x2) -> double4x2 { return double4x2(v) }
+asdouble_double4x3 :: proc "c" (v: double4x3) -> double4x3 { return double4x3(v) }
+asdouble_double2   :: proc "c" (v: double2)   -> double2   { return double2{double(v.x), double(v.y)} }
+asdouble_double3   :: proc "c" (v: double3)   -> double3   { return double3{double(v.x), double(v.y), double(v.z)} }
+asdouble_double4   :: proc "c" (v: double4)   -> double4   { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+
+asint :: proc{
+	asint_float,
+	asint_double,
+	asint_int,
+	asint_uint,
+	asint_float1x1,
+	asint_float2x2,
+	asint_float3x3,
+	asint_float4x4,
+	asint_float1x2,
+	asint_float1x3,
+	asint_float1x4,
+	asint_float2x1,
+	asint_float2x3,
+	asint_float2x4,
+	asint_float3x1,
+	asint_float3x2,
+	asint_float3x4,
+	asint_float4x1,
+	asint_float4x2,
+	asint_float4x3,
+	asint_float2,
+	asint_float3,
+	asint_float4,
+	asint_int2,
+	asint_int3,
+	asint_int4,
+	asint_uint2,
+	asint_uint3,
+	asint_uint4,
+	asint_bool2,
+	asint_bool3,
+	asint_bool4,
+	asint_double1x1,
+	asint_double2x2,
+	asint_double3x3,
+	asint_double4x4,
+	asint_double1x2,
+	asint_double1x3,
+	asint_double1x4,
+	asint_double2x1,
+	asint_double2x3,
+	asint_double2x4,
+	asint_double3x1,
+	asint_double3x2,
+	asint_double3x4,
+	asint_double4x1,
+	asint_double4x2,
+	asint_double4x3,
+	asint_double2,
+	asint_double3,
+	asint_double4,
+}
+asint_float     :: proc "c" (v: float)     -> int    { return int(v) }
+asint_double    :: proc "c" (v: double)    -> int    { return int(v) }
+asint_int       :: proc "c" (v: int)       -> int    { return int(v) }
+asint_uint      :: proc "c" (v: uint)      -> int    { return int(v) }
+asint_float1x1  :: proc "c" (v: float1x1)  -> int1x1 { return int1x1(v) }
+asint_float2x2  :: proc "c" (v: float2x2)  -> int2x2 { return int2x2(v) }
+asint_float3x3  :: proc "c" (v: float3x3)  -> int3x3 { return int3x3(v) }
+asint_float4x4  :: proc "c" (v: float4x4)  -> int4x4 { return int4x4(v) }
+asint_float1x2  :: proc "c" (v: float1x2)  -> int1x2 { return int1x2(v) }
+asint_float1x3  :: proc "c" (v: float1x3)  -> int1x3 { return int1x3(v) }
+asint_float1x4  :: proc "c" (v: float1x4)  -> int1x4 { return int1x4(v) }
+asint_float2x1  :: proc "c" (v: float2x1)  -> int2x1 { return int2x1(v) }
+asint_float2x3  :: proc "c" (v: float2x3)  -> int2x3 { return int2x3(v) }
+asint_float2x4  :: proc "c" (v: float2x4)  -> int2x4 { return int2x4(v) }
+asint_float3x1  :: proc "c" (v: float3x1)  -> int3x1 { return int3x1(v) }
+asint_float3x2  :: proc "c" (v: float3x2)  -> int3x2 { return int3x2(v) }
+asint_float3x4  :: proc "c" (v: float3x4)  -> int3x4 { return int3x4(v) }
+asint_float4x1  :: proc "c" (v: float4x1)  -> int4x1 { return int4x1(v) }
+asint_float4x2  :: proc "c" (v: float4x2)  -> int4x2 { return int4x2(v) }
+asint_float4x3  :: proc "c" (v: float4x3)  -> int4x3 { return int4x3(v) }
+asint_float2    :: proc "c" (v: float2)    -> int2   { return int2{int(v.x), int(v.y)} }
+asint_float3    :: proc "c" (v: float3)    -> int3   { return int3{int(v.x), int(v.y), int(v.z)} }
+asint_float4    :: proc "c" (v: float4)    -> int4   { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+asint_int2      :: proc "c" (v: int2)      -> int2   { return int2{int(v.x), int(v.y)} }
+asint_int3      :: proc "c" (v: int3)      -> int3   { return int3{int(v.x), int(v.y), int(v.z)} }
+asint_int4      :: proc "c" (v: int4)      -> int4   { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+asint_uint2     :: proc "c" (v: uint2)     -> int2   { return int2{int(v.x), int(v.y)} }
+asint_uint3     :: proc "c" (v: uint3)     -> int3   { return int3{int(v.x), int(v.y), int(v.z)} }
+asint_uint4     :: proc "c" (v: uint4)     -> int4   { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+asint_bool2     :: proc "c" (v: bool2)     -> int2   { return int2{int(int(v.x)), int(int(v.y))} }
+asint_bool3     :: proc "c" (v: bool3)     -> int3   { return int3{int(int(v.x)), int(int(v.y)), int(int(v.z))} }
+asint_bool4     :: proc "c" (v: bool4)     -> int4   { return int4{int(int(v.x)), int(int(v.y)), int(int(v.z)), int(int(v.w))} }
+asint_double1x1 :: proc "c" (v: double1x1) -> int1x1 { return int1x1(v) }
+asint_double2x2 :: proc "c" (v: double2x2) -> int2x2 { return int2x2(v) }
+asint_double3x3 :: proc "c" (v: double3x3) -> int3x3 { return int3x3(v) }
+asint_double4x4 :: proc "c" (v: double4x4) -> int4x4 { return int4x4(v) }
+asint_double1x2 :: proc "c" (v: double1x2) -> int1x2 { return int1x2(v) }
+asint_double1x3 :: proc "c" (v: double1x3) -> int1x3 { return int1x3(v) }
+asint_double1x4 :: proc "c" (v: double1x4) -> int1x4 { return int1x4(v) }
+asint_double2x1 :: proc "c" (v: double2x1) -> int2x1 { return int2x1(v) }
+asint_double2x3 :: proc "c" (v: double2x3) -> int2x3 { return int2x3(v) }
+asint_double2x4 :: proc "c" (v: double2x4) -> int2x4 { return int2x4(v) }
+asint_double3x1 :: proc "c" (v: double3x1) -> int3x1 { return int3x1(v) }
+asint_double3x2 :: proc "c" (v: double3x2) -> int3x2 { return int3x2(v) }
+asint_double3x4 :: proc "c" (v: double3x4) -> int3x4 { return int3x4(v) }
+asint_double4x1 :: proc "c" (v: double4x1) -> int4x1 { return int4x1(v) }
+asint_double4x2 :: proc "c" (v: double4x2) -> int4x2 { return int4x2(v) }
+asint_double4x3 :: proc "c" (v: double4x3) -> int4x3 { return int4x3(v) }
+asint_double2   :: proc "c" (v: double2)   -> int2   { return int2{int(v.x), int(v.y)} }
+asint_double3   :: proc "c" (v: double3)   -> int3   { return int3{int(v.x), int(v.y), int(v.z)} }
+asint_double4   :: proc "c" (v: double4)   -> int4   { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+
+
+asuint :: proc{
+	asuint_float,
+	asuint_double,
+	asuint_int,
+	asuint_uint,
+	asuint_float2,
+	asuint_float3,
+	asuint_float4,
+	asuint_int2,
+	asuint_int3,
+	asuint_int4,
+	asuint_uint2,
+	asuint_uint3,
+	asuint_uint4,
+	asuint_bool2,
+	asuint_bool3,
+	asuint_bool4,
+	asuint_double2,
+	asuint_double3,
+	asuint_double4,
+}
+asuint_float     :: proc "c" (v: float)     -> uint    { return uint(v) }
+asuint_double    :: proc "c" (v: double)    -> uint    { return uint(v) }
+asuint_int       :: proc "c" (v: int)       -> uint    { return uint(v) }
+asuint_uint      :: proc "c" (v: uint)      -> uint    { return uint(v) }
+asuint_float2    :: proc "c" (v: float2)    -> uint2   { return uint2{uint(v.x), uint(v.y)} }
+asuint_float3    :: proc "c" (v: float3)    -> uint3   { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+asuint_float4    :: proc "c" (v: float4)    -> uint4   { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+asuint_int2      :: proc "c" (v: int2)      -> uint2   { return uint2{uint(v.x), uint(v.y)} }
+asuint_int3      :: proc "c" (v: int3)      -> uint3   { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+asuint_int4      :: proc "c" (v: int4)      -> uint4   { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+asuint_uint2     :: proc "c" (v: uint2)     -> uint2   { return uint2{uint(v.x), uint(v.y)} }
+asuint_uint3     :: proc "c" (v: uint3)     -> uint3   { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+asuint_uint4     :: proc "c" (v: uint4)     -> uint4   { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+asuint_bool2     :: proc "c" (v: bool2)     -> uint2   { return uint2{uint(uint(v.x)), uint(uint(v.y))} }
+asuint_bool3     :: proc "c" (v: bool3)     -> uint3   { return uint3{uint(uint(v.x)), uint(uint(v.y)), uint(uint(v.z))} }
+asuint_bool4     :: proc "c" (v: bool4)     -> uint4   { return uint4{uint(uint(v.x)), uint(uint(v.y)), uint(uint(v.z)), uint(uint(v.w))} }
+asuint_double2   :: proc "c" (v: double2)   -> uint2   { return uint2{uint(v.x), uint(v.y)} }
+asuint_double3   :: proc "c" (v: double3)   -> uint3   { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+asuint_double4   :: proc "c" (v: double4)   -> uint4   { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+
+
+// TODO(bill): All of the `mul` procedures

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

@@ -0,0 +1,69 @@
+package math_linalg_hlsl
+
+import "core:math"
+
+cos_float         :: proc "c" (x: float)    -> float { return math.cos(x) }
+sin_float         :: proc "c" (x: float)    -> float { return math.sin(x) }
+tan_float         :: proc "c" (x: float)    -> float { return math.tan(x) }
+acos_float        :: proc "c" (x: float)    -> float { return math.acos(x) }
+asin_float        :: proc "c" (x: float)    -> float { return math.asin(x) }
+atan_float        :: proc "c" (x: float)    -> float { return math.atan(x) }
+atan2_float       :: proc "c" (y, x: float) -> float { return math.atan2(y, x) }
+cosh_float        :: proc "c" (x: float)    -> float { return math.cosh(x) }
+sinh_float        :: proc "c" (x: float)    -> float { return math.sinh(x) }
+tanh_float        :: proc "c" (x: float)    -> float { return math.tanh(x) }
+acosh_float       :: proc "c" (x: float)    -> float { return math.acosh(x) }
+asinh_float       :: proc "c" (x: float)    -> float { return math.asinh(x) }
+atanh_float       :: proc "c" (x: float)    -> float { return math.atanh(x) }
+sqrt_float        :: proc "c" (x: float)    -> float { return math.sqrt(x) }
+rsqrt_float       :: proc "c" (x: float)    -> float { return 1.0/math.sqrt(x) }
+rcp_float         :: proc "c" (x: float)    -> float { return 1.0/x }
+pow_float         :: proc "c" (x, y: float) -> float { return math.pow(x, y) }
+exp_float         :: proc "c" (x: float)    -> float { return math.exp(x) }
+log_float         :: proc "c" (x: float)    -> float { return math.ln(x) }
+log2_float        :: proc "c" (x: float)    -> float { return math.log(x, 2) }
+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) }
+ceil_float        :: proc "c" (x: float)    -> float { return math.ceil(x) }
+fmod_float        :: proc "c" (x, y: float) -> float { return math.mod(x, y) }
+frac_float :: proc "c" (x: float) -> float {
+	if x >= 0 {
+		return x - math.trunc(x)
+	}
+	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) }
+acos_double        :: proc "c" (x: double)    -> double { return math.acos(x) }
+asin_double        :: proc "c" (x: double)    -> double { return math.asin(x) }
+atan_double        :: proc "c" (x: double)    -> double { return math.atan(x) }
+atan2_double       :: proc "c" (y, x: double) -> double { return math.atan2(y, x) }
+cosh_double        :: proc "c" (x: double)    -> double { return math.cosh(x) }
+sinh_double        :: proc "c" (x: double)    -> double { return math.sinh(x) }
+tanh_double        :: proc "c" (x: double)    -> double { return math.tanh(x) }
+acosh_double       :: proc "c" (x: double)    -> double { return math.acosh(x) }
+asinh_double       :: proc "c" (x: double)    -> double { return math.asinh(x) }
+atanh_double       :: proc "c" (x: double)    -> double { return math.atanh(x) }
+sqrt_double        :: proc "c" (x: double)    -> double { return math.sqrt(x) }
+rsqrt_double       :: proc "c" (x: double)    -> double { return 1.0/math.sqrt(x) }
+rcp_double         :: proc "c" (x: double)    -> double { return 1.0/x }
+pow_double         :: proc "c" (x, y: double) -> double { return math.pow(x, y) }
+exp_double         :: proc "c" (x: double)    -> double { return math.exp(x) }
+log_double         :: proc "c" (x: double)    -> double { return math.ln(x) }
+log2_double        :: proc "c" (x: double)    -> double { return math.log(x, 2) }
+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) }
+ceil_double        :: proc "c" (x: double)    -> double { return math.ceil(x) }
+fmod_double        :: proc "c" (x, y: double) -> double { return math.mod(x, y) }
+frac_double :: proc "c" (x: double) -> double {
+	if x >= 0 {
+		return x - math.trunc(x)
+	}
+	return math.trunc(-x) + x
+}

+ 1 - 1
core/runtime/internal.odin

@@ -3,7 +3,7 @@ package runtime
 import "core:intrinsics"
 
 @(private)
-RUNTIME_LINKAGE :: "strong" when (ODIN_USE_SEPARATE_MODULES || ODIN_BUILD_MODE == "dynamic") else "internal"
+RUNTIME_LINKAGE :: "strong" when (ODIN_USE_SEPARATE_MODULES || ODIN_BUILD_MODE == "dynamic" || !ODIN_NO_CRT) else "internal"
 
 @(private)
 byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {

+ 26 - 1
core/runtime/procs.odin

@@ -1,6 +1,31 @@
 package runtime
 
-when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" || ODIN_NO_CRT {
+when ODIN_NO_CRT && ODIN_OS == "windows" {
+	foreign import lib "system:NtDll.lib"
+	
+	@(private="file")
+	@(default_calling_convention="std")
+	foreign lib {
+		RtlMoveMemory :: proc(dst, src: rawptr, length: int) ---
+		RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
+	}
+	
+	@(link_name="memset", linkage="strong", require)
+	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
+		RtlFillMemory(ptr, len, val)
+		return ptr
+	}
+	@(link_name="memmove", linkage="strong", require)
+	memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
+		RtlMoveMemory(dst, src, len)
+		return dst
+	}
+	@(link_name="memcpy", linkage="strong", require)
+	memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
+		RtlMoveMemory(dst, src, len)
+		return dst
+	}
+} else when ODIN_NO_CRT || (ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64") {
 	@(link_name="memset", linkage="strong", require)
 	memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
 		if ptr != nil && len != 0 {

+ 2 - 0
examples/all/all_main.odin

@@ -30,6 +30,7 @@ import bits           "core:math/bits"
 import fixed          "core:math/fixed"
 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 ast            "core:odin/ast"
@@ -86,6 +87,7 @@ _ :: bits
 _ :: fixed
 _ :: linalg
 _ :: glm
+_ :: hlm
 _ :: rand
 _ :: mem
 _ :: ast

+ 11 - 4
src/bug_report.cpp

@@ -146,10 +146,17 @@ void report_windows_product_type(DWORD ProductType) {
 #endif
 
 void odin_cpuid(int leaf, int result[]) {
-	#if defined(GB_COMPILER_MSVC)
-		__cpuid(result, leaf);
-	#else
-		__get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
+	#if defined(GB_CPU_ARM)
+		return;
+
+	#elif defined(GB_CPU_X86)
+	
+		#if defined(GB_COMPILER_MSVC)
+			__cpuid(result, leaf);
+		#else
+			__get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
+		#endif
+
 	#endif
 }
 

+ 1 - 1
src/build_settings.cpp

@@ -244,7 +244,7 @@ struct BuildContext {
 	gbAffinity affinity;
 	isize      thread_count;
 
-	Map<ExactValue> defined_values; // Key:
+	PtrMap<char const *, ExactValue> defined_values;
 };
 
 

+ 145 - 0
src/check_builtin.cpp

@@ -318,6 +318,151 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			operand->mode = Addressing_Constant;
 			operand->value = exact_value_string(result);
 
+		} else if (name == "load_hash") {
+			if (ce->args.count != 2) {
+				if (ce->args.count == 0) {
+					error(ce->close, "'#load_hash' expects 2 argument, got 0");
+				} else {
+					error(ce->args[0], "'#load_hash' expects 2 argument, got %td", ce->args.count);
+				}
+				return false;
+			}
+
+			Ast *arg0 = ce->args[0];
+			Ast *arg1 = ce->args[1];
+			Operand o = {};
+			check_expr(c, &o, arg0);
+			if (o.mode != Addressing_Constant) {
+				error(arg0, "'#load_hash' expected a constant string argument");
+				return false;
+			}
+
+			if (!is_type_string(o.type)) {
+				gbString str = type_to_string(o.type);
+				error(arg0, "'#load_hash' expected a constant string, got %s", str);
+				gb_string_free(str);
+				return false;
+			}
+			
+			Operand o_hash = {};
+			check_expr(c, &o_hash, arg1);
+			if (o_hash.mode != Addressing_Constant) {
+				error(arg1, "'#load_hash' expected a constant string argument");
+				return false;
+			}
+
+			if (!is_type_string(o_hash.type)) {
+				gbString str = type_to_string(o.type);
+				error(arg1, "'#load_hash' expected a constant string, got %s", str);
+				gb_string_free(str);
+				return false;
+			}
+			
+
+			gbAllocator a = heap_allocator();
+
+			GB_ASSERT(o.value.kind == ExactValue_String);
+			GB_ASSERT(o_hash.value.kind == ExactValue_String);
+			
+			String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id));
+			String original_string = o.value.value_string;
+			String hash_kind = o_hash.value.value_string;
+			
+			String supported_hashes[] = {
+				str_lit("adler32"),
+				str_lit("crc32"),
+				str_lit("crc64"),
+				str_lit("fnv32"),
+				str_lit("fnv64"),
+				str_lit("fnv32a"),
+				str_lit("fnv64a"),
+				str_lit("murmur32"),
+				str_lit("murmur64"),
+			};
+			
+			bool hash_found = false;
+			for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
+				if (supported_hashes[i] == hash_kind) {
+					hash_found = true;
+					break;
+				}
+			}
+			if (!hash_found) {
+				ERROR_BLOCK();
+				error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind));
+				error_line("\tAvailable hash kinds:\n");
+				for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
+					error_line("\t%.*s\n", LIT(supported_hashes[i]));
+				}
+				return false;
+			}
+			
+
+			BlockingMutex *ignore_mutex = nullptr;
+			String path = {};
+			bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
+			gb_unused(ok);
+
+			char *c_str = alloc_cstring(a, path);
+			defer (gb_free(a, c_str));
+
+
+			gbFile f = {};
+			gbFileError file_err = gb_file_open(&f, c_str);
+			defer (gb_file_close(&f));
+
+			switch (file_err) {
+			default:
+			case gbFileError_Invalid:
+				error(ce->proc, "Failed to `#load_hash` file: %s; invalid file or cannot be found", c_str);
+				return false;
+			case gbFileError_NotExists:
+				error(ce->proc, "Failed to `#load_hash` file: %s; file cannot be found", c_str);
+				return false;
+			case gbFileError_Permission:
+				error(ce->proc, "Failed to `#load_hash` file: %s; file permissions problem", c_str);
+				return false;
+			case gbFileError_None:
+				// Okay
+				break;
+			}
+			
+			// TODO(bill): make these procedures fast :P
+			
+			u64 hash_value = 0;
+			String result = {};
+			isize file_size = cast(isize)gb_file_size(&f);
+			if (file_size > 0) {
+				u8 *data = cast(u8 *)gb_alloc(a, file_size);
+				gb_file_read_at(&f, data, file_size, 0);
+				if (hash_kind == "adler32") {
+					hash_value = gb_adler32(data, file_size);
+				} else if (hash_kind == "crc32") {
+					hash_value = gb_crc32(data, file_size);
+				} else if (hash_kind == "crc64") {
+					hash_value = gb_crc64(data, file_size);
+				} else if (hash_kind == "fnv32") {
+					hash_value = gb_fnv32(data, file_size);
+				} else if (hash_kind == "fnv64") {
+					hash_value = gb_fnv64(data, file_size);
+				} else if (hash_kind == "fnv32a") {
+					hash_value = gb_fnv32a(data, file_size);
+				} else if (hash_kind == "fnv64a") {
+					hash_value = gb_fnv64a(data, file_size);
+				} else if (hash_kind == "murmur32") {
+					hash_value = gb_murmur32(data, file_size);
+				} else if (hash_kind == "murmur64") {
+					hash_value = gb_murmur64(data, file_size);
+				} else {
+					compiler_error("unhandled hash kind: %.*s", LIT(hash_kind));	
+				}
+				gb_free(a, data);
+			}
+
+			operand->type = t_untyped_integer;
+			operand->mode = Addressing_Constant;
+			operand->value = exact_value_u64(hash_value);
+
 		} else if (name == "load_or") {
 			if (ce->args.count != 2) {
 				if (ce->args.count == 0) {

+ 10 - 10
src/check_expr.cpp

@@ -314,7 +314,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
 		return false;
 	}
 
-	auto *found_gen_procs = map_get(&info->gen_procs, hash_pointer(base_entity->identifier));
+	auto *found_gen_procs = map_get(&info->gen_procs, base_entity->identifier.load());
 	if (found_gen_procs) {
 		auto procs = *found_gen_procs;
 		for_array(i, procs) {
@@ -423,7 +423,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
 	} else {
 		auto array = array_make<Entity *>(heap_allocator());
 		array_add(&array, entity);
-		map_set(&info->gen_procs, hash_pointer(base_entity->identifier), array);
+		map_set(&info->gen_procs, base_entity->identifier.load(), array);
 	}
 
 	if (poly_proc_data) {
@@ -659,8 +659,8 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 	}
 	
 	if (is_type_matrix(dst)) {
-		Type *elem = base_array_type(dst);
-		i64 distance = check_distance_between_types(c, operand, elem);
+		Type *dst_elem = base_array_type(dst);
+		i64 distance = check_distance_between_types(c, operand, dst_elem);
 		if (distance >= 0) {
 			return distance + 7;
 		}
@@ -2467,7 +2467,9 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
 	if (is_type_matrix(src) && is_type_matrix(dst)) {
 		GB_ASSERT(src->kind == Type_Matrix);
 		GB_ASSERT(dst->kind == Type_Matrix);
-		if (!are_types_identical(src->Matrix.elem, dst->Matrix.elem)) {
+		Operand op = *operand;
+		op.type = src->Matrix.elem;
+		if (!check_is_castable_to(c, &op, dst->Matrix.elem)) {
 			return false;
 		}
 		
@@ -2477,11 +2479,7 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
 			return src_count == dst_count;
 		}
 		
-		if (dst->Matrix.row_count != dst->Matrix.column_count) {
-			return false;
-		}
-		
-		return true;
+		return is_matrix_square(dst) && is_matrix_square(src);
 	}
 
 
@@ -6108,6 +6106,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
 		    name == "defined" || 
 		    name == "config" || 
 		    name == "load" ||
+		    name == "load_hash" ||
 		    name == "load_or"
 		) {
 			operand->mode = Addressing_Builtin;
@@ -6943,6 +6942,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			    name == "defined" ||
 			    name == "config" ||
 			    name == "load" ||
+			    name == "load_hash" ||
 			    name == "load_or"
 			) {
 				error(node, "'#%.*s' must be used as a call", LIT(name));

+ 4 - 5
src/check_stmt.cpp

@@ -699,7 +699,7 @@ struct TypeAndToken {
 };
 
 
-void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Operand operand, bool use_expr = true) {
+void add_constant_switch_case(CheckerContext *ctx, PtrMap<uintptr, TypeAndToken> *seen, Operand operand, bool use_expr = true) {
 	if (operand.mode != Addressing_Constant) {
 		return;
 	}
@@ -707,7 +707,7 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Oper
 		return;
 	}
 	
-	HashKey key = hash_exact_value(operand.value);
+	uintptr key = hash_exact_value(operand.value);
 	TypeAndToken *found = map_get(seen, key);
 	if (found != nullptr) {
 		isize count = multi_map_count(seen, key);
@@ -964,7 +964,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 		}
 	}
 
-	Map<TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+	PtrMap<uintptr, TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue
 	map_init(&seen, heap_allocator());
 	defer (map_destroy(&seen));
 
@@ -1133,8 +1133,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 				continue;
 			}
 			ExactValue v = f->Constant.value;
-			HashKey key = hash_exact_value(v);
-			auto found = map_get(&seen, key);
+			auto found = map_get(&seen, hash_exact_value(v));
 			if (!found) {
 				array_add(&unhandled, f);
 			}

+ 3 - 3
src/check_type.cpp

@@ -228,7 +228,7 @@ Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type,
 	mutex_lock(&ctx->info->gen_types_mutex);
 	defer (mutex_unlock(&ctx->info->gen_types_mutex));
 
-	auto *found_gen_types = map_get(&ctx->info->gen_types, hash_pointer(original_type));
+	auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
 	if (found_gen_types != nullptr) {
 		// GB_ASSERT_MSG(ordered_operands.count >= param_count, "%td >= %td", ordered_operands.count, param_count);
 
@@ -311,13 +311,13 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
 	named_type->Named.type_name = e;
 
 	mutex_lock(&ctx->info->gen_types_mutex);
-	auto *found_gen_types = map_get(&ctx->info->gen_types, hash_pointer(original_type));
+	auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
 	if (found_gen_types) {
 		array_add(found_gen_types, e);
 	} else {
 		auto array = array_make<Entity *>(heap_allocator());
 		array_add(&array, e);
-		map_set(&ctx->info->gen_types, hash_pointer(original_type), array);
+		map_set(&ctx->info->gen_types, original_type, array);
 	}
 	mutex_unlock(&ctx->info->gen_types_mutex);
 }

+ 46 - 70
src/checker.cpp

@@ -810,7 +810,7 @@ void init_universal(void) {
 
 	bool defined_values_double_declaration = false;
 	for_array(i, bc->defined_values.entries) {
-		char const *name = cast(char const *)cast(uintptr)bc->defined_values.entries[i].key.key;
+		char const *name = bc->defined_values.entries[i].key;
 		ExactValue value = bc->defined_values.entries[i].value;
 		GB_ASSERT(value.kind != ExactValue_Invalid);
 
@@ -1086,7 +1086,7 @@ Scope *scope_of_node(Ast *node) {
 }
 ExprInfo *check_get_expr_info(CheckerContext *c, Ast *expr) {
 	if (c->untyped != nullptr) {
-		ExprInfo **found = map_get(c->untyped, hash_pointer(expr));
+		ExprInfo **found = map_get(c->untyped, expr);
 		if (found) {
 			return *found;
 		}
@@ -1094,7 +1094,7 @@ ExprInfo *check_get_expr_info(CheckerContext *c, Ast *expr) {
 	} else {
 		mutex_lock(&c->info->global_untyped_mutex);
 		defer (mutex_unlock(&c->info->global_untyped_mutex));
-		ExprInfo **found = map_get(&c->info->global_untyped, hash_pointer(expr));
+		ExprInfo **found = map_get(&c->info->global_untyped, expr);
 		if (found) {
 			return *found;
 		}
@@ -1104,23 +1104,23 @@ ExprInfo *check_get_expr_info(CheckerContext *c, Ast *expr) {
 
 void check_set_expr_info(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue value) {
 	if (c->untyped != nullptr) {
-		map_set(c->untyped, hash_pointer(expr), make_expr_info(mode, type, value, false));
+		map_set(c->untyped, expr, make_expr_info(mode, type, value, false));
 	} else {
 		mutex_lock(&c->info->global_untyped_mutex);
-		map_set(&c->info->global_untyped, hash_pointer(expr), make_expr_info(mode, type, value, false));
+		map_set(&c->info->global_untyped, expr, make_expr_info(mode, type, value, false));
 		mutex_unlock(&c->info->global_untyped_mutex);
 	}
 }
 
 void check_remove_expr_info(CheckerContext *c, Ast *e) {
 	if (c->untyped != nullptr) {
-		map_remove(c->untyped, hash_pointer(e));
-		GB_ASSERT(map_get(c->untyped, hash_pointer(e)) == nullptr);
+		map_remove(c->untyped, e);
+		GB_ASSERT(map_get(c->untyped, e) == nullptr);
 	} else {
 		auto *untyped = &c->info->global_untyped;
 		mutex_lock(&c->info->global_untyped_mutex);
-		map_remove(untyped, hash_pointer(e));
-		GB_ASSERT(map_get(untyped, hash_pointer(e)) == nullptr);
+		map_remove(untyped, e);
+		GB_ASSERT(map_get(untyped, e) == nullptr);
 		mutex_unlock(&c->info->global_untyped_mutex);
 	}
 }
@@ -1135,8 +1135,7 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
 	mutex_lock(&info->type_info_mutex);
 
 	isize entry_index = -1;
-	HashKey key = hash_type(type);
-	isize *found_entry_index = map_get(&info->type_info_map, key);
+	isize *found_entry_index = map_get(&info->type_info_map, type);
 	if (found_entry_index) {
 		entry_index = *found_entry_index;
 	}
@@ -1145,11 +1144,10 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
 		// TODO(bill): This is O(n) and can be very slow
 		for_array(i, info->type_info_map.entries){
 			auto *e = &info->type_info_map.entries[i];
-			Type *prev_type = cast(Type *)cast(uintptr)e->key.key;
-			if (are_types_identical(prev_type, type)) {
+			if (are_types_identical(e->key, type)) {
 				entry_index = e->value;
 				// NOTE(bill): Add it to the search map
-				map_set(&info->type_info_map, key, entry_index);
+				map_set(&info->type_info_map, type, entry_index);
 				break;
 			}
 		}
@@ -1484,7 +1482,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 
 	add_type_info_dependency(c->decl, t);
 
-	auto found = map_get(&c->info->type_info_map, hash_type(t));
+	auto found = map_get(&c->info->type_info_map, t);
 	if (found != nullptr) {
 		// Types have already been added
 		return;
@@ -1494,8 +1492,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 	isize ti_index = -1;
 	for_array(i, c->info->type_info_map.entries) {
 		auto *e = &c->info->type_info_map.entries[i];
-		Type *prev_type = cast(Type *)cast(uintptr)e->key.key;
-		if (are_types_identical(t, prev_type)) {
+		if (are_types_identical(t, e->key)) {
 			// Duplicate entry
 			ti_index = e->value;
 			prev = true;
@@ -1508,7 +1505,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 		ti_index = c->info->type_info_types.count;
 		array_add(&c->info->type_info_types, t);
 	}
-	map_set(&c->checker->info.type_info_map, hash_type(t), ti_index);
+	map_set(&c->checker->info.type_info_map, t, ti_index);
 
 	if (prev) {
 		// NOTE(bill): If a previous one exists already, no need to continue
@@ -2182,27 +2179,8 @@ bool is_entity_a_dependency(Entity *e) {
 	return false;
 }
 
-void add_entity_dependency_from_procedure_parameters(Map<EntityGraphNode *> *M, EntityGraphNode *n, Type *tuple) {
-	if (tuple == nullptr) {
-		return;
-	}
-	GB_ASSERT(tuple->kind == Type_Tuple);
-	TypeTuple *t = &tuple->Tuple;
-	for_array(i, t->variables) {
-		Entity *v = t->variables[i];
-		EntityGraphNode **found = map_get(M, hash_pointer(v));
-		if (found == nullptr) {
-			continue;
-		}
-		EntityGraphNode *m = *found;
-		entity_graph_node_set_add(&n->succ, m);
-		entity_graph_node_set_add(&m->pred, n);
-	}
-
-}
-
 Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) {
-	Map<EntityGraphNode *> M = {}; // Key: Entity *
+	PtrMap<Entity *, EntityGraphNode *> M = {};
 	map_init(&M, allocator, info->entities.count);
 	defer (map_destroy(&M));
 	for_array(i, info->entities) {
@@ -2210,7 +2188,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbA
 		if (is_entity_a_dependency(e)) {
 			EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode);
 			n->entity = e;
-			map_set(&M, hash_pointer(e), n);
+			map_set(&M, e, n);
 		}
 	}
 
@@ -2230,9 +2208,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbA
 			}
 			GB_ASSERT(dep != nullptr);
 			if (is_entity_a_dependency(dep)) {
-				EntityGraphNode **m_ = map_get(&M, hash_pointer(dep));
-				GB_ASSERT(m_ != nullptr);
-				EntityGraphNode *m = *m_;
+				EntityGraphNode *m = map_must_get(&M, dep);
 				entity_graph_node_set_add(&n->succ, m);
 				entity_graph_node_set_add(&m->pred, n);
 			}
@@ -2247,7 +2223,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbA
 
 	for_array(i, M.entries) {
 		auto *entry = &M.entries[i];
-		auto *e = cast(Entity *)cast(uintptr)entry->key.key;
+		auto *e = entry->key;
 		EntityGraphNode *n = entry->value;
 
 		if (e->kind == Entity_Procedure) {
@@ -3731,7 +3707,7 @@ String path_to_entity_name(String name, String fullpath, bool strip_extension=tr
 
 #if 1
 
-void add_import_dependency_node(Checker *c, Ast *decl, Map<ImportGraphNode *> *M) {
+void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPackage *, ImportGraphNode *> *M) {
 	AstPackage *parent_pkg = decl->file->pkg;
 
 	switch (decl->kind) {
@@ -3755,11 +3731,11 @@ void add_import_dependency_node(Checker *c, Ast *decl, Map<ImportGraphNode *> *M
 		ImportGraphNode *m = nullptr;
 		ImportGraphNode *n = nullptr;
 
-		found_node = map_get(M, hash_pointer(pkg));
+		found_node = map_get(M, pkg);
 		GB_ASSERT(found_node != nullptr);
 		m = *found_node;
 
-		found_node = map_get(M, hash_pointer(parent_pkg));
+		found_node = map_get(M, parent_pkg);
 		GB_ASSERT(found_node != nullptr);
 		n = *found_node;
 
@@ -3797,14 +3773,14 @@ void add_import_dependency_node(Checker *c, Ast *decl, Map<ImportGraphNode *> *M
 }
 
 Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c) {
-	Map<ImportGraphNode *> M = {}; // Key: AstPackage *
+	PtrMap<AstPackage *, ImportGraphNode *> M = {}; 
 	map_init(&M, heap_allocator(), 2*c->parser->packages.count);
 	defer (map_destroy(&M));
 
 	for_array(i, c->parser->packages) {
 		AstPackage *pkg = c->parser->packages[i];
 		ImportGraphNode *n = import_graph_node_create(heap_allocator(), pkg);
-		map_set(&M, hash_pointer(pkg), n);
+		map_set(&M, pkg, n);
 	}
 
 	// Calculate edges for graph M
@@ -4510,9 +4486,9 @@ void check_import_entities(Checker *c) {
 }
 
 
-Array<Entity *> find_entity_path(Entity *start, Entity *end, Map<Entity *> *visited = nullptr);
+Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<Entity *> *visited = nullptr);
 
-bool find_entity_path_tuple(Type *tuple, Entity *end, Map<Entity *> *visited, Array<Entity *> *path_) {
+bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet<Entity *> *visited, Array<Entity *> *path_) {
 	GB_ASSERT(path_ != nullptr);
 	if (tuple == nullptr) {
 		return false;
@@ -4544,26 +4520,24 @@ bool find_entity_path_tuple(Type *tuple, Entity *end, Map<Entity *> *visited, Ar
 	return false;
 }
 
-Array<Entity *> find_entity_path(Entity *start, Entity *end, Map<Entity *> *visited) {
-	Map<Entity *> visited_ = {};
+Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<Entity *> *visited) {
+	PtrSet<Entity *> visited_ = {};
 	bool made_visited = false;
 	if (visited == nullptr) {
 		made_visited = true;
-		map_init(&visited_, heap_allocator());
+		ptr_set_init(&visited_, heap_allocator());
 		visited = &visited_;
 	}
 	defer (if (made_visited) {
-		map_destroy(&visited_);
+		ptr_set_destroy(&visited_);
 	});
 
 	Array<Entity *> empty_path = {};
 
-	HashKey key = hash_pointer(start);
-
-	if (map_get(visited, key) != nullptr) {
+	if (ptr_set_exists(visited, start)) {
 		return empty_path;
 	}
-	map_set(visited, key, start);
+	ptr_set_add(visited, start);
 
 	DeclInfo *decl = start->decl_info;
 	if (decl) {
@@ -4955,7 +4929,7 @@ void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped) {
 		return;
 	}
 	for_array(i, untyped->entries) {
-		Ast *expr = cast(Ast *)cast(uintptr)untyped->entries[i].key.key;
+		Ast *expr = untyped->entries[i].key;
 		ExprInfo *info = untyped->entries[i].value;
 		if (expr != nullptr && info != nullptr) {
 			mpmc_enqueue(&cinfo->checker->global_untyped_queue, UntypedExprInfo{expr, info});
@@ -5207,6 +5181,17 @@ void check_sort_init_procedures(Checker *c) {
 	gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp);
 }
 
+void add_type_info_for_type_definitions(Checker *c) {
+	for_array(i, c->info.definitions) {
+		Entity *e = c->info.definitions[i];
+		if (e->kind == Entity_TypeName && e->type != nullptr) {
+			i64 align = type_align_of(e->type);
+			if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
+				add_type_info_type(&c->builtin_ctx, e->type);
+			}
+		}
+	}
+}
 
 void check_parsed_files(Checker *c) {
 	TIME_SECTION("map full filepaths to scope");
@@ -5286,7 +5271,7 @@ void check_parsed_files(Checker *c) {
 	}
 
 
-	TIME_SECTION("add type information");
+	TIME_SECTION("add basic type information");
 	// Add "Basic" type information
 	for (isize i = 0; i < Basic_COUNT; i++) {
 		Type *t = &basic_types[i];
@@ -5331,16 +5316,7 @@ void check_parsed_files(Checker *c) {
 	check_unchecked_bodies(c);
 
 	TIME_SECTION("add type info for type definitions");
-	for_array(i, c->info.definitions) {
-		Entity *e = c->info.definitions[i];
-		if (e->kind == Entity_TypeName && e->type != nullptr) {
-			i64 align = type_align_of(e->type);
-			if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
-				add_type_info_type(&c->builtin_ctx, e->type);
-			}
-		}
-	}
-
+	add_type_info_for_type_definitions(c);
 	check_merge_queues_into_arrays(c);
 
 	TIME_SECTION("check entry point");

+ 4 - 11
src/checker.hpp

@@ -264,7 +264,7 @@ struct UntypedExprInfo {
 	ExprInfo *info;
 };
 
-typedef Map<ExprInfo *> UntypedExprInfoMap; // Key: Ast *
+typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap; 
 typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
 
 // CheckerInfo stores all the symbol information for a type-checked program
@@ -316,12 +316,12 @@ struct CheckerInfo {
 
 	RecursiveMutex gen_procs_mutex;
 	RecursiveMutex gen_types_mutex;
-	Map<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
-	Map<Array<Entity *> > gen_types; // Key: Type *
+	PtrMap<Ast *, Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
+	PtrMap<Type *, Array<Entity *> > gen_types; 
 
 	BlockingMutex type_info_mutex; // NOT recursive
 	Array<Type *> type_info_types;
-	Map<isize>    type_info_map;   // Key: Type *
+	PtrMap<Type *, isize> type_info_map;
 
 	BlockingMutex foreign_mutex; // NOT recursive
 	StringMap<Entity *> foreigns;
@@ -406,13 +406,6 @@ gb_global AstPackage *intrinsics_pkg = nullptr;
 gb_global AstPackage *config_pkg      = nullptr;
 
 
-HashKey hash_node     (Ast *node)  { return hash_pointer(node); }
-HashKey hash_ast_file (AstFile *file)  { return hash_pointer(file); }
-HashKey hash_entity   (Entity *e)      { return hash_pointer(e); }
-HashKey hash_type     (Type *t)        { return hash_pointer(t); }
-HashKey hash_decl_info(DeclInfo *decl) { return hash_pointer(decl); }
-
-
 // CheckerInfo API
 TypeAndValue type_and_value_of_expr (Ast *expr);
 Type *       type_of_expr           (Ast *expr);

+ 6 - 6
src/common.cpp

@@ -275,9 +275,9 @@ gb_global String global_module_path = {0};
 gb_global bool global_module_path_set = false;
 
 
-#include "string_map.cpp"
-#include "map.cpp"
+#include "ptr_map.cpp"
 #include "ptr_set.cpp"
+#include "string_map.cpp"
 #include "string_set.cpp"
 #include "priority_queue.cpp"
 #include "thread_pool.cpp"
@@ -289,13 +289,13 @@ struct StringIntern {
 	char str[1];
 };
 
-Map<StringIntern *> string_intern_map = {}; // Key: u64
+PtrMap<uintptr, StringIntern *> string_intern_map = {}; // Key: u64
 gb_global Arena string_intern_arena = {};
 
 char const *string_intern(char const *text, isize len) {
 	u64 hash = gb_fnv64a(text, len);
-	u64 key = hash ? hash : 1;
-	StringIntern **found = map_get(&string_intern_map, hash_integer(key));
+	uintptr key = cast(uintptr)(hash ? hash : 1);
+	StringIntern **found = map_get(&string_intern_map, key);
 	if (found) {
 		for (StringIntern *it = *found; it != nullptr; it = it->next) {
 			if (it->len == len && gb_strncmp(it->str, (char *)text, len) == 0) {
@@ -309,7 +309,7 @@ char const *string_intern(char const *text, isize len) {
 	new_intern->next = found ? *found : nullptr;
 	gb_memmove(new_intern->str, text, len);
 	new_intern->str[len] = 0;
-	map_set(&string_intern_map, hash_integer(key), new_intern);
+	map_set(&string_intern_map, key, new_intern);
 	return new_intern->str;
 }
 

+ 17 - 27
src/docs_writer.cpp

@@ -26,12 +26,10 @@ struct OdinDocWriter {
 
 	StringMap<OdinDocString> string_cache;
 
-	Map<OdinDocFileIndex>   file_cache;      // Key: AstFile *
-	Map<OdinDocPkgIndex>    pkg_cache;       // Key: AstPackage *
-	Map<OdinDocEntityIndex> entity_cache;    // Key: Entity *
-	Map<Entity *>           entity_id_cache; // Key: OdinDocEntityIndex
-	Map<OdinDocTypeIndex>   type_cache;      // Key: Type *
-	Map<Type *>             type_id_cache;   // Key: OdinDocTypeIndex
+	PtrMap<AstFile *,    OdinDocFileIndex>   file_cache;
+	PtrMap<AstPackage *, OdinDocPkgIndex>    pkg_cache;
+	PtrMap<Entity *,     OdinDocEntityIndex> entity_cache;
+	PtrMap<Type *,       OdinDocTypeIndex>   type_cache;
 
 	OdinDocWriterItemTracker<OdinDocFile>   files;
 	OdinDocWriterItemTracker<OdinDocPkg>    pkgs;
@@ -61,9 +59,7 @@ void odin_doc_writer_prepare(OdinDocWriter *w) {
 	map_init(&w->file_cache, a);
 	map_init(&w->pkg_cache, a);
 	map_init(&w->entity_cache, a);
-	map_init(&w->entity_id_cache, a);
 	map_init(&w->type_cache, a);
-	map_init(&w->type_id_cache, a);
 
 	odin_doc_writer_item_tracker_init(&w->files,    1);
 	odin_doc_writer_item_tracker_init(&w->pkgs,     1);
@@ -81,9 +77,7 @@ void odin_doc_writer_destroy(OdinDocWriter *w) {
 	map_destroy(&w->file_cache);
 	map_destroy(&w->pkg_cache);
 	map_destroy(&w->entity_cache);
-	map_destroy(&w->entity_id_cache);
 	map_destroy(&w->type_cache);
-	map_destroy(&w->type_id_cache);
 }
 
 
@@ -115,9 +109,7 @@ void odin_doc_writer_start_writing(OdinDocWriter *w) {
 	map_clear(&w->file_cache);
 	map_clear(&w->pkg_cache);
 	map_clear(&w->entity_cache);
-	map_clear(&w->entity_id_cache);
 	map_clear(&w->type_cache);
-	map_clear(&w->type_id_cache);
 
 	isize total_size = odin_doc_writer_calc_total_size(w);
 	total_size = align_formula_isize(total_size, 8);
@@ -267,7 +259,7 @@ OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
 	if (pos.file_id != 0) {
 		AstFile *file = get_ast_file_from_id(pos.file_id);
 		if (file != nullptr) {
-			OdinDocFileIndex *file_index_found = map_get(&w->file_cache, hash_pointer(file));
+			OdinDocFileIndex *file_index_found = map_get(&w->file_cache, file);
 			GB_ASSERT(file_index_found != nullptr);
 			file_index = *file_index_found;
 		}
@@ -483,16 +475,16 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 	if (type == nullptr) {
 		return 0;
 	}
-	OdinDocTypeIndex *found = map_get(&w->type_cache, hash_pointer(type));
+	OdinDocTypeIndex *found = map_get(&w->type_cache, type);
 	if (found) {
 		return *found;
 	}
 	for_array(i, w->type_cache.entries) {
 		// NOTE(bill): THIS IS SLOW
-		Type *other = cast(Type *)cast(uintptr)w->type_cache.entries[i].key.key;
+		Type *other = w->type_cache.entries[i].key;
 		if (are_types_identical(type, other)) {
 			OdinDocTypeIndex index = w->type_cache.entries[i].value;
-			map_set(&w->type_cache, hash_pointer(type), index);
+			map_set(&w->type_cache, type, index);
 			return index;
 		}
 	}
@@ -502,8 +494,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 	OdinDocType doc_type = {};
 	OdinDocTypeIndex type_index = 0;
 	type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
-	map_set(&w->type_cache, hash_pointer(type), type_index);
-	map_set(&w->type_id_cache, hash_integer(type_index), type);
+	map_set(&w->type_cache, type, type_index);
 
 	switch (type->kind) {
 	case Type_Basic:
@@ -776,12 +767,12 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 		return 0;
 	}
 
-	OdinDocEntityIndex *prev_index = map_get(&w->entity_cache, hash_pointer(e));
+	OdinDocEntityIndex *prev_index = map_get(&w->entity_cache, e);
 	if (prev_index) {
 		return *prev_index;
 	}
 
-	if (e->pkg != nullptr && map_get(&w->pkg_cache, hash_pointer(e->pkg)) == nullptr) {
+	if (e->pkg != nullptr && map_get(&w->pkg_cache, e->pkg) == nullptr) {
 		return 0;
 	}
 
@@ -789,8 +780,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 	OdinDocEntity* dst = nullptr;
 
 	OdinDocEntityIndex doc_entity_index = odin_doc_write_item(w, &w->entities, &doc_entity, &dst);
-	map_set(&w->entity_cache, hash_pointer(e), doc_entity_index);
-	map_set(&w->entity_id_cache, hash_integer(doc_entity_index), e);
+	map_set(&w->entity_cache, e, doc_entity_index);
 
 
 	Ast *type_expr = nullptr;
@@ -901,7 +891,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
 		defer (array_free(&entities));
 
 		for_array(i, w->entity_cache.entries) {
-			Entity *e = cast(Entity *)cast(uintptr)w->entity_cache.entries[i].key.key;
+			Entity *e = w->entity_cache.entries[i].key;
 			entities[i] = e;
 		}
 		for_array(i, entities) {
@@ -912,7 +902,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
 	}
 
 	for_array(i, w->entity_cache.entries) {
-		Entity *e = cast(Entity *)cast(uintptr)w->entity_cache.entries[i].key.key;
+		Entity *e = w->entity_cache.entries[i].key;
 		OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value;
 		OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
 
@@ -955,7 +945,7 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
 	if (pkg->scope == nullptr) {
 		return {};
 	}
-	if (map_get(&w->pkg_cache, hash_pointer(pkg)) == nullptr) {
+	if (map_get(&w->pkg_cache, pkg) == nullptr) {
 		return {};
 	}
 
@@ -1056,7 +1046,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
 
 		OdinDocPkg *dst = nullptr;
 		OdinDocPkgIndex pkg_index = odin_doc_write_item(w, &w->pkgs, &doc_pkg, &dst);
-		map_set(&w->pkg_cache, hash_pointer(pkg), pkg_index);
+		map_set(&w->pkg_cache, pkg, pkg_index);
 
 		auto file_indices = array_make<OdinDocFileIndex>(heap_allocator(), 0, pkg->files.count);
 		defer (array_free(&file_indices));
@@ -1067,7 +1057,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
 			doc_file.pkg = pkg_index;
 			doc_file.name = odin_doc_write_string(w, file->fullpath);
 			OdinDocFileIndex file_index = odin_doc_write_item(w, &w->files, &doc_file);
-			map_set(&w->file_cache, hash_pointer(file), file_index);
+			map_set(&w->file_cache, file, file_index);
 			array_add(&file_indices, file_index);
 		}
 

+ 14 - 19
src/exact_value.cpp

@@ -63,44 +63,39 @@ struct ExactValue {
 
 gb_global ExactValue const empty_exact_value = {};
 
-HashKey hash_exact_value(ExactValue v) {
+uintptr hash_exact_value(ExactValue v) {
 	mutex_lock(&hash_exact_value_mutex);
 	defer (mutex_unlock(&hash_exact_value_mutex));
 	
-	HashKey empty = {};
 	switch (v.kind) {
 	case ExactValue_Invalid:
-		return empty;
+		return 0;
 	case ExactValue_Bool:
-		return hash_integer(u64(v.value_bool));
+		return gb_fnv32a(&v.value_bool, gb_size_of(v.value_bool));
 	case ExactValue_String:
-		{
-			char const *str = string_intern(v.value_string);
-			return hash_pointer(str);
-		}
+		return ptr_map_hash_key(string_intern(v.value_string));
 	case ExactValue_Integer:
 		{
-			HashKey key = hashing_proc(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
+			u32 key = gb_fnv32a(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
 			u8 last = (u8)v.value_integer.sign;
-			key.key = (key.key ^ last) * 0x100000001b3ll;
-			return key;
+			return (key ^ last) * 0x01000193;
 		}
 	case ExactValue_Float:
-		return hash_f64(v.value_float);
+		return gb_fnv32a(&v.value_float, gb_size_of(v.value_float));
 	case ExactValue_Pointer:
-		return hash_integer(v.value_pointer);
+		return ptr_map_hash_key(v.value_pointer);
 	case ExactValue_Complex:
-		return hashing_proc(v.value_complex, gb_size_of(Complex128));
+		return gb_fnv32a(v.value_complex, gb_size_of(Complex128));
 	case ExactValue_Quaternion:
-		return hashing_proc(v.value_quaternion, gb_size_of(Quaternion256));
+		return gb_fnv32a(v.value_quaternion, gb_size_of(Quaternion256));
 	case ExactValue_Compound:
-		return hash_pointer(v.value_compound);
+		return ptr_map_hash_key(v.value_compound);
 	case ExactValue_Procedure:
-		return hash_pointer(v.value_procedure);
+		return ptr_map_hash_key(v.value_procedure);
 	case ExactValue_Typeid:
-		return hash_pointer(v.value_typeid);
+		return ptr_map_hash_key(v.value_typeid);
 	}
-	return hashing_proc(&v, gb_size_of(ExactValue));
+	return gb_fnv32a(&v, gb_size_of(ExactValue));
 
 }
 

+ 6 - 9
src/llvm_backend.cpp

@@ -123,8 +123,7 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
 	Type *pt = alloc_type_pointer(type);
 	LLVMTypeRef ptr_type = lb_type(m, pt);
 
-	auto key = hash_type(type);
-	lbProcedure **found = map_get(&m->equal_procs, key);
+	lbProcedure **found = map_get(&m->equal_procs, type);
 	lbProcedure *compare_proc = nullptr;
 	if (found) {
 		compare_proc = *found;
@@ -140,7 +139,7 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
 	String proc_name = make_string_c(str);
 
 	lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
-	map_set(&m->equal_procs, key, p);
+	map_set(&m->equal_procs, type, p);
 	lb_begin_procedure_body(p);
 
 	LLVMValueRef x = LLVMGetParam(p->value, 0);
@@ -279,8 +278,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 
 	Type *pt = alloc_type_pointer(type);
 
-	auto key = hash_type(type);
-	lbProcedure **found = map_get(&m->hasher_procs, key);
+	lbProcedure **found = map_get(&m->hasher_procs, type);
 	if (found) {
 		GB_ASSERT(*found != nullptr);
 		return {(*found)->value, (*found)->type};
@@ -294,7 +292,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 	String proc_name = make_string_c(str);
 
 	lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_hasher_proc);
-	map_set(&m->hasher_procs, key, p);
+	map_set(&m->hasher_procs, type, p);
 	lb_begin_procedure_body(p);
 	defer (lb_end_procedure_body(p));
 
@@ -433,7 +431,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 
 
 lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
-	lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, hash_pointer(expr));
+	lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr);
 	if (found) {
 		return lb_find_procedure_value_from_entity(m, (*found)->entity);
 	}
@@ -473,8 +471,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
 		string_map_set(&m->members, name, value);
 	}
 
-	map_set(&m->anonymous_proc_lits, hash_pointer(expr), p);
-	map_set(&m->gen->anonymous_proc_lits, hash_pointer(expr), p);
+	map_set(&m->gen->anonymous_proc_lits, expr, p);
 
 	return value;
 }

+ 12 - 14
src/llvm_backend.hpp

@@ -114,25 +114,23 @@ struct lbModule {
 	CheckerInfo *info;
 	AstPackage *pkg; // associated
 
-	Map<LLVMTypeRef> types;                             // Key: Type *
-	Map<lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *
-	Map<Type *> llvm_types;                             // Key: LLVMTypeRef
+	PtrMap<Type *, LLVMTypeRef> types;
+	PtrMap<void *, lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *
 	i32 internal_type_level;
 
-	Map<lbValue>  values;           // Key: Entity *
-	Map<lbAddr>   soa_values;       // Key: Entity *
+	PtrMap<Entity *, lbValue> values;           
+	PtrMap<Entity *, lbAddr>  soa_values;       
 	StringMap<lbValue>  members;
 	StringMap<lbProcedure *> procedures;
-	Map<Entity *> procedure_values; // Key: LLVMValueRef
+	PtrMap<LLVMValueRef, Entity *> procedure_values;
 	Array<lbProcedure *> missing_procedures_to_check;
 
 	StringMap<LLVMValueRef> const_strings;
 
-	Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
-	Map<struct lbFunctionType *> function_type_map; // Key: Type *
+	PtrMap<Type *, struct lbFunctionType *> function_type_map; 
 
-	Map<lbProcedure *> equal_procs; // Key: Type *
-	Map<lbProcedure *> hasher_procs; // Key: Type *
+	PtrMap<Type *, lbProcedure *> equal_procs;
+	PtrMap<Type *, lbProcedure *> hasher_procs;
 
 	u32 nested_type_name_guid;
 
@@ -143,7 +141,7 @@ struct lbModule {
 
 	LLVMDIBuilderRef debug_builder;
 	LLVMMetadataRef debug_compile_unit;
-	Map<LLVMMetadataRef> debug_values; // Key: Pointer
+	PtrMap<void *, LLVMMetadataRef> debug_values; 
 
 	Array<lbIncompleteDebugType> debug_incomplete_types;
 };
@@ -155,11 +153,11 @@ struct lbGenerator {
 	Array<String> output_temp_paths;
 	String   output_base;
 	String   output_name;
-	Map<lbModule *> modules; // Key: AstPackage *
-	Map<lbModule *> modules_through_ctx; // Key: LLVMContextRef *
+	PtrMap<AstPackage *, lbModule *> modules; 
+	PtrMap<LLVMContextRef, lbModule *> modules_through_ctx; 
 	lbModule default_module;
 
-	Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
+	PtrMap<Ast *, lbProcedure *> anonymous_proc_lits; 
 
 	std::atomic<u32> global_array_index;
 	std::atomic<u32> global_generated_index;

+ 2 - 2
src/llvm_backend_debug.cpp

@@ -2,7 +2,7 @@ LLVMMetadataRef lb_get_llvm_metadata(lbModule *m, void *key) {
 	if (key == nullptr) {
 		return nullptr;
 	}
-	auto found = map_get(&m->debug_values, hash_pointer(key));
+	auto found = map_get(&m->debug_values, key);
 	if (found) {
 		return *found;
 	}
@@ -10,7 +10,7 @@ LLVMMetadataRef lb_get_llvm_metadata(lbModule *m, void *key) {
 }
 void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef value) {
 	if (key != nullptr) {
-		map_set(&m->debug_values, hash_pointer(key), value);
+		map_set(&m->debug_values, key, value);
 	}
 }
 

+ 3 - 3
src/llvm_backend_expr.cpp

@@ -3142,7 +3142,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 }
 
 lbAddr lb_get_soa_variable_addr(lbProcedure *p, Entity *e) {
-	return map_must_get(&p->module->soa_values, hash_entity(e));
+	return map_must_get(&p->module->soa_values, e);
 }
 lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
 	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Using);
@@ -3150,7 +3150,7 @@ lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
 	Entity *parent = e->using_parent;
 	Selection sel = lookup_field(parent->type, name, false);
 	GB_ASSERT(sel.entity != nullptr);
-	lbValue *pv = map_get(&p->module->values, hash_entity(parent));
+	lbValue *pv = map_get(&p->module->values, parent);
 
 	lbValue v = {};
 
@@ -3190,7 +3190,7 @@ lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) {
 
 
 	lbValue v = {};
-	lbValue *found = map_get(&p->module->values, hash_entity(e));
+	lbValue *found = map_get(&p->module->values, e);
 	if (found) {
 		v = *found;
 	} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Using) {

+ 25 - 31
src/llvm_backend_general.cpp

@@ -57,14 +57,12 @@ void lb_init_module(lbModule *m, Checker *c) {
 	gbAllocator a = heap_allocator();
 	map_init(&m->types, a);
 	map_init(&m->struct_field_remapping, a);
-	map_init(&m->llvm_types, a);
 	map_init(&m->values, a);
 	map_init(&m->soa_values, a);
 	string_map_init(&m->members, a);
 	map_init(&m->procedure_values, a);
 	string_map_init(&m->procedures, a);
 	string_map_init(&m->const_strings, a);
-	map_init(&m->anonymous_proc_lits, a);
 	map_init(&m->function_type_map, a);
 	map_init(&m->equal_procs, a);
 	map_init(&m->hasher_procs, a);
@@ -133,20 +131,20 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
 			auto m = gb_alloc_item(permanent_allocator(), lbModule);
 			m->pkg = pkg;
 			m->gen = gen;
-			map_set(&gen->modules, hash_pointer(pkg), m);
+			map_set(&gen->modules, pkg, m);
 			lb_init_module(m, c);
 		}
 	}
 
 	gen->default_module.gen = gen;
-	map_set(&gen->modules, hash_pointer(nullptr), &gen->default_module);
+	map_set(&gen->modules, cast(AstPackage *)nullptr, &gen->default_module);
 	lb_init_module(&gen->default_module, c);
 
 
 	for_array(i, gen->modules.entries) {
 		lbModule *m = gen->modules.entries[i].value;
 		LLVMContextRef ctx = LLVMGetModuleContext(m->mod);
-		map_set(&gen->modules_through_ctx, hash_pointer(ctx), m);
+		map_set(&gen->modules_through_ctx, ctx, m);
 	}
 
 	return true;
@@ -251,7 +249,7 @@ bool lb_is_instr_terminating(LLVMValueRef instr) {
 
 
 lbModule *lb_pkg_module(lbGenerator *gen, AstPackage *pkg) {
-	auto *found = map_get(&gen->modules, hash_pointer(pkg));
+	auto *found = map_get(&gen->modules, pkg);
 	if (found) {
 		return *found;
 	}
@@ -1590,7 +1588,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 				return lb_type_internal(m, base);
 			}
 
-			LLVMTypeRef *found = map_get(&m->types, hash_type(base));
+			LLVMTypeRef *found = map_get(&m->types, base);
 			if (found) {
 				LLVMTypeKind kind = LLVMGetTypeKind(*found);
 				if (kind == LLVMStructTypeKind) {
@@ -1600,7 +1598,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 						return llvm_type;
 					}
 					llvm_type = LLVMStructCreateNamed(ctx, name);
-					map_set(&m->types, hash_type(type), llvm_type);
+					map_set(&m->types, type, llvm_type);
 					lb_clone_struct_type(llvm_type, *found);
 					return llvm_type;
 				}
@@ -1616,7 +1614,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 						return llvm_type;
 					}
 					llvm_type = LLVMStructCreateNamed(ctx, name);
-					map_set(&m->types, hash_type(type), llvm_type);
+					map_set(&m->types, type, llvm_type);
 					lb_clone_struct_type(llvm_type, lb_type(m, base));
 					return llvm_type;
 				}
@@ -1697,7 +1695,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 				for_array(i, entries_field_remapping) {
 					entries_field_remapping[i] = cast(i32)i;
 				}
-				map_set(&m->struct_field_remapping, hash_pointer(fields[1]), entries_field_remapping);
+				map_set(&m->struct_field_remapping, cast(void *)fields[1], entries_field_remapping);
 			}
 			
 			return LLVMStructTypeInContext(ctx, fields, field_count, false);
@@ -1721,8 +1719,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 				field_remapping[0] = 0;
 				
 				LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
-				map_set(&m->struct_field_remapping, hash_pointer(struct_type), field_remapping);
-				map_set(&m->struct_field_remapping, hash_pointer(type), field_remapping);
+				map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
+				map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
 				return struct_type;
 			}
 			
@@ -1768,8 +1766,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 			}
 			
 			LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, type->Struct.is_packed);
-			map_set(&m->struct_field_remapping, hash_pointer(struct_type), field_remapping);
-			map_set(&m->struct_field_remapping, hash_pointer(type), field_remapping);			
+			map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
+			map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);			
 			#if 0
 			GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size, 
 			              "(%lld) %s vs (%lld) %s", 
@@ -1930,7 +1928,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 				              LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext());
 			}
 
-			map_set(&m->function_type_map, hash_type(type), ft);
+			map_set(&m->function_type_map, type, ft);
 			LLVMTypeRef new_abi_fn_ptr_type = lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg);
 			LLVMTypeRef new_abi_fn_type = LLVMGetElementType(new_abi_fn_ptr_type);
 
@@ -1991,7 +1989,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 LLVMTypeRef lb_type(lbModule *m, Type *type) {
 	type = default_type(type);
 
-	LLVMTypeRef *found = map_get(&m->types, hash_type(type));
+	LLVMTypeRef *found = map_get(&m->types, type);
 	if (found) {
 		return *found;
 	}
@@ -2002,21 +2000,18 @@ LLVMTypeRef lb_type(lbModule *m, Type *type) {
 	llvm_type = lb_type_internal(m, type);
 	m->internal_type_level -= 1;
 	if (m->internal_type_level == 0) {
-		map_set(&m->types, hash_type(type), llvm_type);
-		if (is_type_named(type)) {
-			map_set(&m->llvm_types, hash_pointer(llvm_type), type);
-		}
+		map_set(&m->types, type, llvm_type);
 	}
 	return llvm_type;
 }
 
 lbFunctionType *lb_get_function_type(lbModule *m, lbProcedure *p, Type *pt) {
 	lbFunctionType **ft_found = nullptr;
-	ft_found = map_get(&m->function_type_map, hash_type(pt));
+	ft_found = map_get(&m->function_type_map, pt);
 	if (!ft_found) {
 		LLVMTypeRef llvm_proc_type = lb_type(p->module, pt);
 		gb_unused(llvm_proc_type);
-		ft_found = map_get(&m->function_type_map, hash_type(pt));
+		ft_found = map_get(&m->function_type_map, pt);
 	}
 	GB_ASSERT(ft_found != nullptr);
 
@@ -2027,12 +2022,11 @@ void lb_ensure_abi_function_type(lbModule *m, lbProcedure *p) {
 	if (p->abi_function_type != nullptr) {
 		return;
 	}
-	auto hash = hash_type(p->type);
-	lbFunctionType **ft_found = map_get(&m->function_type_map, hash);
+	lbFunctionType **ft_found = map_get(&m->function_type_map, p->type);
 	if (ft_found == nullptr) {
 		LLVMTypeRef llvm_proc_type = lb_type(p->module, p->type);
 		gb_unused(llvm_proc_type);
-		ft_found = map_get(&m->function_type_map, hash);
+		ft_found = map_get(&m->function_type_map, p->type);
 	}
 	GB_ASSERT(ft_found != nullptr);
 	p->abi_function_type = *ft_found;
@@ -2041,7 +2035,7 @@ void lb_ensure_abi_function_type(lbModule *m, lbProcedure *p) {
 
 void lb_add_entity(lbModule *m, Entity *e, lbValue val) {
 	if (e != nullptr) {
-		map_set(&m->values, hash_entity(e), val);
+		map_set(&m->values, e, val);
 	}
 }
 void lb_add_member(lbModule *m, String const &name, lbValue val) {
@@ -2054,7 +2048,7 @@ void lb_add_member(lbModule *m, StringHashKey const &key, lbValue val) {
 }
 void lb_add_procedure_value(lbModule *m, lbProcedure *p) {
 	if (p->entity != nullptr) {
-		map_set(&m->procedure_values, hash_pointer(p->value), p->entity);
+		map_set(&m->procedure_values, p->value, p->entity);
 	}
 	string_map_set(&m->procedures, p->name, p);
 }
@@ -2368,7 +2362,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
 
 
 lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) {
-	auto *found = map_get(&m->values, hash_entity(e));
+	auto *found = map_get(&m->values, e);
 	if (found) {
 		auto v = *found;
 		// NOTE(bill): This is because pointers are already pointers in LLVM
@@ -2417,7 +2411,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
 	e = strip_entity_wrapping(e);
 	GB_ASSERT(e != nullptr);
 
-	auto *found = map_get(&m->values, hash_entity(e));
+	auto *found = map_get(&m->values, e);
 	if (found) {
 		return *found;
 	}
@@ -2437,7 +2431,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
 	if (!ignore_body) {
 		array_add(&m->missing_procedures_to_check, missing_proc);
 	}
-	found = map_get(&m->values, hash_entity(e));
+	found = map_get(&m->values, e);
 	if (found) {
 		return *found;
 	}
@@ -2501,7 +2495,7 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
 		return lb_find_procedure_value_from_entity(m, e);
 	}
 
-	auto *found = map_get(&m->values, hash_entity(e));
+	auto *found = map_get(&m->values, e);
 	if (found) {
 		return *found;
 	}

+ 1 - 1
src/llvm_backend_opt.cpp

@@ -387,7 +387,7 @@ void lb_run_remove_unused_function_pass(lbModule *m) {
 				continue;
 			}
 			
-			Entity **found = map_get(&m->procedure_values, hash_pointer(curr_func));
+			Entity **found = map_get(&m->procedure_values, curr_func);
 			if (found && *found) {
 				Entity *e = *found;
 				bool is_required = (e->flags & EntityFlag_Require) == EntityFlag_Require;

+ 1 - 1
src/llvm_backend_proc.cpp

@@ -948,7 +948,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
 
 	}
 
-	Entity **found = map_get(&p->module->procedure_values, hash_pointer(value.value));
+	Entity **found = map_get(&p->module->procedure_values, value.value);
 	if (found != nullptr) {
 		Entity *e = *found;
 		if (e != nullptr && entity_has_deferred_procedure(e)) {

+ 6 - 6
src/llvm_backend_stmt.cpp

@@ -83,7 +83,7 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
 		DeclInfo *decl = decl_info_of_entity(e);
 		ast_node(pl, ProcLit, decl->proc_lit);
 		if (pl->body != nullptr) {
-			auto *found = map_get(&info->gen_procs, hash_pointer(ident));
+			auto *found = map_get(&info->gen_procs, ident);
 			if (found) {
 				auto procs = *found;
 				for_array(i, procs) {
@@ -670,7 +670,7 @@ void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *sco
 		Entity *e = entity_of_node(rs->vals[0]);
 		if (e != nullptr) {
 			lbAddr soa_val = lb_addr_soa_variable(array.addr, lb_addr_load(p, index), nullptr);
-			map_set(&p->module->soa_values, hash_entity(e), soa_val);
+			map_set(&p->module->soa_values, e, soa_val);
 		}
 	}
 	if (val_types[1]) {
@@ -1525,7 +1525,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 	} else if (return_count == 1) {
 		Entity *e = tuple->variables[0];
 		if (res_count == 0) {
-			lbValue found = map_must_get(&p->module->values, hash_entity(e));
+			lbValue found = map_must_get(&p->module->values, e);
 			res = lb_emit_load(p, found);
 		} else {
 			res = lb_build_expr(p, return_results[0]);
@@ -1534,7 +1534,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 		if (p->type->Proc.has_named_results) {
 			// NOTE(bill): store the named values before returning
 			if (e->token.string != "") {
-				lbValue found = map_must_get(&p->module->values, hash_entity(e));
+				lbValue found = map_must_get(&p->module->values, e);
 				lb_emit_store(p, found, lb_emit_conv(p, res, e->type));
 			}
 		}
@@ -1558,7 +1558,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 		} else {
 			for (isize res_index = 0; res_index < return_count; res_index++) {
 				Entity *e = tuple->variables[res_index];
-				lbValue found = map_must_get(&p->module->values, hash_entity(e));
+				lbValue found = map_must_get(&p->module->values, e);
 				lbValue res = lb_emit_load(p, found);
 				array_add(&results, res);
 			}
@@ -1580,7 +1580,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 				if (e->token.string == "") {
 					continue;
 				}
-				named_results[i] = map_must_get(&p->module->values, hash_entity(e));
+				named_results[i] = map_must_get(&p->module->values, e);
 				values[i] = lb_emit_conv(p, results[i], e->type);
 			}
 

+ 3 - 3
src/llvm_backend_utility.cpp

@@ -401,7 +401,7 @@ lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
 			GB_ASSERT(end_entity->token.string.len != 0);
 
 			// NOTE(bill): store the named values before returning
-			lbValue found = map_must_get(&p->module->values, hash_entity(end_entity));
+			lbValue found = map_must_get(&p->module->values, end_entity);
 			lb_emit_store(p, found, rhs);
 
 			lb_build_return_stmt(p, {});
@@ -811,9 +811,9 @@ lbValue lb_address_from_load(lbProcedure *p, lbValue value) {
 lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t) {
 	t = base_type(t);
 	LLVMTypeRef struct_type = lb_type(m, t);
-	auto *field_remapping = map_get(&m->struct_field_remapping, hash_pointer(struct_type));
+	auto *field_remapping = map_get(&m->struct_field_remapping, cast(void *)struct_type);
 	if (field_remapping == nullptr) {
-		field_remapping = map_get(&m->struct_field_remapping, hash_pointer(t));
+		field_remapping = map_get(&m->struct_field_remapping, cast(void *)t);
 	}
 	GB_ASSERT(field_remapping != nullptr);
 	return *field_remapping;

+ 1 - 1
src/main.cpp

@@ -1109,7 +1109,7 @@ bool parse_build_flags(Array<String> args) {
 								break;
 							}
 
-							HashKey key = hash_pointer(string_intern(name));
+							char const *key = string_intern(name);
 
 							if (map_get(&build_context.defined_values, key) != nullptr) {
 								gb_printf_err("Defined constant '%.*s' already exists\n", LIT(name));

+ 0 - 358
src/map.cpp

@@ -1,358 +0,0 @@
-// A `Map` is an unordered hash table which can allow for a key to point to multiple values
-// with the use of the `multi_*` procedures.
-// TODO(bill): I should probably allow the `multi_map_*` stuff to be #ifdefed out
-
-#define MAP_ENABLE_MULTI_MAP 1
-
-#ifndef MAP_UTIL_STUFF
-#define MAP_UTIL_STUFF
-// NOTE(bill): This util stuff is the same for every `Map`
-struct MapFindResult {
-	isize hash_index;
-	isize entry_prev;
-	isize entry_index;
-};
-
-
-struct HashKey {
-	u64 key;
-};
-GB_STATIC_ASSERT(gb_size_of(u64) >= gb_size_of(void *));
-
-gb_inline HashKey hashing_proc(void const *data, isize len) {
-	HashKey h = {};
-	h.key = fnv64a(data, len);
-	return h;
-}
-
-gb_inline HashKey hash_pointer(void const *ptr) {
-	HashKey h = {};
-	h.key = cast(u64)cast(uintptr)ptr;
-	return h;
-}
-
-gb_inline HashKey hash_integer(u64 u) {
-	HashKey h = {};
-	h.key = u;
-	return h;
-}
-gb_inline HashKey hash_f64(f64 f) {
-	HashKey h = {};
-	h.key = bit_cast<u64>(f);
-	return h;
-}
-
-gb_inline bool hash_key_equal(HashKey a, HashKey b) {
-	return a.key == b.key;
-}
-gb_inline bool operator==(HashKey a, HashKey b) { return hash_key_equal(a, b); }
-gb_inline bool operator!=(HashKey a, HashKey b) { return !hash_key_equal(a, b); }
-
-#endif
-
-template <typename T>
-struct MapEntry {
-	HashKey  key;
-	isize    next;
-	T        value;
-};
-
-template <typename T>
-struct Map {
-	Slice<isize>        hashes;
-	Array<MapEntry<T> > entries;
-};
-
-
-template <typename T> void map_init             (Map<T> *h, gbAllocator a, isize capacity = 16);
-template <typename T> void map_destroy          (Map<T> *h);
-template <typename T> T *  map_get              (Map<T> *h, HashKey const &key);
-template <typename T> T &  map_must_get         (Map<T> *h, HashKey const &key);
-template <typename T> void map_set              (Map<T> *h, HashKey const &key, T const &value);
-template <typename T> void map_remove           (Map<T> *h, HashKey const &key);
-template <typename T> void map_clear            (Map<T> *h);
-template <typename T> void map_grow             (Map<T> *h);
-template <typename T> void map_rehash           (Map<T> *h, isize new_count);
-
-#if MAP_ENABLE_MULTI_MAP
-// Mutlivalued map procedure
-template <typename T> MapEntry<T> * multi_map_find_first(Map<T> *h, HashKey const &key);
-template <typename T> MapEntry<T> * multi_map_find_next (Map<T> *h, MapEntry<T> *e);
-
-template <typename T> isize multi_map_count     (Map<T> *h, HashKey const &key);
-template <typename T> void  multi_map_get_all   (Map<T> *h, HashKey const &key, T *items);
-template <typename T> void  multi_map_insert    (Map<T> *h, HashKey const &key, T const &value);
-template <typename T> void  multi_map_remove    (Map<T> *h, HashKey const &key, MapEntry<T> *e);
-template <typename T> void  multi_map_remove_all(Map<T> *h, HashKey const &key);
-#endif
-
-template <typename T>
-gb_inline void map_init(Map<T> *h, gbAllocator a, isize capacity) {
-	capacity = next_pow2_isize(capacity);
-	slice_init(&h->hashes,  a, capacity);
-	array_init(&h->entries, a, 0, capacity);
-	for (isize i = 0; i < capacity; i++) {
-		h->hashes.data[i] = -1;
-	}
-}
-
-template <typename T>
-gb_inline void map_destroy(Map<T> *h) {
-	slice_free(&h->hashes, h->entries.allocator);
-	array_free(&h->entries);
-}
-
-template <typename T>
-gb_internal isize map__add_entry(Map<T> *h, HashKey const &key) {
-	MapEntry<T> e = {};
-	e.key = key;
-	e.next = -1;
-	array_add(&h->entries, e);
-	return h->entries.count-1;
-}
-
-template <typename T>
-gb_internal MapFindResult map__find(Map<T> *h, HashKey const &key) {
-	MapFindResult fr = {-1, -1, -1};
-	if (h->hashes.count == 0) {
-		return fr;
-	}
-	fr.hash_index = key.key & (h->hashes.count-1);
-	fr.entry_index = h->hashes.data[fr.hash_index];
-	while (fr.entry_index >= 0) {
-		if (hash_key_equal(h->entries.data[fr.entry_index].key, key)) {
-			return fr;
-		}
-		fr.entry_prev = fr.entry_index;
-		fr.entry_index = h->entries.data[fr.entry_index].next;
-	}
-	return fr;
-}
-
-template <typename T>
-gb_internal MapFindResult map__find_from_entry(Map<T> *h, MapEntry<T> *e) {
-	MapFindResult fr = {-1, -1, -1};
-	if (h->hashes.count == 0) {
-		return fr;
-	}
-	fr.hash_index  = e->key.key & (h->hashes.count-1);
-	fr.entry_index = h->hashes.data[fr.hash_index];
-	while (fr.entry_index >= 0) {
-		if (&h->entries.data[fr.entry_index] == e) {
-			return fr;
-		}
-		fr.entry_prev = fr.entry_index;
-		fr.entry_index = h->entries.data[fr.entry_index].next;
-	}
-	return fr;
-}
-
-template <typename T>
-gb_internal b32 map__full(Map<T> *h) {
-	return 0.75f * h->hashes.count <= h->entries.count;
-}
-
-template <typename T>
-gb_inline void map_grow(Map<T> *h) {
-	isize new_count = gb_max(h->hashes.count<<1, 16);
-	map_rehash(h, new_count);
-}
-
-template <typename T>
-void map_rehash(Map<T> *h, isize new_count) {
-	isize i, j;
-	Map<T> nh = {};
-	new_count = next_pow2_isize(new_count);
-	nh.hashes = h->hashes;
-	nh.entries.allocator = h->entries.allocator;
-	slice_resize(&nh.hashes, h->entries.allocator, new_count);
-	for (i = 0; i < new_count; i++) {
-		nh.hashes.data[i] = -1;
-	}
-	array_reserve(&nh.entries, ARRAY_GROW_FORMULA(h->entries.count));
-	for (i = 0; i < h->entries.count; i++) {
-		MapEntry<T> *e = &h->entries.data[i];
-		MapFindResult fr;
-		if (nh.hashes.count == 0) {
-			map_grow(&nh);
-		}
-		fr = map__find(&nh, e->key);
-		j = map__add_entry(&nh, e->key);
-		if (fr.entry_prev < 0) {
-			nh.hashes.data[fr.hash_index] = j;
-		} else {
-			nh.entries.data[fr.entry_prev].next = j;
-		}
-		nh.entries.data[j].next = fr.entry_index;
-		nh.entries.data[j].value = e->value;
-		if (map__full(&nh)) {
-			map_grow(&nh);
-		}
-	}
-	array_free(&h->entries);
-	*h = nh;
-}
-
-template <typename T>
-T *map_get(Map<T> *h, HashKey const &key) {
-	isize index = map__find(h, key).entry_index;
-	if (index >= 0) {
-		return &h->entries.data[index].value;
-	}
-	return nullptr;
-}
-
-template <typename T>
-T &map_must_get(Map<T> *h, HashKey const &key) {
-	isize index = map__find(h, key).entry_index;
-	GB_ASSERT(index >= 0);
-	return h->entries.data[index].value;
-}
-
-template <typename T>
-void map_set(Map<T> *h, HashKey const &key, T const &value) {
-	isize index;
-	MapFindResult fr;
-	if (h->hashes.count == 0) {
-		map_grow(h);
-	}
-	fr = map__find(h, key);
-	if (fr.entry_index >= 0) {
-		index = fr.entry_index;
-	} else {
-		index = map__add_entry(h, key);
-		if (fr.entry_prev >= 0) {
-			h->entries.data[fr.entry_prev].next = index;
-		} else {
-			h->hashes.data[fr.hash_index] = index;
-		}
-	}
-	h->entries.data[index].value = value;
-
-	if (map__full(h)) {
-		map_grow(h);
-	}
-}
-
-
-template <typename T>
-void map__erase(Map<T> *h, MapFindResult const &fr) {
-	MapFindResult last;
-	if (fr.entry_prev < 0) {
-		h->hashes.data[fr.hash_index] = h->entries.data[fr.entry_index].next;
-	} else {
-		h->entries.data[fr.entry_prev].next = h->entries.data[fr.entry_index].next;
-	}
-	if (fr.entry_index == h->entries.count-1) {
-		array_pop(&h->entries);
-		return;
-	}
-	h->entries.data[fr.entry_index] = h->entries.data[h->entries.count-1];
-	array_pop(&h->entries);
-	
-	last = map__find(h, h->entries.data[fr.entry_index].key);
-	if (last.entry_prev >= 0) {
-		h->entries.data[last.entry_prev].next = fr.entry_index;
-	} else {
-		h->hashes.data[last.hash_index] = fr.entry_index;
-	}
-}
-
-template <typename T>
-void map_remove(Map<T> *h, HashKey const &key) {
-	MapFindResult fr = map__find(h, key);
-	if (fr.entry_index >= 0) {
-		map__erase(h, fr);
-	}
-}
-
-template <typename T>
-gb_inline void map_clear(Map<T> *h) {
-	array_clear(&h->entries);
-	for (isize i = 0; i < h->hashes.count; i++) {
-		h->hashes.data[i] = -1;
-	}
-}
-
-
-#if MAP_ENABLE_MULTI_MAP
-template <typename T>
-MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey const &key) {
-	isize i = map__find(h, key).entry_index;
-	if (i < 0) {
-		return nullptr;
-	}
-	return &h->entries.data[i];
-}
-
-template <typename T>
-MapEntry<T> *multi_map_find_next(Map<T> *h, MapEntry<T> *e) {
-	isize i = e->next;
-	while (i >= 0) {
-		if (hash_key_equal(h->entries.data[i].key, e->key)) {
-			return &h->entries.data[i];
-		}
-		i = h->entries.data[i].next;
-	}
-	return nullptr;
-}
-
-template <typename T>
-isize multi_map_count(Map<T> *h, HashKey const &key) {
-	isize count = 0;
-	MapEntry<T> *e = multi_map_find_first(h, key);
-	while (e != nullptr) {
-		count++;
-		e = multi_map_find_next(h, e);
-	}
-	return count;
-}
-
-template <typename T>
-void multi_map_get_all(Map<T> *h, HashKey const &key, T *items) {
-	isize i = 0;
-	MapEntry<T> *e = multi_map_find_first(h, key);
-	while (e != nullptr) {
-		items[i++] = e->value;
-		e = multi_map_find_next(h, e);
-	}
-}
-
-template <typename T>
-void multi_map_insert(Map<T> *h, HashKey const &key, T const &value) {
-	MapFindResult fr;
-	isize i;
-	if (h->hashes.count == 0) {
-		map_grow(h);
-	}
-	// Make
-	fr = map__find(h, key);
-	i = map__add_entry(h, key);
-	if (fr.entry_prev < 0) {
-		h->hashes.data[fr.hash_index] = i;
-	} else {
-		h->entries.data[fr.entry_prev].next = i;
-	}
-	h->entries.data[i].next = fr.entry_index;
-	h->entries.data[i].value = value;
-	// Grow if needed
-	if (map__full(h)) {
-		map_grow(h);
-	}
-}
-
-template <typename T>
-void multi_map_remove(Map<T> *h, HashKey const &key, MapEntry<T> *e) {
-	MapFindResult fr = map__find_from_entry(h, e);
-	if (fr.entry_index >= 0) {
-		map__erase(h, fr);
-	}
-}
-
-template <typename T>
-void multi_map_remove_all(Map<T> *h, HashKey const &key) {
-	while (map_get(h, key) != nullptr) {
-		map_remove(h, key);
-	}
-}
-#endif

+ 339 - 0
src/ptr_map.cpp

@@ -0,0 +1,339 @@
+#define PTR_MAP_ENABLE_MULTI_MAP 1
+
+typedef u32 MapIndex;
+
+struct MapFindResult {
+	MapIndex hash_index;
+	MapIndex entry_prev;
+	MapIndex entry_index;
+};
+
+enum : MapIndex { MAP_SENTINEL = ~(MapIndex)0 };
+
+template <typename K, typename V>
+struct PtrMapEntry {
+	static_assert(sizeof(K) == sizeof(void *), "Key size must be pointer size");
+	
+	K        key;
+	V        value;
+	MapIndex next;
+};
+
+template <typename K, typename V>
+struct PtrMap {
+	Slice<MapIndex>           hashes;
+	Array<PtrMapEntry<K, V> > entries;
+};
+
+
+u32 ptr_map_hash_key(uintptr key) {
+#if defined(GB_ARCH_64_BIT)
+	u64 x = (u64)key;
+	u8 count = (u8)(x >> 59);
+	x ^= x >> (5 + count);
+	x *= 12605985483714917081ull;
+	return (u32)(x ^ (x >> 43));
+#elif defined(GB_ARCH_32_BIT)
+	u32 state = ((u32)key) * 747796405u + 2891336453u;
+	u32 word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
+	return (word >> 22u) ^ word;
+#endif
+}
+u32 ptr_map_hash_key(void const *key) {
+	return ptr_map_hash_key((uintptr)key);
+}
+
+
+template <typename K, typename V> void map_init             (PtrMap<K, V> *h, gbAllocator a, isize capacity = 16);
+template <typename K, typename V> void map_destroy          (PtrMap<K, V> *h);
+template <typename K, typename V> V *  map_get              (PtrMap<K, V> *h, K key);
+template <typename K, typename V> void map_set              (PtrMap<K, V> *h, K key, V const &value);
+template <typename K, typename V> void map_remove           (PtrMap<K, V> *h, K key);
+template <typename K, typename V> void map_clear            (PtrMap<K, V> *h);
+template <typename K, typename V> void map_grow             (PtrMap<K, V> *h);
+template <typename K, typename V> void map_rehash           (PtrMap<K, V> *h, isize new_count);
+template <typename K, typename V> void map_reserve          (PtrMap<K, V> *h, isize cap);
+
+#if PTR_MAP_ENABLE_MULTI_MAP
+// Mutlivalued map procedure
+template <typename K, typename V> PtrMapEntry<K, V> * multi_map_find_first(PtrMap<K, V> *h, K key);
+template <typename K, typename V> PtrMapEntry<K, V> * multi_map_find_next (PtrMap<K, V> *h, PtrMapEntry<K, V> *e);
+
+template <typename K, typename V> isize multi_map_count     (PtrMap<K, V> *h, K key);
+template <typename K, typename V> void  multi_map_get_all   (PtrMap<K, V> *h, K key, V *items);
+template <typename K, typename V> void  multi_map_insert    (PtrMap<K, V> *h, K key, V const &value);
+template <typename K, typename V> void  multi_map_remove    (PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e);
+template <typename K, typename V> void  multi_map_remove_all(PtrMap<K, V> *h, K key);
+#endif
+
+template <typename K, typename V>
+gb_inline void map_init(PtrMap<K, V> *h, gbAllocator a, isize capacity) {
+	capacity = next_pow2_isize(capacity);
+	slice_init(&h->hashes,  a, capacity);
+	array_init(&h->entries, a, 0, capacity);
+	for (isize i = 0; i < capacity; i++) {
+		h->hashes.data[i] = MAP_SENTINEL;
+	}
+}
+
+template <typename K, typename V>
+gb_inline void map_destroy(PtrMap<K, V> *h) {
+	slice_free(&h->hashes, h->entries.allocator);
+	array_free(&h->entries);
+}
+
+template <typename K, typename V>
+gb_internal MapIndex map__add_entry(PtrMap<K, V> *h, K key) {
+	PtrMapEntry<K, V> e = {};
+	e.key = key;
+	e.next = MAP_SENTINEL;
+	array_add(&h->entries, e);
+	return cast(MapIndex)(h->entries.count-1);
+}
+
+template <typename K, typename V>
+gb_internal MapFindResult map__find(PtrMap<K, V> *h, K key) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (h->hashes.count == 0) {
+		return fr;
+	}
+	u32 hash = ptr_map_hash_key(key);
+	fr.hash_index = cast(MapIndex)(hash & (h->hashes.count-1));
+	fr.entry_index = h->hashes.data[fr.hash_index];
+	while (fr.entry_index != MAP_SENTINEL) {
+		if (h->entries.data[fr.entry_index].key == key) {
+			return fr;
+		}
+		fr.entry_prev = fr.entry_index;
+		fr.entry_index = h->entries.data[fr.entry_index].next;
+	}
+	return fr;
+}
+
+template <typename K, typename V>
+gb_internal MapFindResult map__find_from_entry(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (h->hashes.count == 0) {
+		return fr;
+	}
+	u32 hash = ptr_map_hash_key(e->key);
+	fr.hash_index  = cast(MapIndex)(hash & (h->hashes.count-1));
+	fr.entry_index = h->hashes.data[fr.hash_index];
+	while (fr.entry_index != MAP_SENTINEL) {
+		if (&h->entries.data[fr.entry_index] == e) {
+			return fr;
+		}
+		fr.entry_prev = fr.entry_index;
+		fr.entry_index = h->entries.data[fr.entry_index].next;
+	}
+	return fr;
+}
+
+template <typename K, typename V>
+gb_internal b32 map__full(PtrMap<K, V> *h) {
+	return 0.75f * h->hashes.count <= h->entries.count;
+}
+
+template <typename K, typename V>
+gb_inline void map_grow(PtrMap<K, V> *h) {
+	isize new_count = gb_max(h->hashes.count<<1, 16);
+	map_rehash(h, new_count);
+}
+
+template <typename K, typename V>
+void map_reset_entries(PtrMap<K, V> *h) {
+	for (isize i = 0; i < h->hashes.count; i++) {
+		h->hashes.data[i] = MAP_SENTINEL;
+	}
+	for (isize i = 0; i < h->entries.count; i++) {
+		MapFindResult fr;
+		PtrMapEntry<K, V> *e = &h->entries.data[i];
+		e->next = MAP_SENTINEL;
+		fr = map__find_from_entry(h, e);
+		if (fr.entry_prev == MAP_SENTINEL) {
+			h->hashes[fr.hash_index] = cast(MapIndex)i;
+		} else {
+			h->entries[fr.entry_prev].next = cast(MapIndex)i;
+		}
+	}
+}
+
+template <typename K, typename V>
+void map_reserve(PtrMap<K, V> *h, isize cap) {
+	array_reserve(&h->entries, cap);
+	if (h->entries.count*2 < h->hashes.count) {
+		return;
+	}
+	slice_resize(&h->hashes, h->entries.allocator, cap*2);
+	map_reset_entries(h);
+}
+
+
+template <typename K, typename V>
+void map_rehash(PtrMap<K, V> *h, isize new_count) {
+	map_reserve(h, new_count);
+}
+
+template <typename K, typename V>
+V *map_get(PtrMap<K, V> *h, K key) {
+	MapIndex index = map__find(h, key).entry_index;
+	if (index != MAP_SENTINEL) {
+		return &h->entries.data[index].value;
+	}
+	return nullptr;
+}
+
+template <typename K, typename V>
+V &map_must_get(PtrMap<K, V> *h, K key) {
+	MapIndex index = map__find(h, key).entry_index;
+	GB_ASSERT(index != MAP_SENTINEL);
+	return h->entries.data[index].value;
+}
+
+template <typename K, typename V>
+void map_set(PtrMap<K, V> *h, K key, V const &value) {
+	MapIndex index;
+	MapFindResult fr;
+	if (h->hashes.count == 0) {
+		map_grow(h);
+	}
+	fr = map__find(h, key);
+	if (fr.entry_index != MAP_SENTINEL) {
+		index = fr.entry_index;
+	} else {
+		index = map__add_entry(h, key);
+		if (fr.entry_prev != MAP_SENTINEL) {
+			h->entries.data[fr.entry_prev].next = index;
+		} else {
+			h->hashes.data[fr.hash_index] = index;
+		}
+	}
+	h->entries.data[index].value = value;
+
+	if (map__full(h)) {
+		map_grow(h);
+	}
+}
+
+
+template <typename K, typename V>
+void map__erase(PtrMap<K, V> *h, MapFindResult const &fr) {
+	MapFindResult last;
+	if (fr.entry_prev == MAP_SENTINEL) {
+		h->hashes.data[fr.hash_index] = h->entries.data[fr.entry_index].next;
+	} else {
+		h->entries.data[fr.entry_prev].next = h->entries.data[fr.entry_index].next;
+	}
+	if (fr.entry_index == h->entries.count-1) {
+		array_pop(&h->entries);
+		return;
+	}
+	h->entries.data[fr.entry_index] = h->entries.data[h->entries.count-1];
+	array_pop(&h->entries);
+	
+	last = map__find(h, h->entries.data[fr.entry_index].key);
+	if (last.entry_prev != MAP_SENTINEL) {
+		h->entries.data[last.entry_prev].next = fr.entry_index;
+	} else {
+		h->hashes.data[last.hash_index] = fr.entry_index;
+	}
+}
+
+template <typename K, typename V>
+void map_remove(PtrMap<K, V> *h, K key) {
+	MapFindResult fr = map__find(h, key);
+	if (fr.entry_index != MAP_SENTINEL) {
+		map__erase(h, fr);
+	}
+}
+
+template <typename K, typename V>
+gb_inline void map_clear(PtrMap<K, V> *h) {
+	array_clear(&h->entries);
+	for (isize i = 0; i < h->hashes.count; i++) {
+		h->hashes.data[i] = MAP_SENTINEL;
+	}
+}
+
+
+#if PTR_MAP_ENABLE_MULTI_MAP
+template <typename K, typename V>
+PtrMapEntry<K, V> *multi_map_find_first(PtrMap<K, V> *h, K key) {
+	MapIndex i = map__find(h, key).entry_index;
+	if (i == MAP_SENTINEL) {
+		return nullptr;
+	}
+	return &h->entries.data[i];
+}
+
+template <typename K, typename V>
+PtrMapEntry<K, V> *multi_map_find_next(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
+	MapIndex i = e->next;
+	while (i != MAP_SENTINEL) {
+		if (h->entries.data[i].key == e->key) {
+			return &h->entries.data[i];
+		}
+		i = h->entries.data[i].next;
+	}
+	return nullptr;
+}
+
+template <typename K, typename V>
+isize multi_map_count(PtrMap<K, V> *h, K key) {
+	isize count = 0;
+	PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
+	while (e != nullptr) {
+		count++;
+		e = multi_map_find_next(h, e);
+	}
+	return count;
+}
+
+template <typename K, typename V>
+void multi_map_get_all(PtrMap<K, V> *h, K key, V *items) {
+	isize i = 0;
+	PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
+	while (e != nullptr) {
+		items[i++] = e->value;
+		e = multi_map_find_next(h, e);
+	}
+}
+
+template <typename K, typename V>
+void multi_map_insert(PtrMap<K, V> *h, K key, V const &value) {
+	MapFindResult fr;
+	MapIndex i;
+	if (h->hashes.count == 0) {
+		map_grow(h);
+	}
+	// Make
+	fr = map__find(h, key);
+	i = map__add_entry(h, key);
+	if (fr.entry_prev == MAP_SENTINEL) {
+		h->hashes.data[fr.hash_index] = i;
+	} else {
+		h->entries.data[fr.entry_prev].next = i;
+	}
+	h->entries.data[i].next = fr.entry_index;
+	h->entries.data[i].value = value;
+	// Grow if needed
+	if (map__full(h)) {
+		map_grow(h);
+	}
+}
+
+template <typename K, typename V>
+void multi_map_remove(PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e) {
+	MapFindResult fr = map__find_from_entry(h, e);
+	if (fr.entry_index != MAP_SENTINEL) {
+		map__erase(h, fr);
+	}
+}
+
+template <typename K, typename V>
+void multi_map_remove_all(PtrMap<K, V> *h, K key) {
+	while (map_get(h, key) != nullptr) {
+		map_remove(h, key);
+	}
+}
+#endif

+ 79 - 68
src/ptr_set.cpp

@@ -1,23 +1,12 @@
-typedef u32 PtrSetIndex;
-
-struct PtrSetFindResult {
-	PtrSetIndex hash_index;
-	PtrSetIndex entry_prev;
-	PtrSetIndex entry_index;
-};
-
-enum : PtrSetIndex { PTR_SET_SENTINEL = ~(PtrSetIndex)0 };
-
-
 template <typename T>
 struct PtrSetEntry {
-	T           ptr;
-	PtrSetIndex next;
+	T        ptr;
+	MapIndex next;
 };
 
 template <typename T>
 struct PtrSet {
-	Array<PtrSetIndex>    hashes;
+	Slice<MapIndex>    hashes;
 	Array<PtrSetEntry<T>> entries;
 };
 
@@ -30,44 +19,44 @@ template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
 template <typename T> void ptr_set_clear  (PtrSet<T> *s);
 template <typename T> void ptr_set_grow   (PtrSet<T> *s);
 template <typename T> void ptr_set_rehash (PtrSet<T> *s, isize new_count);
+template <typename T> void ptr_set_reserve(PtrSet<T> *h, isize cap);
 
 
 template <typename T>
 void ptr_set_init(PtrSet<T> *s, gbAllocator a, isize capacity) {
 	capacity = next_pow2_isize(gb_max(16, capacity));
 
-	array_init(&s->hashes,  a, capacity);
+	slice_init(&s->hashes,  a, capacity);
 	array_init(&s->entries, a, 0, capacity);
 	for (isize i = 0; i < capacity; i++) {
-		s->hashes.data[i] = PTR_SET_SENTINEL;
+		s->hashes.data[i] = MAP_SENTINEL;
 	}
 }
 
 template <typename T>
 void ptr_set_destroy(PtrSet<T> *s) {
-	array_free(&s->hashes);
+	slice_free(&s->hashes, s->entries.allocator);
 	array_free(&s->entries);
 }
 
 template <typename T>
-gb_internal PtrSetIndex ptr_set__add_entry(PtrSet<T> *s, T ptr) {
+gb_internal MapIndex ptr_set__add_entry(PtrSet<T> *s, T ptr) {
 	PtrSetEntry<T> e = {};
 	e.ptr = ptr;
-	e.next = PTR_SET_SENTINEL;
+	e.next = MAP_SENTINEL;
 	array_add(&s->entries, e);
-	return cast(PtrSetIndex)(s->entries.count-1);
+	return cast(MapIndex)(s->entries.count-1);
 }
 
 
 template <typename T>
-gb_internal PtrSetFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
-	PtrSetFindResult fr = {PTR_SET_SENTINEL, PTR_SET_SENTINEL, PTR_SET_SENTINEL};
+gb_internal MapFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
 	if (s->hashes.count != 0) {
-		u64 hash = 0xcbf29ce484222325ull ^ cast(u64)cast(uintptr)ptr;
-		u64 n = cast(u64)s->hashes.count;
-		fr.hash_index = cast(PtrSetIndex)(hash & (n-1));
+		u32 hash = ptr_map_hash_key(ptr);
+		fr.hash_index = cast(MapIndex)(hash & (s->hashes.count-1));
 		fr.entry_index = s->hashes.data[fr.hash_index];
-		while (fr.entry_index != PTR_SET_SENTINEL) {
+		while (fr.entry_index != MAP_SENTINEL) {
 			if (s->entries.data[fr.entry_index].ptr == ptr) {
 				return fr;
 			}
@@ -78,6 +67,24 @@ gb_internal PtrSetFindResult ptr_set__find(PtrSet<T> *s, T ptr) {
 	return fr;
 }
 
+template <typename T>
+gb_internal MapFindResult ptr_set__find_from_entry(PtrSet<T> *s, PtrSetEntry<T> *e) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (s->hashes.count != 0) {
+		u32 hash = ptr_map_hash_key(e->ptr);
+		fr.hash_index = cast(MapIndex)(hash & (s->hashes.count-1));
+		fr.entry_index = s->hashes.data[fr.hash_index];
+		while (fr.entry_index != MAP_SENTINEL) {
+			if (&s->entries.data[fr.entry_index] == e) {
+				return fr;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = s->entries.data[fr.entry_index].next;
+		}
+	}
+	return fr;
+}
+
 template <typename T>
 gb_internal bool ptr_set__full(PtrSet<T> *s) {
 	return 0.75f * s->hashes.count <= s->entries.count;
@@ -85,60 +92,62 @@ gb_internal bool ptr_set__full(PtrSet<T> *s) {
 
 template <typename T>
 gb_inline void ptr_set_grow(PtrSet<T> *s) {
-	isize new_count = s->hashes.count*2;
+	isize new_count = gb_max(s->hashes.count<<1, 16);
 	ptr_set_rehash(s, new_count);
 }
 
 template <typename T>
-void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
-	isize i, j;
-	PtrSet<T> ns = {};
-	ptr_set_init(&ns, s->hashes.allocator);
-	array_resize(&ns.hashes, new_count);
-	array_reserve(&ns.entries, s->entries.count);
-	for (i = 0; i < new_count; i++) {
-		ns.hashes.data[i] = PTR_SET_SENTINEL;
-	}
-	for (i = 0; i < s->entries.count; i++) {
+void ptr_set_reset_entries(PtrSet<T> *s) {
+	for (isize i = 0; i < s->hashes.count; i++) {
+		s->hashes.data[i] = MAP_SENTINEL;
+	}
+	for (isize i = 0; i < s->entries.count; i++) {
+		MapFindResult fr;
 		PtrSetEntry<T> *e = &s->entries.data[i];
-		PtrSetFindResult fr;
-		if (ns.hashes.count == 0) {
-			ptr_set_grow(&ns);
-		}
-		fr = ptr_set__find(&ns, e->ptr);
-		j = ptr_set__add_entry(&ns, e->ptr);
-		if (fr.entry_prev == PTR_SET_SENTINEL) {
-			ns.hashes.data[fr.hash_index] = cast(PtrSetIndex)j;
+		e->next = MAP_SENTINEL;
+		fr = ptr_set__find_from_entry(s, e);
+		if (fr.entry_prev == MAP_SENTINEL) {
+			s->hashes[fr.hash_index] = cast(MapIndex)i;
 		} else {
-			ns.entries.data[fr.entry_prev].next = cast(PtrSetIndex)j;
-		}
-		ns.entries.data[j].next = fr.entry_index;
-		if (ptr_set__full(&ns)) {
-			ptr_set_grow(&ns);
+			s->entries[fr.entry_prev].next = cast(MapIndex)i;
 		}
 	}
-	ptr_set_destroy(s);
-	*s = ns;
+}
+
+template <typename T>
+void ptr_set_reserve(PtrSet<T> *s, isize cap) {
+	array_reserve(&s->entries, cap);
+	if (s->entries.count*2 < s->hashes.count) {
+		return;
+	}
+	slice_resize(&s->hashes, s->entries.allocator, cap*2);
+	ptr_set_reset_entries(s);
+}
+
+
+template <typename T>
+void ptr_set_rehash(PtrSet<T> *s, isize new_count) {
+	ptr_set_reserve(s, new_count);
 }
 
 template <typename T>
 gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
 	isize index = ptr_set__find(s, ptr).entry_index;
-	return index != PTR_SET_SENTINEL;
+	return index != MAP_SENTINEL;
 }
 
 // Returns true if it already exists
 template <typename T>
 T ptr_set_add(PtrSet<T> *s, T ptr) {
-	PtrSetIndex index;
-	PtrSetFindResult fr;
+	MapIndex index;
+	MapFindResult fr;
 	if (s->hashes.count == 0) {
 		ptr_set_grow(s);
 	}
 	fr = ptr_set__find(s, ptr);
-	if (fr.entry_index == PTR_SET_SENTINEL) {
+	if (fr.entry_index == MAP_SENTINEL) {
 		index = ptr_set__add_entry(s, ptr);
-		if (fr.entry_prev != PTR_SET_SENTINEL) {
+		if (fr.entry_prev != MAP_SENTINEL) {
 			s->entries.data[fr.entry_prev].next = index;
 		} else {
 			s->hashes.data[fr.hash_index] = index;
@@ -153,17 +162,17 @@ T ptr_set_add(PtrSet<T> *s, T ptr) {
 template <typename T>
 bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it previously existsed
 	bool exists = false;
-	PtrSetIndex index;
-	PtrSetFindResult fr;
+	MapIndex index;
+	MapFindResult fr;
 	if (s->hashes.count == 0) {
 		ptr_set_grow(s);
 	}
 	fr = ptr_set__find(s, ptr);
-	if (fr.entry_index != PTR_SET_SENTINEL) {
+	if (fr.entry_index != MAP_SENTINEL) {
 		exists = true;
 	} else {
 		index = ptr_set__add_entry(s, ptr);
-		if (fr.entry_prev != PTR_SET_SENTINEL) {
+		if (fr.entry_prev != MAP_SENTINEL) {
 			s->entries.data[fr.entry_prev].next = index;
 		} else {
 			s->hashes.data[fr.hash_index] = index;
@@ -178,9 +187,9 @@ bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it previously exis
 
 
 template <typename T>
-void ptr_set__erase(PtrSet<T> *s, PtrSetFindResult fr) {
-	PtrSetFindResult last;
-	if (fr.entry_prev == PTR_SET_SENTINEL) {
+void ptr_set__erase(PtrSet<T> *s, MapFindResult fr) {
+	MapFindResult last;
+	if (fr.entry_prev == MAP_SENTINEL) {
 		s->hashes.data[fr.hash_index] = s->entries.data[fr.entry_index].next;
 	} else {
 		s->entries.data[fr.entry_prev].next = s->entries.data[fr.entry_index].next;
@@ -191,7 +200,7 @@ void ptr_set__erase(PtrSet<T> *s, PtrSetFindResult fr) {
 	}
 	s->entries.data[fr.entry_index] = s->entries.data[s->entries.count-1];
 	last = ptr_set__find(s, s->entries.data[fr.entry_index].ptr);
-	if (last.entry_prev != PTR_SET_SENTINEL) {
+	if (last.entry_prev != MAP_SENTINEL) {
 		s->entries.data[last.entry_prev].next = fr.entry_index;
 	} else {
 		s->hashes.data[last.hash_index] = fr.entry_index;
@@ -200,14 +209,16 @@ void ptr_set__erase(PtrSet<T> *s, PtrSetFindResult fr) {
 
 template <typename T>
 void ptr_set_remove(PtrSet<T> *s, T ptr) {
-	PtrSetFindResult fr = ptr_set__find(s, ptr);
-	if (fr.entry_index != PTR_SET_SENTINEL) {
+	MapFindResult fr = ptr_set__find(s, ptr);
+	if (fr.entry_index != MAP_SENTINEL) {
 		ptr_set__erase(s, fr);
 	}
 }
 
 template <typename T>
 gb_inline void ptr_set_clear(PtrSet<T> *s) {
-	array_clear(&s->hashes);
 	array_clear(&s->entries);
+	for (isize i = 0; i < s->hashes.count; i++) {
+		s->hashes.data[i] = MAP_SENTINEL;
+	}
 }

+ 57 - 67
src/string_map.cpp

@@ -1,22 +1,11 @@
-// NOTE(bill): This util stuff is the same for every `Map`
-struct StringMapFindResult {
-	isize hash_index;
-	isize entry_prev;
-	isize entry_index;
-};
-
 struct StringHashKey {
-	u64    hash;
+	u32    hash;
 	String string;
 };
 
-u64 string_hashing_proc(void const *data, isize len) {
-	return fnv64a(data, len);
-}
-
 gb_inline StringHashKey string_hash_string(String const &s) {
 	StringHashKey hash_key = {};
-	hash_key.hash = string_hashing_proc(s.text, s.len);
+	hash_key.hash = fnv32a(s.text, s.len);
 	hash_key.string = s;
 	return hash_key;
 }
@@ -35,13 +24,13 @@ bool operator!=(StringHashKey const &a, StringHashKey const &b) { return !string
 template <typename T>
 struct StringMapEntry {
 	StringHashKey key;
-	isize         next;
+	MapIndex      next;
 	T             value;
 };
 
 template <typename T>
 struct StringMap {
-	Slice<isize>              hashes;
+	Slice<MapIndex>           hashes;
 	Array<StringMapEntry<T> > entries;
 };
 
@@ -65,6 +54,7 @@ template <typename T> void string_map_remove           (StringMap<T> *h, StringH
 template <typename T> void string_map_clear            (StringMap<T> *h);
 template <typename T> void string_map_grow             (StringMap<T> *h);
 template <typename T> void string_map_rehash           (StringMap<T> *h, isize new_count);
+template <typename T> void string_map_reserve          (StringMap<T> *h, isize cap);
 
 template <typename T>
 gb_inline void string_map_init(StringMap<T> *h, gbAllocator a, isize capacity) {
@@ -72,7 +62,7 @@ gb_inline void string_map_init(StringMap<T> *h, gbAllocator a, isize capacity) {
 	slice_init(&h->hashes,  a, capacity);
 	array_init(&h->entries, a, 0, capacity);
 	for (isize i = 0; i < capacity; i++) {
-		h->hashes.data[i] = -1;
+		h->hashes.data[i] = MAP_SENTINEL;
 	}
 }
 
@@ -83,21 +73,21 @@ gb_inline void string_map_destroy(StringMap<T> *h) {
 }
 
 template <typename T>
-gb_internal isize string_map__add_entry(StringMap<T> *h, StringHashKey const &key) {
+gb_internal MapIndex string_map__add_entry(StringMap<T> *h, StringHashKey const &key) {
 	StringMapEntry<T> e = {};
 	e.key = key;
-	e.next = -1;
+	e.next = MAP_SENTINEL;
 	array_add(&h->entries, e);
-	return h->entries.count-1;
+	return cast(MapIndex)(h->entries.count-1);
 }
 
 template <typename T>
-gb_internal StringMapFindResult string_map__find(StringMap<T> *h, StringHashKey const &key) {
-	StringMapFindResult fr = {-1, -1, -1};
+gb_internal MapFindResult string_map__find(StringMap<T> *h, StringHashKey const &key) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
 	if (h->hashes.count != 0) {
-		fr.hash_index = key.hash & (h->hashes.count-1);
+		fr.hash_index = cast(MapIndex)(key.hash & (h->hashes.count-1));
 		fr.entry_index = h->hashes.data[fr.hash_index];
-		while (fr.entry_index >= 0) {
+		while (fr.entry_index != MAP_SENTINEL) {
 			if (string_hash_key_equal(h->entries.data[fr.entry_index].key, key)) {
 				return fr;
 			}
@@ -109,12 +99,12 @@ gb_internal StringMapFindResult string_map__find(StringMap<T> *h, StringHashKey
 }
 
 template <typename T>
-gb_internal StringMapFindResult string_map__find_from_entry(StringMap<T> *h, StringMapEntry<T> *e) {
-	StringMapFindResult fr = {-1, -1, -1};
+gb_internal MapFindResult string_map__find_from_entry(StringMap<T> *h, StringMapEntry<T> *e) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
 	if (h->hashes.count != 0) {
-		fr.hash_index  = e->key.hash & (h->hashes.count-1);
+		fr.hash_index  = cast(MapIndex)(e->key.hash & (h->hashes.count-1));
 		fr.entry_index = h->hashes.data[fr.hash_index];
-		while (fr.entry_index >= 0) {
+		while (fr.entry_index != MAP_SENTINEL) {
 			if (&h->entries.data[fr.entry_index] == e) {
 				return fr;
 			}
@@ -136,45 +126,45 @@ gb_inline void string_map_grow(StringMap<T> *h) {
 	string_map_rehash(h, new_count);
 }
 
+
 template <typename T>
-void string_map_rehash(StringMap<T> *h, isize new_count) {
-	isize i, j;
-	StringMap<T> nh = {};
-	new_count = next_pow2_isize(new_count);
-	nh.hashes = h->hashes;
-	nh.entries.allocator = h->entries.allocator;
-	slice_resize(&nh.hashes, h->entries.allocator, new_count);
-	for (i = 0; i < new_count; i++) {
-		nh.hashes.data[i] = -1;
+void string_map_reset_entries(StringMap<T> *h) {
+	for (isize i = 0; i < h->hashes.count; i++) {
+		h->hashes.data[i] = MAP_SENTINEL;
 	}
-	array_reserve(&nh.entries, ARRAY_GROW_FORMULA(h->entries.count));
-	for (i = 0; i < h->entries.count; i++) {
+	for (isize i = 0; i < h->entries.count; i++) {
+		MapFindResult fr;
 		StringMapEntry<T> *e = &h->entries.data[i];
-		StringMapFindResult fr;
-		if (nh.hashes.count == 0) {
-			string_map_grow(&nh);
-		}
-		fr = string_map__find(&nh, e->key);
-		j = string_map__add_entry(&nh, e->key);
-		if (fr.entry_prev < 0) {
-			nh.hashes.data[fr.hash_index] = j;
+		e->next = MAP_SENTINEL;
+		fr = string_map__find_from_entry(h, e);
+		if (fr.entry_prev == MAP_SENTINEL) {
+			h->hashes[fr.hash_index] = cast(MapIndex)i;
 		} else {
-			nh.entries.data[fr.entry_prev].next = j;
-		}
-		nh.entries.data[j].next = fr.entry_index;
-		nh.entries.data[j].value = e->value;
-		if (string_map__full(&nh)) {
-			string_map_grow(&nh);
+			h->entries[fr.entry_prev].next = cast(MapIndex)i;
 		}
 	}
-	array_free(&h->entries);
-	*h = nh;
+}
+
+template <typename T>
+void string_map_reserve(StringMap<T> *h, isize cap) {
+	array_reserve(&h->entries, cap);
+	if (h->entries.count*2 < h->hashes.count) {
+		return;
+	}
+	slice_resize(&h->hashes, h->entries.allocator, cap*2);
+	string_map_reset_entries(h);
+}
+
+
+template <typename T>
+void string_map_rehash(StringMap<T> *h, isize new_count) {
+	string_map_reserve(h, new_count);
 }
 
 template <typename T>
 T *string_map_get(StringMap<T> *h, StringHashKey const &key) {
 	isize index = string_map__find(h, key).entry_index;
-	if (index >= 0) {
+	if (index != MAP_SENTINEL) {
 		return &h->entries.data[index].value;
 	}
 	return nullptr;
@@ -193,7 +183,7 @@ gb_inline T *string_map_get(StringMap<T> *h, char const *key) {
 template <typename T>
 T &string_map_must_get(StringMap<T> *h, StringHashKey const &key) {
 	isize index = string_map__find(h, key).entry_index;
-	GB_ASSERT(index >= 0);
+	GB_ASSERT(index != MAP_SENTINEL);
 	return h->entries.data[index].value;
 }
 
@@ -209,17 +199,17 @@ gb_inline T &string_map_must_get(StringMap<T> *h, char const *key) {
 
 template <typename T>
 void string_map_set(StringMap<T> *h, StringHashKey const &key, T const &value) {
-	isize index;
-	StringMapFindResult fr;
+	MapIndex index;
+	MapFindResult fr;
 	if (h->hashes.count == 0) {
 		string_map_grow(h);
 	}
 	fr = string_map__find(h, key);
-	if (fr.entry_index >= 0) {
+	if (fr.entry_index != MAP_SENTINEL) {
 		index = fr.entry_index;
 	} else {
 		index = string_map__add_entry(h, key);
-		if (fr.entry_prev >= 0) {
+		if (fr.entry_prev != MAP_SENTINEL) {
 			h->entries.data[fr.entry_prev].next = index;
 		} else {
 			h->hashes.data[fr.hash_index] = index;
@@ -244,9 +234,9 @@ gb_inline void string_map_set(StringMap<T> *h, char const *key, T const &value)
 
 
 template <typename T>
-void string_map__erase(StringMap<T> *h, StringMapFindResult const &fr) {
-	StringMapFindResult last;
-	if (fr.entry_prev < 0) {
+void string_map__erase(StringMap<T> *h, MapFindResult const &fr) {
+	MapFindResult last;
+	if (fr.entry_prev == MAP_SENTINEL) {
 		h->hashes.data[fr.hash_index] = h->entries.data[fr.entry_index].next;
 	} else {
 		h->entries.data[fr.entry_prev].next = h->entries.data[fr.entry_index].next;
@@ -257,7 +247,7 @@ void string_map__erase(StringMap<T> *h, StringMapFindResult const &fr) {
 	}
 	h->entries.data[fr.entry_index] = h->entries.data[h->entries.count-1];
 	last = string_map__find(h, h->entries.data[fr.entry_index].key);
-	if (last.entry_prev >= 0) {
+	if (last.entry_prev != MAP_SENTINEL) {
 		h->entries.data[last.entry_prev].next = fr.entry_index;
 	} else {
 		h->hashes.data[last.hash_index] = fr.entry_index;
@@ -266,8 +256,8 @@ void string_map__erase(StringMap<T> *h, StringMapFindResult const &fr) {
 
 template <typename T>
 void string_map_remove(StringMap<T> *h, StringHashKey const &key) {
-	StringMapFindResult fr = string_map__find(h, key);
-	if (fr.entry_index >= 0) {
+	MapFindResult fr = string_map__find(h, key);
+	if (fr.entry_index != MAP_SENTINEL) {
 		string_map__erase(h, fr);
 	}
 }
@@ -276,7 +266,7 @@ template <typename T>
 gb_inline void string_map_clear(StringMap<T> *h) {
 	array_clear(&h->entries);
 	for (isize i = 0; i < h->hashes.count; i++) {
-		h->hashes.data[i] = -1;
+		h->hashes.data[i] = MAP_SENTINEL;
 	}
 }
 

+ 76 - 61
src/string_set.cpp

@@ -1,17 +1,11 @@
-struct StringSetFindResult {
-	isize hash_index;
-	isize entry_prev;
-	isize entry_index;
-};
-
 struct StringSetEntry {
-	u64    hash;
-	isize  next;
-	String value;
+	u32      hash;
+	MapIndex next;
+	String   value;
 };
 
 struct StringSet {
-	Array<isize>          hashes;
+	Slice<MapIndex>       hashes;
 	Array<StringSetEntry> entries;
 };
 
@@ -27,31 +21,35 @@ void string_set_rehash (StringSet *s, isize new_count);
 
 
 gb_inline void string_set_init(StringSet *s, gbAllocator a, isize capacity) {
-	array_init(&s->hashes,  a);
-	array_init(&s->entries, a);
+	capacity = next_pow2_isize(gb_max(16, capacity));
+	
+	slice_init(&s->hashes,  a, capacity);
+	array_init(&s->entries, a, 0, capacity);
+	for (isize i = 0; i < capacity; i++) {
+		s->hashes.data[i] = MAP_SENTINEL;
+	}
 }
 
 gb_inline void string_set_destroy(StringSet *s) {
+	slice_free(&s->hashes, s->entries.allocator);
 	array_free(&s->entries);
-	array_free(&s->hashes);
 }
 
-gb_internal isize string_set__add_entry(StringSet *s, StringHashKey const &key) {
+gb_internal MapIndex string_set__add_entry(StringSet *s, StringHashKey const &key) {
 	StringSetEntry e = {};
 	e.hash = key.hash;
-	e.next = -1;
+	e.next = MAP_SENTINEL;
 	e.value = key.string;
 	array_add(&s->entries, e);
-	return s->entries.count-1;
+	return cast(MapIndex)(s->entries.count-1);
 }
 
-gb_internal StringSetFindResult string_set__find(StringSet *s, StringHashKey const &key) {
-	StringSetFindResult fr = {-1, -1, -1};
+gb_internal MapFindResult string_set__find(StringSet *s, StringHashKey const &key) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
 	if (s->hashes.count > 0) {
-		// fr.hash_index  = u128_to_i64(key.key % u128_from_i64(s->hashes.count));
-		fr.hash_index = key.hash % s->hashes.count;
+		fr.hash_index = cast(MapIndex)(((u64)key.hash) % s->hashes.count);
 		fr.entry_index = s->hashes[fr.hash_index];
-		while (fr.entry_index >= 0) {
+		while (fr.entry_index != MAP_SENTINEL) {
 			auto const &entry = s->entries[fr.entry_index];
 			if (entry.hash == key.hash && entry.value == key.string) {
 				return fr;
@@ -62,68 +60,83 @@ gb_internal StringSetFindResult string_set__find(StringSet *s, StringHashKey con
 	}
 	return fr;
 }
+gb_internal MapFindResult string_set__find_from_entry(StringSet *s, StringSetEntry *e) {
+	MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+	if (s->hashes.count > 0) {
+		fr.hash_index = cast(MapIndex)(e->hash % s->hashes.count);
+		fr.entry_index = s->hashes[fr.hash_index];
+		while (fr.entry_index != MAP_SENTINEL) {
+			if (&s->entries[fr.entry_index] == e) {
+				return fr;
+			}
+			fr.entry_prev = fr.entry_index;
+			fr.entry_index = s->entries[fr.entry_index].next;
+		}
+	}
+	return fr;
+}
+
 
 gb_internal b32 string_set__full(StringSet *s) {
 	return 0.75f * s->hashes.count <= s->entries.count;
 }
 
 gb_inline void string_set_grow(StringSet *s) {
-	isize new_count = ARRAY_GROW_FORMULA(s->entries.count);
+	isize new_count = gb_max(s->hashes.count<<1, 16);
 	string_set_rehash(s, new_count);
 }
 
-void string_set_rehash(StringSet *s, isize new_count) {
-	isize i, j;
-	StringSet ns = {};
-	string_set_init(&ns, s->hashes.allocator);
-	array_resize(&ns.hashes, new_count);
-	array_reserve(&ns.entries, s->entries.count);
-	for (i = 0; i < new_count; i++) {
-		ns.hashes[i] = -1;
+
+void string_set_reset_entries(StringSet *s) {
+	for (isize i = 0; i < s->hashes.count; i++) {
+		s->hashes.data[i] = MAP_SENTINEL;
 	}
-	for (i = 0; i < s->entries.count; i++) {
-		StringSetEntry *e = &s->entries[i];
-		StringSetFindResult fr;
-		if (ns.hashes.count == 0) {
-			string_set_grow(&ns);
-		}
-		StringHashKey key = {e->hash, e->value};
-		fr = string_set__find(&ns, key);
-		j = string_set__add_entry(&ns, key);
-		if (fr.entry_prev < 0) {
-			ns.hashes[fr.hash_index] = j;
+	for (isize i = 0; i < s->entries.count; i++) {
+		MapFindResult fr;
+		StringSetEntry *e = &s->entries.data[i];
+		e->next = MAP_SENTINEL;
+		fr = string_set__find_from_entry(s, e);
+		if (fr.entry_prev == MAP_SENTINEL) {
+			s->hashes[fr.hash_index] = cast(MapIndex)i;
 		} else {
-			ns.entries[fr.entry_prev].next = j;
-		}
-		ns.entries[j].next = fr.entry_index;
-		ns.entries[j].value = e->value;
-		if (string_set__full(&ns)) {
-			string_set_grow(&ns);
+			s->entries[fr.entry_prev].next = cast(MapIndex)i;
 		}
 	}
-	string_set_destroy(s);
-	*s = ns;
+}
+
+void string_set_reserve(StringSet *s, isize cap) {
+	array_reserve(&s->entries, cap);
+	if (s->entries.count*2 < s->hashes.count) {
+		return;
+	}
+	slice_resize(&s->hashes, s->entries.allocator, cap*2);
+	string_set_reset_entries(s);
+}
+
+
+void string_set_rehash(StringSet *s, isize new_count) {
+	string_set_reserve(s, new_count);
 }
 
 gb_inline bool string_set_exists(StringSet *s, String const &str) {
 	StringHashKey key = string_hash_string(str);
 	isize index = string_set__find(s, key).entry_index;
-	return index >= 0;
+	return index != MAP_SENTINEL;
 }
 
 void string_set_add(StringSet *s, String const &str) {
-	isize index;
-	StringSetFindResult fr;
+	MapIndex index;
+	MapFindResult fr;
 	StringHashKey key = string_hash_string(str);
 	if (s->hashes.count == 0) {
 		string_set_grow(s);
 	}
 	fr = string_set__find(s, key);
-	if (fr.entry_index >= 0) {
+	if (fr.entry_index != MAP_SENTINEL) {
 		index = fr.entry_index;
 	} else {
 		index = string_set__add_entry(s, key);
-		if (fr.entry_prev >= 0) {
+		if (fr.entry_prev != MAP_SENTINEL) {
 			s->entries[fr.entry_prev].next = index;
 		} else {
 			s->hashes[fr.hash_index] = index;
@@ -137,9 +150,9 @@ void string_set_add(StringSet *s, String const &str) {
 }
 
 
-void string_set__erase(StringSet *s, StringSetFindResult fr) {
-	StringSetFindResult last;
-	if (fr.entry_prev < 0) {
+void string_set__erase(StringSet *s, MapFindResult fr) {
+	MapFindResult last;
+	if (fr.entry_prev == MAP_SENTINEL) {
 		s->hashes[fr.hash_index] = s->entries[fr.entry_index].next;
 	} else {
 		s->entries[fr.entry_prev].next = s->entries[fr.entry_index].next;
@@ -152,7 +165,7 @@ void string_set__erase(StringSet *s, StringSetFindResult fr) {
 	*entry = s->entries[s->entries.count-1];
 	StringHashKey key = {entry->hash, entry->value};
 	last = string_set__find(s, key);
-	if (last.entry_prev >= 0) {
+	if (last.entry_prev != MAP_SENTINEL) {
 		s->entries[last.entry_prev].next = fr.entry_index;
 	} else {
 		s->hashes[last.hash_index] = fr.entry_index;
@@ -161,13 +174,15 @@ void string_set__erase(StringSet *s, StringSetFindResult fr) {
 
 void string_set_remove(StringSet *s, String const &str) {
 	StringHashKey key = string_hash_string(str);
-	StringSetFindResult fr = string_set__find(s, key);
-	if (fr.entry_index >= 0) {
+	MapFindResult fr = string_set__find(s, key);
+	if (fr.entry_index != MAP_SENTINEL) {
 		string_set__erase(s, fr);
 	}
 }
 
 gb_inline void string_set_clear(StringSet *s) {
-	array_clear(&s->hashes);
 	array_clear(&s->entries);
+	for_array(i, s->hashes) {
+		s->hashes.data[i] = MAP_SENTINEL;
+	}
 }