Explorar el Código

Merge branch 'master' into windows-llvm-11.1.0

gingerBill hace 3 años
padre
commit
cf4aeecc3c

+ 14 - 0
core/math/big/api.odin

@@ -32,6 +32,9 @@ add :: proc {
 		int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error)
 	*/
 	int_add_digit,
+	rat_add_rat,
+	rat_add_int,
+	int_add_rat,
 }
 
 /*
@@ -46,6 +49,9 @@ sub :: proc {
 		int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error)
 	*/
 	int_sub_digit,
+	rat_sub_rat,
+	rat_sub_int,
+	int_sub_rat,
 }
 
 /*
@@ -67,6 +73,10 @@ is_zero :: proc {
 		int_is_zero :: proc(a: ^Int) -> bool
 	*/
 	int_is_zero,
+	/*
+		rat_is_zero :: proc(a: ^Rat) -> bool
+	*/
+	rat_is_zero,
 }
 
 is_positive :: proc {
@@ -74,6 +84,7 @@ is_positive :: proc {
 		int_is_positive :: proc(a: ^Int) -> bool
 	*/
 	int_is_positive,
+	rat_is_positive,
 }
 is_pos :: is_positive
 
@@ -82,6 +93,7 @@ is_negative :: proc {
 		int_is_negative :: proc(a: ^Int) -> bool
 	*/
 	int_is_negative,
+	rat_is_negative,
 }
 is_neg :: is_negative
 
@@ -90,6 +102,7 @@ is_even :: proc {
 		int_is_even :: proc(a: ^Int) -> bool
 	*/
 	int_is_even,
+	rat_is_even,
 }
 
 is_odd :: proc {
@@ -97,6 +110,7 @@ is_odd :: proc {
 		int_is_odd :: proc(a: ^Int) -> bool
 	*/
 	int_is_odd,
+	rat_is_odd,
 }
 
 is_power_of_two :: proc {

+ 30 - 6
core/math/big/helpers.odin

@@ -44,7 +44,20 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator :
 	return #force_inline internal_int_set_from_integer(dest, src, minimize)
 }
 
-set :: proc { int_set_from_integer, int_copy, int_atoi, }
+set :: proc { 
+	int_set_from_integer, 
+	int_copy, 
+	int_atoi, 
+
+	rat_set_f64, 
+	rat_set_f32, 
+	rat_set_f16, 
+	rat_set_u64, 
+	rat_set_i64,
+	rat_set_int, 
+	rat_set_digit, 
+	rat_set_rat, 
+}
 
 /*
 	Copy one `Int` to another.
@@ -66,7 +79,10 @@ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.alloca
 
 	return #force_inline internal_int_copy(dest, src, minimize)
 }
-copy :: proc { int_copy, }
+copy :: proc { 
+	int_copy, 
+	rat_copy,
+}
 
 /*
 	In normal code, you can also write `a, b = b, a`.
@@ -77,7 +93,7 @@ int_swap :: proc(a, b: ^Int) {
 	assert_if_nil(a, b)
 	#force_inline internal_swap(a, b)
 }
-swap :: proc { int_swap, }
+swap :: proc { int_swap, rat_swap }
 
 /*
 	Set `dest` to |`src`|.
@@ -98,7 +114,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error)
 platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) {
 	return n if n >= 0 else -n
 }
-abs :: proc{ int_abs, platform_abs, }
+abs :: proc{ int_abs, platform_abs, rat_abs }
 
 /*
 	Set `dest` to `-src`.
@@ -115,7 +131,7 @@ int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error)
 
 	return #force_inline internal_int_neg(dest, src)
 }
-neg :: proc { int_neg, }
+neg :: proc { int_neg, rat_neg }
 
 /*
 	Helpers to extract values from the `Int`.
@@ -788,7 +804,10 @@ destroy_constants :: proc() {
 }
 
 
-assert_if_nil :: proc{assert_if_nil_int}
+assert_if_nil :: proc{
+	assert_if_nil_int,
+	assert_if_nil_rat,
+}
 
 assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_location) {
 	for i in integers {
@@ -796,3 +815,8 @@ assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_locatio
 	}
 }
 
+assert_if_nil_rat :: #force_inline proc(rationals: ..^Rat, loc := #caller_location) {
+	for r in rationals {
+		assert(r != nil, "(nil)", loc)
+	}
+}

+ 12 - 5
core/math/big/internal.odin

@@ -1042,7 +1042,10 @@ internal_is_initialized :: proc { internal_int_is_initialized, }
 internal_int_is_zero :: #force_inline proc(a: ^Int) -> (zero: bool) {
 	return a.used == 0
 }
-internal_is_zero :: proc { internal_int_is_zero }
+internal_is_zero :: proc { 
+	internal_rat_is_zero,
+	internal_int_is_zero,
+}
 
 /*
 	This procedure will return `true` if the `Int` is positive, `false` if not.
@@ -1865,7 +1868,10 @@ internal_int_destroy :: proc(integers: ..^Int) {
 		a = &Int{}
 	}
 }
-internal_destroy :: proc{ internal_int_destroy, }
+internal_destroy :: proc{ 
+	internal_int_destroy, 
+	internal_rat_destroy, 
+}
 
 /*
 	Helpers to set an `Int` to a specific value.
@@ -1950,13 +1956,14 @@ internal_copy :: proc { internal_int_copy, }
 	This helper swaps completely.
 */
 internal_int_swap :: #force_inline proc(a, b: ^Int) {
-	a := a; b := b
-
 	a.used,  b.used  = b.used,  a.used
 	a.sign,  b.sign  = b.sign,  a.sign
 	a.digit, b.digit = b.digit, a.digit
 }
-internal_swap :: proc { internal_int_swap, }
+internal_swap :: proc { 
+	internal_int_swap, 
+	internal_rat_swap,
+}
 
 /*
 	Set `dest` to |`src`|.

+ 18 - 3
core/math/big/public.odin

@@ -152,9 +152,18 @@ int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) ->
 	return #force_inline internal_int_mul(dest, src, multiplier)
 }
 
-mul :: proc { int_mul, int_mul_digit, }
+mul :: proc { 
+	int_mul, 
+	int_mul_digit, 
+	rat_mul_rat,
+	rat_mul_int,
+	int_mul_rat,
+}
+
+int_sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src) }
+rat_sqr :: proc(dest, src: ^Rat) -> (err: Error) { return mul(dest, src, src) }
+sqr :: proc { int_sqr, rat_sqr }
 
-sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src) }
 
 /*
 	divmod.
@@ -200,7 +209,13 @@ int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator :
 	_ = #force_inline internal_divmod(quotient, numerator, denominator) or_return
 	return
 }
-div :: proc { int_div, int_div_digit, }
+div :: proc { 
+	int_div, 
+	int_div_digit, 
+	rat_div_rat,
+	rat_div_int,
+	int_div_rat,
+}
 
 /*
 	remainder = numerator % denominator.

+ 540 - 0
core/math/big/rat.odin

@@ -0,0 +1,540 @@
+package math_big
+
+import "core:builtin"
+import "core:intrinsics"
+import "core:math"
+
+Rat :: struct {
+	a, b: Int,
+}
+
+rat_set_f64 :: proc(dst: ^Rat, f: f64, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst)
+	context.allocator = allocator
+	
+	EXP_MASK :: 1<<11 - 1
+	
+	bits := transmute(u64)f
+	mantissa := bits & (1<<52 - 1)
+	exp := int((bits>>52) & EXP_MASK)
+	
+	int_set_from_integer(&dst.b, 1) or_return
+	
+	switch exp {
+	case EXP_MASK:
+		dst.a.flags += {.Inf}
+		return
+	case 0:
+		exp -= 1022
+	case:
+		mantissa |= 1<<52
+		exp -= 1023
+	}
+	
+	shift := 52 - exp
+	
+	for mantissa&1 == 0 && shift > 0 {
+		mantissa >>= 1
+		shift -= 1
+	}
+	
+	int_set_from_integer(&dst.a, mantissa) or_return
+	dst.a.sign = .Negative if f < 0 else .Zero_or_Positive
+	
+	if shift > 0 {
+		internal_int_shl_digit(&dst.b, shift) or_return
+	} else {
+		internal_int_shl_digit(&dst.a, -shift) or_return
+	}
+	
+	return internal_rat_norm(dst)
+}
+
+rat_set_f32 :: proc(dst: ^Rat, f: f32, allocator := context.allocator) -> (err: Error) {
+	return rat_set_f64(dst, f64(f), allocator)
+}
+rat_set_f16 :: proc(dst: ^Rat, f: f16, allocator := context.allocator) -> (err: Error) {
+	return rat_set_f64(dst, f64(f), allocator)
+}
+
+
+rat_set_frac :: proc{rat_set_frac_digit, rat_set_frac_int}
+
+rat_set_frac_digit :: proc(dst: ^Rat, a, b: DIGIT, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst)
+	if b == 0 {
+		return .Division_by_Zero
+	}
+	context.allocator = allocator
+	internal_set(&dst.a, a) or_return
+	internal_set(&dst.b, b) or_return
+	return internal_rat_norm(dst)
+}
+
+rat_set_frac_int :: proc(dst: ^Rat, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst)
+	assert_if_nil(a, b)
+	if internal_is_zero(b) {
+		return .Division_by_Zero
+	}
+	context.allocator = allocator
+	internal_set(&dst.a, a) or_return
+	internal_set(&dst.b, b) or_return
+	return internal_rat_norm(dst)
+}
+
+rat_set_int :: proc(dst: ^Rat, a: ^Int, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst)
+	assert_if_nil(a)
+	context.allocator = allocator
+	internal_set(&dst.a, a) or_return
+	internal_set(&dst.b, 1) or_return
+	return
+}
+
+rat_set_digit :: proc(dst: ^Rat, a: DIGIT, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst)
+	context.allocator = allocator
+	internal_set(&dst.a, a) or_return
+	internal_set(&dst.b, 1) or_return
+	return
+}
+
+rat_set_rat :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x)
+	context.allocator = allocator
+	internal_set(&dst.a, &x.a) or_return
+	internal_set(&dst.b, &x.b) or_return
+	return
+}
+
+rat_set_u64 :: proc(dst: ^Rat, x: u64, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst)
+	context.allocator = allocator
+	internal_set(&dst.a, x) or_return
+	internal_set(&dst.a, 1) or_return
+	return
+}
+rat_set_i64 :: proc(dst: ^Rat, x: i64, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst)
+	context.allocator = allocator
+	internal_set(&dst.a, x) or_return
+	internal_set(&dst.a, 1) or_return
+	return
+}
+
+rat_copy :: proc(dst, src: ^Rat, minimize := false, allocator := context.allocator) -> (err: Error) {
+	if (dst == src) { return nil }
+	
+	assert_if_nil(dst, src)
+	context.allocator = allocator
+	int_copy(&dst.a, &src.a, minimize, allocator) or_return
+	int_copy(&dst.b, &src.b, minimize, allocator) or_return
+	internal_rat_norm(dst) or_return
+	return nil
+}
+
+internal_rat_destroy :: proc(rationals: ..^Rat) {
+	rationals := rationals
+
+	for z in &rationals {
+		internal_int_destroy(&z.a, &z.b)
+	}
+}
+
+internal_rat_norm :: proc(z: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(z)
+	context.allocator = allocator
+	switch {
+	case internal_is_zero(&z.a):
+		z.a.sign = .Zero_or_Positive
+		fallthrough
+	case internal_is_zero(&z.b):
+		int_set_from_integer(&z.b, 1) or_return
+	case:
+		sign := z.a.sign
+		z.a.sign = .Zero_or_Positive
+		z.b.sign = .Zero_or_Positive
+		
+		f := &Int{}
+		internal_int_gcd(f, &z.a, &z.b) or_return
+		if !internal_int_equals_digit(f, 1) {
+			f.sign = .Zero_or_Positive
+			internal_int_div(&z.a, &z.a, f) or_return
+			internal_int_div(&z.b, &z.b, f) or_return
+		}
+		z.a.sign = sign	
+	}
+	return
+}
+
+rat_swap :: proc(a, b: ^Rat) {
+	assert_if_nil(a, b)
+	#force_inline internal_swap(a, b)
+}
+
+internal_rat_swap :: #force_inline proc(a, b: ^Rat) {
+	internal_int_swap(&a.a, &b.a)
+	internal_int_swap(&a.b, &b.b)
+}
+
+rat_sign :: proc(z: ^Rat) -> Sign {
+	if z == nil {
+		return .Zero_or_Positive
+	}
+	return z.a.sign
+}
+
+rat_is_int :: proc(z: ^Rat) -> bool {
+	assert_if_nil(z)
+	return internal_is_zero(&z.a) || internal_int_equals_digit(&z.b, 1)
+}
+
+rat_is_zero :: proc(z: ^Rat) -> bool {
+	return internal_rat_is_zero(z)
+}
+internal_rat_is_zero :: #force_inline proc(z: ^Rat) -> bool {
+	assert_if_nil(z)
+	return internal_is_zero(&z.a)
+}
+
+internal_int_mul_denom :: proc(dst, x, y: ^Int, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x, y)
+	context.allocator = allocator
+	switch {
+	case internal_is_zero(x) && internal_is_zero(y):
+		return internal_set(dst, 1) 
+	case internal_is_zero(x):
+		return internal_set(dst, y)
+	case internal_is_zero(y):
+		return internal_set(dst, x)
+	}
+	return int_mul(dst, x, y)
+}
+
+internal_int_scale_denom :: proc(dst, x, y: ^Int, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x, y)
+	if internal_is_zero(y) {
+		return internal_set(dst, x)
+	}
+	int_mul(dst, x, y) or_return
+	dst.sign = x.sign
+	return
+}
+
+
+rat_add_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x, y)
+	context.allocator = allocator
+	
+	a1, a2: Int
+	defer internal_destroy(&a1, &a2)
+	
+	internal_int_scale_denom(&a1, &x.a, &y.b)  or_return
+	internal_int_scale_denom(&a2, &y.a, &x.b)  or_return
+	int_add(&dst.a, &a1, &a2)                  or_return
+	internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
+	return internal_rat_norm(dst)
+}
+
+rat_sub_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x, y)
+	context.allocator = allocator
+	
+	a1, a2 := &Int{}, &Int{}
+	defer internal_destroy(a1, a2)
+	
+	internal_int_scale_denom(a1, &x.a, &y.b)   or_return
+	internal_int_scale_denom(a2, &y.a, &x.b)   or_return
+	int_sub(&dst.a, a1, a2)                    or_return
+	internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
+	return internal_rat_norm(dst)
+}
+
+rat_mul_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x, y)
+	context.allocator = allocator
+	
+	if x == y {
+		internal_sqr(&dst.a, &x.a)         or_return
+		if internal_is_zero(&x.b) {
+			internal_set(&dst.b, 1)    or_return
+		} else {
+			internal_sqr(&dst.a, &x.b) or_return
+		}
+		return
+	}
+	
+	int_sub(&dst.a, &x.a, &y.a)                or_return
+	internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
+	return internal_rat_norm(dst)
+}
+
+rat_div_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	if internal_rat_is_zero(y) {
+		return .Division_by_Zero
+	}
+	context.allocator = allocator
+	
+	a, b := &Int{}, &Int{}
+	defer internal_destroy(a, b)
+	
+	internal_int_scale_denom(a, &x.a, &y.b) or_return
+	internal_int_scale_denom(b, &y.a, &x.b) or_return
+	internal_set(&dst.a, a) or_return
+	internal_set(&dst.b, b) or_return
+	internal_int_abs(&dst.a, &dst.a) 
+	internal_int_abs(&dst.b, &dst.b) 
+	dst.a.sign = .Negative if a.sign != b.sign else .Zero_or_Positive
+	return internal_rat_norm(dst)
+}
+
+
+rat_abs :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) {
+	rat_set_rat(dst, x, allocator)          or_return
+	internal_abs(&dst.a, &dst.a, allocator) or_return
+	return
+}
+rat_neg :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) {
+	rat_set_rat(dst, x, allocator)          or_return
+	internal_neg(&dst.a, &dst.a, allocator) or_return
+	return
+}
+
+
+rat_is_positive :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
+	assert_if_nil(z)
+	a := int_is_positive(&z.a, allocator) or_return
+	b := int_is_positive(&z.b, allocator) or_return
+	return !(a ~ b), nil
+}
+rat_is_negative :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
+	assert_if_nil(z)
+	a := int_is_positive(&z.a, allocator) or_return
+	b := int_is_positive(&z.b, allocator) or_return
+	return (a ~ b), nil
+}
+
+rat_is_even :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
+	assert_if_nil(z)
+	if rat_is_int(z) {
+		return int_is_even(&z.a, allocator)
+	}
+	return false, nil
+}
+rat_is_odd :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
+	assert_if_nil(z)
+	if rat_is_int(z) {
+		return int_is_odd(&z.a, allocator)
+	}
+	return false, nil
+}
+
+rat_to_f16 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f16, exact: bool, err: Error) {
+	assert_if_nil(z)
+	return internal_rat_to_float(f16, z, allocator)
+}
+rat_to_f32 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f32, exact: bool, err: Error) {
+	assert_if_nil(z)
+	return internal_rat_to_float(f32, z, allocator)
+}
+rat_to_f64 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f64, exact: bool, err: Error) {
+	assert_if_nil(z)
+	return internal_rat_to_float(f64, z, allocator)
+}
+
+internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocator) -> (f: T, exact: bool, err: Error)  where intrinsics.type_is_float(T) {
+	FSIZE :: 8*size_of(T)
+	when FSIZE == 16 {
+		MSIZE :: 10
+	} else when FSIZE == 32 {
+		MSIZE :: 23
+	} else when FSIZE == 64 {
+		MSIZE :: 52
+	} else {
+		#panic("unsupported float type")
+	}
+	
+	MSIZE1 :: MSIZE+1
+	MSIZE2 :: MSIZE+2
+	
+	ESIZE :: FSIZE - MSIZE1
+	EBIAS :: 1<<(ESIZE-1) - 1
+	EMIN  :: 1 - EBIAS
+	EMAX  :: EBIAS
+	
+	assert_if_nil(z)
+	a, b := &z.a, &z.b
+	
+	context.allocator = allocator
+	
+	alen := internal_count_bits(a)
+	if alen == 0 {
+		return 0, true, nil
+	}
+	blen := internal_count_bits(b)
+	if blen == 0 {
+		return T(math.nan_f64()), false, .Division_by_Zero
+	}
+	
+	has_sign := a.sign != b.sign
+	defer if has_sign {
+		f = -builtin.abs(f)
+	}
+	
+	exp := alen - blen
+	a2, b2 := &Int{}, &Int{}
+	defer internal_destroy(a2, b2)
+	internal_int_abs(a2, a) or_return
+	internal_int_abs(b2, b) or_return
+	
+	if shift := MSIZE2 - exp; shift > 0 {
+		internal_int_shl_digit(a2, shift) or_return
+	} else {
+		internal_int_shl_digit(b2, -shift) or_return
+	}
+	
+	q, r := &Int{}, &Int{}
+	defer internal_destroy(q, r)
+	
+	internal_int_divmod(q, r, a2, b2) or_return
+	
+	has_rem := !internal_is_zero(r)
+	mantissa := internal_int_get_u64(q) or_return
+	
+	if mantissa>>MSIZE2 == 1 {
+		if mantissa&1 == 1 {
+			has_rem = true
+		}
+		mantissa >>= 1
+		exp += 1
+	}
+	
+	assert(mantissa>>MSIZE1 == 1, "invalid bit result")
+	
+	
+	if EMIN-MSIZE <= exp && exp <= EMIN {
+		shift := uint(EMIN - (exp - 1))
+		lost_bits := mantissa & (1<<shift - 1)
+		has_rem ||= lost_bits != 0
+		mantissa >>= shift
+		exp = 2 - EBIAS // exp + shift
+	}
+	
+	
+	exact = !has_rem
+	if mantissa&1 != 0 {
+		exact = false
+		if has_rem || mantissa&2 != 0 {
+			mantissa += 1
+			if mantissa >= 1<<MSIZE2 {
+				mantissa >>= 1
+				exp += 1
+			}
+		}
+	}
+	
+	mantissa >>= 1
+	
+	f = T(math.ldexp(f64(mantissa), i32(exp-MSIZE1)))
+	if math.is_inf(f, 0) {
+		exact = false
+	}
+	return
+}
+
+
+rat_compare :: proc(x, y: ^Rat, allocator := context.allocator) -> (comparison: int, error: Error) {
+	assert_if_nil(x, y)
+	context.allocator = allocator
+	
+	a, b: Int
+	internal_init_multi(&a, &b) or_return
+	defer internal_destroy(&a, &b)
+	internal_int_scale_denom(&a, &x.a, &y.b) or_return
+	internal_int_scale_denom(&b, &y.a, &x.b) or_return
+	return int_compare(&a, &b)
+}
+
+
+
+rat_add_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x)
+	assert_if_nil(y)
+	
+	z: Rat
+	rat_set_int(&z, y, allocator) or_return
+	defer internal_destroy(&z)
+	return rat_add_rat(dst, x, &z, allocator)
+}
+
+rat_sub_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x)
+	assert_if_nil(y)
+	
+	z: Rat
+	rat_set_int(&z, y, allocator) or_return
+	defer internal_destroy(&z)
+	return rat_sub_rat(dst, x, &z, allocator)
+}
+
+rat_mul_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(dst, x)
+	assert_if_nil(y)
+	
+	z: Rat
+	rat_set_int(&z, y, allocator) or_return
+	defer internal_destroy(&z)
+	return rat_mul_rat(dst, x, &z, allocator)
+}
+
+rat_div_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
+	if internal_is_zero(y) {
+		return .Division_by_Zero
+	}
+	z: Rat
+	rat_set_int(&z, y, allocator) or_return
+	defer internal_destroy(&z)
+	return rat_div_rat(dst, x, &z, allocator)
+}
+
+
+int_add_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(x)
+	assert_if_nil(dst, y)
+	
+	w: Rat
+	rat_set_int(&w, x, allocator) or_return
+	defer internal_destroy(&w)
+	return rat_add_rat(dst, &w, y, allocator)
+}
+
+int_sub_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(x)
+	assert_if_nil(dst, y)
+	
+	w: Rat
+	rat_set_int(&w, x, allocator) or_return
+	defer internal_destroy(&w)
+	return rat_sub_rat(dst, &w, y, allocator)
+}
+
+int_mul_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	assert_if_nil(x)
+	assert_if_nil(dst, y)
+	
+	w: Rat
+	rat_set_int(&w, x, allocator) or_return
+	defer internal_destroy(&w)
+	return rat_mul_rat(dst, &w, y, allocator)
+}
+
+int_div_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
+	if internal_is_zero(y) {
+		return .Division_by_Zero
+	}
+	w: Rat
+	rat_set_int(&w, x, allocator) or_return
+	defer internal_destroy(&w)
+	return rat_div_rat(dst, &w, y, allocator)
+}

+ 30 - 42
core/math/bits/bits.odin

@@ -147,55 +147,53 @@ len :: proc{len_u8, len_u16, len_u32, len_u64, len_uint}
 
 
 add_u32 :: proc(x, y, carry: u32) -> (sum, carry_out: u32) {
-	yc := y + carry
-	sum = x + yc
-	if sum < x || yc < y {
-		carry_out = 1
-	}
+	tmp_carry, tmp_carry2: bool
+	sum, tmp_carry = intrinsics.overflow_add(x, y)
+	sum, tmp_carry2 = intrinsics.overflow_add(sum, carry)
+	carry_out = u32(tmp_carry | tmp_carry2)
 	return
 }
 add_u64 :: proc(x, y, carry: u64) -> (sum, carry_out: u64) {
-	yc := y + carry
-	sum = x + yc
-	if sum < x || yc < y {
-		carry_out = 1
-	}
+	tmp_carry, tmp_carry2: bool
+	sum, tmp_carry = intrinsics.overflow_add(x, y)
+	sum, tmp_carry2 = intrinsics.overflow_add(sum, carry)
+	carry_out = u64(tmp_carry | tmp_carry2)
 	return
 }
 add_uint :: proc(x, y, carry: uint) -> (sum, carry_out: uint) {
-	yc := y + carry
-	sum = x + yc
-	if sum < x || yc < y {
-		carry_out = 1
+	when size_of(uint) == size_of(u64) {
+		a, b := add_u64(u64(x), u64(y), u64(carry))
+	} else {
+		#assert(size_of(uint) == size_of(u32))
+		a, b := add_u32(u32(x), u32(y), u32(carry))
 	}
-	return
+	return uint(a), uint(b)
 }
 add :: proc{add_u32, add_u64, add_uint}
 
 
 sub_u32 :: proc(x, y, borrow: u32) -> (diff, borrow_out: u32) {
-	yb := y + borrow
-	diff = x - yb
-	if diff > x || yb < y {
-		borrow_out = 1
-	}
+	tmp_borrow, tmp_borrow2: bool
+	diff, tmp_borrow = intrinsics.overflow_sub(x, y)
+	diff, tmp_borrow2 = intrinsics.overflow_sub(diff, borrow)
+	borrow_out = u32(tmp_borrow | tmp_borrow2)
 	return
 }
 sub_u64 :: proc(x, y, borrow: u64) -> (diff, borrow_out: u64) {
-	yb := y + borrow
-	diff = x - yb
-	if diff > x || yb < y {
-		borrow_out = 1
-	}
+	tmp_borrow, tmp_borrow2: bool
+	diff, tmp_borrow = intrinsics.overflow_sub(x, y)
+	diff, tmp_borrow2 = intrinsics.overflow_sub(diff, borrow)
+	borrow_out = u64(tmp_borrow | tmp_borrow2)
 	return
 }
 sub_uint :: proc(x, y, borrow: uint) -> (diff, borrow_out: uint) {
-	yb := y + borrow
-	diff = x - yb
-	if diff > x || yb < y {
-		borrow_out = 1
+	when size_of(uint) == size_of(u64) {
+		a, b := sub_u64(u64(x), u64(y), u64(borrow))
+	} else {
+		#assert(size_of(uint) == size_of(u32))
+		a, b := sub_u32(u32(x), u32(y), u32(borrow))
 	}
-	return
+	return uint(a), uint(b)
 }
 sub :: proc{sub_u32, sub_u64, sub_uint}
 
@@ -206,18 +204,8 @@ mul_u32 :: proc(x, y: u32) -> (hi, lo: u32) {
 	return
 }
 mul_u64 :: proc(x, y: u64) -> (hi, lo: u64) {
-	mask :: 1<<32 - 1
-
-	x0, x1 := x & mask, x >> 32
-	y0, y1 := y & mask, y >> 32
-
-	w0 := x0 * y0
-	t := x1*y0 + w0>>32
-
-	w1, w2 := t & mask, t >> 32
-	w1 += x0 * y1
-	hi = x1*y1 + w2 + w1>>32
-	lo = x * y
+	prod_wide := u128(x) * u128(y)
+	hi, lo = u64(prod_wide>>64), u64(prod_wide)
 	return
 }
 

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

@@ -224,6 +224,15 @@ Slice_Expr :: struct {
 	close:    tokenizer.Pos,
 }
 
+Matrix_Index_Expr :: struct {
+	using node: Expr,
+	expr:         ^Expr,
+	open:         tokenizer.Pos,
+	row_index:    ^Expr,
+	column_index: ^Expr,
+	close:        tokenizer.Pos,
+}
+
 Call_Expr :: struct {
 	using node: Expr,
 	inlining: Proc_Inlining,
@@ -739,3 +748,11 @@ Relative_Type :: struct {
 	tag:  ^Expr,
 	type: ^Expr,
 }
+
+Matrix_Type :: struct {
+	using node: Expr,
+	tok_pos:      tokenizer.Pos,
+	row_count:    ^Expr,
+	column_count: ^Expr,
+	elem:         ^Expr,
+}

+ 8 - 1
core/odin/ast/clone.odin

@@ -117,6 +117,10 @@ clone_node :: proc(node: ^Node) -> ^Node {
 	case Index_Expr:
 		r.expr = clone(r.expr)
 		r.index = clone(r.index)
+	case Matrix_Index_Expr:
+		r.expr = clone(r.expr)
+		r.row_index = clone(r.row_index)
+		r.column_index = clone(r.column_index)
 	case Deref_Expr:
 		r.expr = clone(r.expr)
 	case Slice_Expr:
@@ -275,7 +279,10 @@ clone_node :: proc(node: ^Node) -> ^Node {
 	case Map_Type:
 		r.key = clone(r.key)
 		r.value = clone(r.value)
-
+	case Matrix_Type:
+		r.row_count = clone(r.row_count)
+		r.column_count = clone(r.column_count)
+		r.elem = clone(r.elem)
 	case:
 		fmt.panicf("Unhandled node kind: %T", r)
 	}

+ 8 - 0
core/odin/ast/walk.odin

@@ -110,6 +110,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 	case Index_Expr:
 		walk(v, n.expr)
 		walk(v, n.index)
+	case Matrix_Index_Expr:
+		walk(v, n.expr)
+		walk(v, n.row_index)
+		walk(v, n.column_index)
 	case Deref_Expr:
 		walk(v, n.expr)
 	case Slice_Expr:
@@ -398,6 +402,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 	case Relative_Type:
 		walk(v, n.tag)
 		walk(v, n.type)
+	case Matrix_Type:
+		walk(v, n.row_count)
+		walk(v, n.column_count)
+		walk(v, n.elem)
 
 	case:
 		fmt.panicf("ast.walk: unexpected node type %T", n)

+ 13 - 8
core/odin/doc-format/doc_format.odin

@@ -10,7 +10,7 @@ Array :: struct($T: typeid) {
 String :: distinct Array(byte)
 
 Version_Type_Major :: 0
-Version_Type_Minor :: 1
+Version_Type_Minor :: 2
 Version_Type_Patch :: 0
 
 Version_Type :: struct {
@@ -101,17 +101,19 @@ Entity_Flag :: enum u32le {
 	Param_Ellipsis  = 5, // Variadic parameter
 	Param_CVararg   = 6, // #c_vararg
 	Param_No_Alias  = 7, // #no_alias
+	Param_Any_Int   = 8, // #any_int
 
-	Type_Alias = 8,
+	Type_Alias = 20,
 
-	Var_Thread_Local = 9,
-	Var_Static       = 10,
+	Var_Thread_Local = 40,
+	Var_Static       = 41,
 }
 
-Entity_Flags :: distinct bit_set[Entity_Flag; u32le]
+Entity_Flags :: distinct bit_set[Entity_Flag; u64le]
 
 Entity :: struct {
 	kind:             Entity_Kind,
+	_:                u32le, // reserved
 	flags:            Entity_Flags,
 	pos:              Position,
 	name:             String,
@@ -167,6 +169,7 @@ Type_Kind :: enum u32le {
 	Relative_Pointer   = 20,
 	Relative_Slice     = 21,
 	Multi_Pointer      = 22,
+	Matrix             = 23,
 }
 
 Type_Elems_Cap :: 4
@@ -192,11 +195,12 @@ Type :: struct {
 	custom_align: String,
 
 	// Used by:
-	// .Array - 1 count: 0=len
+	// .Array            - 1 count: 0=len
 	// .Enumerated_Array - 1 count: 0=len
 	// .SOA_Struct_Fixed - 1 count: 0=len
-	// .Bit_Set - 2 count: 0=lower, 1=upper
-	// .Simd_Vector - 1 count: 0=len
+	// .Bit_Set          - 2 count: 0=lower, 1=upper
+	// .Simd_Vector      - 1 count: 0=len
+	// .Matrix           - 2 count: 0=row_count, 1=column_count
 	elem_count_len: u32le,
 	elem_counts:    [Type_Elems_Cap]i64le,
 
@@ -224,6 +228,7 @@ Type :: struct {
 	// .Relative_Pointer   - 2 types:   0=pointer type, 1=base integer
 	// .Relative_Slice     - 2 types:   0=slice type, 1=base integer
 	// .Multi_Pointer      - 1 type:    0=element
+	// .Matrix             - 1 type:    0=element
 	types: Array(Type_Index),
 
 	// Used by:

+ 44 - 14
core/odin/parser/parser.odin

@@ -2703,6 +2703,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		bst.underlying = underlying
 		bst.close = close.pos
 		return bst
+		
+	case .Matrix:
+		tok := expect_token(p, .Matrix)
+		expect_token(p, .Open_Bracket)
+		row_count := parse_expr(p, false)
+		expect_token(p, .Comma)
+		column_count := parse_expr(p, false)
+		expect_token(p, .Close_Bracket)
+		elem := parse_type(p)
+
+		mt := ast.new(ast.Matrix_Type, tok.pos, elem.end)
+		mt.tok_pos = tok.pos
+		mt.row_count = row_count
+		mt.column_count = column_count
+		mt.elem = elem
+		return mt
 
 	case .Asm:
 		tok := expect_token(p, .Asm)
@@ -2969,7 +2985,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 			defer p.allow_range = prev_allow_range
 			p.allow_range = false
 
-			indicies: [2]^ast.Expr
+			indices: [2]^ast.Expr
 			interval: tokenizer.Token
 			is_slice_op := false
 
@@ -2981,18 +2997,18 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 				// NOTE(bill): Do not err yet
 				break
 			case:
-				indicies[0] = parse_expr(p, false)
+				indices[0] = parse_expr(p, false)
 			}
 
 			#partial switch p.curr_tok.kind {
 			case .Ellipsis, .Range_Half, .Range_Full:
 				error(p, p.curr_tok.pos, "expected a colon, not a range")
 				fallthrough
-			case .Colon:
+			case .Colon, .Comma/*matrix index*/:
 				interval = advance_token(p)
 				is_slice_op = true
 				if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
-					indicies[1] = parse_expr(p, false)
+					indices[1] = parse_expr(p, false)
 				}
 			}
 
@@ -3000,20 +3016,34 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 			p.expr_level -= 1
 
 			if is_slice_op {
-				se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close))
-				se.expr = operand
-				se.open = open.pos
-				se.low = indicies[0]
-				se.interval = interval
-				se.high = indicies[1]
-				se.close = close.pos
-
-				operand = se
+				if interval.kind == .Comma {
+					if indices[0] == nil || indices[1] == nil {
+						error(p, p.curr_tok.pos, "matrix index expressions require both row and column indices")
+					}
+					se := ast.new(ast.Matrix_Index_Expr, operand.pos, end_pos(close))
+					se.expr = operand
+					se.open = open.pos
+					se.row_index = indices[0]
+					se.column_index = indices[1]
+					se.close = close.pos
+
+					operand = se
+				} else {
+					se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close))
+					se.expr = operand
+					se.open = open.pos
+					se.low = indices[0]
+					se.interval = interval
+					se.high = indices[1]
+					se.close = close.pos
+
+					operand = se
+				}
 			} else {
 				ie := ast.new(ast.Index_Expr, operand.pos, end_pos(close))
 				ie.expr = operand
 				ie.open = open.pos
-				ie.index = indicies[0]
+				ie.index = indices[0]
 				ie.close = close.pos
 
 				operand = ie

+ 8 - 6
core/odin/tokenizer/token.odin

@@ -150,6 +150,7 @@ Token_Kind :: enum u32 {
 		Asm,         // asm
 		Inline,      // inline
 		No_Inline,   // no_inline
+		Matrix,      // matrix
 	B_Keyword_End,
 
 	COUNT,
@@ -280,6 +281,7 @@ tokens := [Token_Kind.COUNT]string {
 	"asm",
 	"inline",
 	"no_inline",
+	"matrix",
 	"",
 }
 
@@ -299,10 +301,10 @@ token_to_string :: proc(tok: Token) -> string {
 }
 
 to_string :: proc(kind: Token_Kind) -> string {
-	if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
+	if .Invalid <= kind && kind < .COUNT {
 		return tokens[kind]
 	}
-	if Token_Kind.B_Custom_Keyword_Begin < kind {
+	if .B_Custom_Keyword_Begin < kind {
 		n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin))
 		if n < len(custom_keyword_tokens) {
 			return custom_keyword_tokens[n]
@@ -313,7 +315,7 @@ to_string :: proc(kind: Token_Kind) -> string {
 }
 
 is_literal  :: proc(kind: Token_Kind) -> bool {
-	return Token_Kind.B_Literal_Begin  < kind && kind < Token_Kind.B_Literal_End
+	return .B_Literal_Begin  < kind && kind < .B_Literal_End
 }
 is_operator :: proc(kind: Token_Kind) -> bool {
 	#partial switch kind {
@@ -327,13 +329,13 @@ is_operator :: proc(kind: Token_Kind) -> bool {
 	return false
 }
 is_assignment_operator :: proc(kind: Token_Kind) -> bool {
-	return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq
+	return .B_Assign_Op_Begin < kind && kind < .B_Assign_Op_End || kind == .Eq
 }
 is_keyword :: proc(kind: Token_Kind) -> bool {
 	switch {
-	case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
+	case .B_Keyword_Begin < kind && kind < .B_Keyword_End:
 		return true
-	case Token_Kind.B_Custom_Keyword_Begin < kind:
+	case .B_Custom_Keyword_Begin < kind:
 		return true
 	}
 	return false

+ 49 - 5
core/time/time.odin

@@ -43,6 +43,36 @@ Weekday :: enum int {
 	Saturday,
 }
 
+Stopwatch :: struct {
+	running: bool,
+	_start_time: Tick,
+	_accumulation: Duration,
+}
+
+stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
+	if !running {
+		_start_time = tick_now()
+		running = true
+	}
+}
+
+stopwatch_stop :: proc(using stopwatch: ^Stopwatch) {
+	if running {
+		_accumulation += tick_diff(_start_time, tick_now())
+		running = false
+	}
+}
+
+stopwatch_reset :: proc(using stopwatch: ^Stopwatch) {
+	_accumulation = {}
+	running = false
+}
+
+stopwatch_duration :: proc(using stopwatch: Stopwatch) -> Duration {
+	if !running { return _accumulation }
+	return _accumulation + tick_diff(_start_time, tick_now())
+}
+
 diff :: proc(start, end: Time) -> Duration {
 	d := end._nsec - start._nsec
 	return Duration(d)
@@ -52,7 +82,6 @@ since :: proc(start: Time) -> Duration {
 	return diff(start, now())
 }
 
-
 duration_nanoseconds :: proc(d: Duration) -> i64 {
 	return i64(d)
 }
@@ -106,6 +135,7 @@ duration_round :: proc(d, m: Duration) -> Duration {
 	}
 	return MAX_DURATION
 }
+
 duration_truncate :: proc(d, m: Duration) -> Duration {
 	return d if m <= 0 else d - d%m
 }
@@ -119,17 +149,33 @@ year :: proc(t: Time) -> (year: int) {
 	year, _, _, _ = _date(t, true)
 	return
 }
+
 month :: proc(t: Time) -> (month: Month) {
 	_, month, _, _ = _date(t, true)
 	return
 }
+
 day :: proc(t: Time) -> (day: int) {
 	_, _, day, _ = _date(t, true)
 	return
 }
 
-clock :: proc(t: Time) -> (hour, min, sec: int) {
-	sec = int(_time_abs(t) % SECONDS_PER_DAY)
+clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
+
+clock_from_time :: proc(t: Time) -> (hour, min, sec: int) {
+	return clock_from_seconds(_time_abs(t))
+}
+
+clock_from_duration :: proc(d: Duration) -> (hour, min, sec: int) {
+	return clock_from_seconds(u64(d/1e9))
+}
+
+clock_from_stopwatch :: proc(s: Stopwatch) -> (hour, min, sec: int) {
+	return clock_from_duration(stopwatch_duration(s))
+}
+
+clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
+	sec = int(nsec % SECONDS_PER_DAY)
 	hour = sec / SECONDS_PER_HOUR
 	sec -= hour * SECONDS_PER_HOUR
 	min = sec / SECONDS_PER_MINUTE
@@ -137,12 +183,10 @@ clock :: proc(t: Time) -> (hour, min, sec: int) {
 	return
 }
 
-
 read_cycle_counter :: proc() -> u64 {
 	return u64(intrinsics.read_cycle_counter())
 }
 
-
 unix :: proc(sec: i64, nsec: i64) -> Time {
 	sec, nsec := sec, nsec
 	if nsec < 0 || nsec >= 1e9 {

+ 210 - 0
examples/demo/demo.odin

@@ -2203,6 +2203,215 @@ arbitrary_precision_maths :: proc() {
 	print_bigint("\nLCM of random prime A and random number B (in base 36): ", d, 36)
 }
 
+matrix_type :: proc() {
+	fmt.println("\n# matrix type")
+	// A matrix is a mathematical type built into Odin. It is a regular array of numbers,
+	// arranged in rows and columns
+	
+	{
+		// The following represents a matrix that has 2 rows and 3 columns
+		m: matrix[2, 3]f32
+		
+		m = matrix[2, 3]f32{
+			1, 9, -13,
+			20, 5, -6,
+		}
+		
+		// Element types of integers, float, and complex numbers are supported by matrices.
+		// There is no support for booleans, quaternions, or any compound type.
+		
+		// Indexing a matrix can be used with the matrix indexing syntax
+		// This mirrors othe type usages: type on the left, usage on the right
+		
+		elem := m[1, 2] // row 1, column 2
+		assert(elem == -6)
+		
+		
+		// Scalars act as if they are scaled identity matrices
+		// and can be assigned to matrices as them
+		b := matrix[2, 2]f32{}
+		f := f32(3)
+		b = f
+		
+		fmt.println("b", b)
+		fmt.println("b == f", b == f)
+		
+	} 
+	
+	{ // Matrices support multiplication between matrices
+		a := matrix[2, 3]f32{
+			2, 3, 1,
+			4, 5, 0,
+		}
+		
+		b := matrix[3, 2]f32{
+			1, 2,
+			3, 4,
+			5, 6,
+		}
+		
+		fmt.println("a", a)
+		fmt.println("b", b)
+		
+		c := a * b
+		#assert(type_of(c) == matrix[2, 2]f32)
+		fmt.tprintln("c = a * b", c)		
+	}
+	
+	{ // Matrices support multiplication between matrices and arrays
+		m := matrix[4, 4]f32{
+			1, 2, 3, 4, 
+			5, 5, 4, 2, 
+			0, 1, 3, 0, 
+			0, 1, 4, 1,
+		}
+		
+		v := [4]f32{1, 5, 4, 3}
+		
+		// treating 'v' as a column vector
+		fmt.println("m * v", m * v)
+		
+		// treating 'v' as a row vector
+		fmt.println("v * m", v * m)
+		
+		// Support with non-square matrices
+		s := matrix[2, 4]f32{ // [4][2]f32
+			2, 4, 3, 1, 
+			7, 8, 6, 5, 
+		}
+		
+		w := [2]f32{1, 2}
+		r: [4]f32 = w * s
+		fmt.println("r", r)
+	}
+	
+	{ // Component-wise operations 
+		// if the element type supports it
+		// Not support for '/', '%', or '%%' operations
+		
+		a := matrix[2, 2]i32{
+			1, 2,
+			3, 4,
+		}
+		
+		b := matrix[2, 2]i32{
+			-5,  1,
+			 9, -7,
+		}
+		
+		c0 := a + b
+		c1 := a - b
+		c2 := a & b
+		c3 := a | b
+		c4 := a ~ b
+		c5 := a &~ b
+
+		// component-wise multiplication
+		// since a * b would be a standard matrix multiplication
+		c6 := hadamard_product(a, b) 
+		
+		
+		fmt.println("a + b",  c0)
+		fmt.println("a - b",  c1)
+		fmt.println("a & b",  c2)
+		fmt.println("a | b",  c3)
+		fmt.println("a ~ b",  c4)
+		fmt.println("a &~ b", c5)
+		fmt.println("hadamard_product(a, b)", c6)
+	}
+	
+	{ // Submatrix casting square matrices
+		// Casting a square matrix to another square matrix with same element type
+		// is supported. 
+		// If the cast is to a smaller matrix type, the top-left submatrix is taken.
+		// If the cast is to a larger matrix type, the matrix is extended with zeros
+		// everywhere and ones in the diagonal for the unfilled elements of the 
+		// extended matrix.
+		
+		mat2 :: distinct matrix[2, 2]f32
+		mat4 :: distinct matrix[4, 4]f32
+		
+		m2 := mat2{
+			1, 3,
+			2, 4,
+		}
+		
+		m4 := mat4(m2)
+		assert(m4[2, 2] == 1)
+		assert(m4[3, 3] == 1)
+		fmt.println("m2", m2)
+		fmt.println("m4", m4)
+		fmt.println("mat2(m4)", mat2(m4))
+		assert(mat2(m4) == m2)
+	}
+	
+	{ // Casting non-square matrices
+		// Casting a matrix to another matrix is allowed as long as they share 
+		// the same element type and the number of elements (rows*columns).
+		// Matrices in Odin are stored in column-major order, which means
+		// the casts will preserve this element order.
+		
+		mat2x4 :: distinct matrix[2, 4]f32
+		mat4x2 :: distinct matrix[4, 2]f32
+		
+		x := mat2x4{
+			1, 3, 5, 7, 
+			2, 4, 6, 8,
+		}
+		
+		y := mat4x2(x)
+		fmt.println("x", x)
+		fmt.println("y", y)
+	}
+	
+	// TECHNICAL INFORMATION: the internal representation of a matrix in Odin is stored
+	// in column-major format
+	// e.g. matrix[2, 3]f32 is internally [3][2]f32 (with different a alignment requirement)
+	// Column-major is used in order to utilize (SIMD) vector instructions effectively on 
+	// modern hardware, if possible.
+	//
+	// Unlike normal arrays, matrices try to maximize alignment to allow for the (SIMD) vectorization
+	// properties whilst keeping zero padding (either between columns or at the end of the type).
+	// 
+	// Zero padding is a compromise for use with third-party libraries, instead of optimizing for performance.
+	// Padding between columns was not taken even if that would have allowed each column to be loaded 
+	// individually into a SIMD register with the correct alignment properties. 
+	// 
+	// Currently, matrices are limited to a maximum of 16 elements (rows*columns), and a minimum of 1 element.
+	// This is because matrices are stored as values (not a reference type), and thus operations on them will
+	// be stored on the stack. Restricting the maximum element count minimizing the possibility of stack overflows.
+	
+	// Built-in Procedures (Compiler Level)
+	// 	transpose(m)
+	//		transposes a matrix
+	// 	outer_product(a, b)
+	// 		takes two array-like data types and returns the outer product
+	//		of the values in a matrix
+	// 	hadamard_product(a, b)
+	// 		component-wise multiplication of two matrices of the same type
+	// 	matrix_flatten(m)
+	//		converts the matrix into a flatten array of elements
+	//		in column-major order
+	//		Example:
+	//		m := matrix[2, 2]f32{
+	//			x0, x1,
+	//			y0, y1,	
+	//		}
+	//		array: [4]f32 = matrix_flatten(m)
+	//		assert(array == {x0, y0, x1, y1})
+	//	conj(x)
+	//		conjugates the elements of a matrix for complex element types only
+	
+	// Built-in Procedures (Runtime Level) (all square matrix procedures)
+	// 	determinant(m)
+	// 	adjugate(m)
+	// 	inverse(m)
+	// 	inverse_transpose(m)
+	// 	hermitian_adjoint(m)
+	// 	matrix_trace(m)
+	// 	matrix_minor(m)
+}
+
 main :: proc() {
 	when true {
 		the_basics()
@@ -2238,5 +2447,6 @@ main :: proc() {
 		or_else_operator()
 		or_return_operator()
 		arbitrary_precision_maths()
+		matrix_type()
 	}
 }

+ 11 - 1
src/common.cpp

@@ -443,7 +443,17 @@ u64 ceil_log2(u64 x) {
 	return cast(u64)(bit_set_count(x) - 1 - y);
 }
 
-
+u32 prev_pow2(u32 n) {
+	if (n == 0) {
+		return 0;
+	}
+	n |= n >> 1;
+	n |= n >> 2;
+	n |= n >> 4;
+	n |= n >> 8;
+	n |= n >> 16;
+	return n - (n >> 1);
+}
 i32 prev_pow2(i32 n) {
 	if (n <= 0) {
 		return 0;

+ 15 - 13
src/docs_format.cpp

@@ -14,7 +14,7 @@ struct OdinDocVersionType {
 };
 
 #define OdinDocVersionType_Major 0
-#define OdinDocVersionType_Minor 1
+#define OdinDocVersionType_Minor 2
 #define OdinDocVersionType_Patch 0
 
 struct OdinDocHeaderBase {
@@ -82,6 +82,7 @@ enum OdinDocTypeKind : u32 {
 	OdinDocType_RelativePointer  = 20,
 	OdinDocType_RelativeSlice    = 21,
 	OdinDocType_MultiPointer     = 22,
+	OdinDocType_Matrix           = 23,
 };
 
 enum OdinDocTypeFlag_Basic : u32 {
@@ -154,21 +155,22 @@ enum OdinDocEntityKind : u32 {
 	OdinDocEntity_LibraryName = 7,
 };
 
-enum OdinDocEntityFlag : u32 {
-	OdinDocEntityFlag_Foreign = 1<<0,
-	OdinDocEntityFlag_Export  = 1<<1,
+enum OdinDocEntityFlag : u64 {
+	OdinDocEntityFlag_Foreign = 1ull<<0,
+	OdinDocEntityFlag_Export  = 1ull<<1,
 
-	OdinDocEntityFlag_Param_Using    = 1<<2,
-	OdinDocEntityFlag_Param_Const    = 1<<3,
-	OdinDocEntityFlag_Param_AutoCast = 1<<4,
-	OdinDocEntityFlag_Param_Ellipsis = 1<<5,
-	OdinDocEntityFlag_Param_CVararg  = 1<<6,
-	OdinDocEntityFlag_Param_NoAlias  = 1<<7,
+	OdinDocEntityFlag_Param_Using    = 1ull<<2,
+	OdinDocEntityFlag_Param_Const    = 1ull<<3,
+	OdinDocEntityFlag_Param_AutoCast = 1ull<<4,
+	OdinDocEntityFlag_Param_Ellipsis = 1ull<<5,
+	OdinDocEntityFlag_Param_CVararg  = 1ull<<6,
+	OdinDocEntityFlag_Param_NoAlias  = 1ull<<7,
+	OdinDocEntityFlag_Param_AnyInt   = 1ull<<8,
 
-	OdinDocEntityFlag_Type_Alias = 1<<8,
+	OdinDocEntityFlag_Type_Alias = 1ull<<20,
 
-	OdinDocEntityFlag_Var_Thread_Local = 1<<9,
-	OdinDocEntityFlag_Var_Static       = 1<<10,
+	OdinDocEntityFlag_Var_Thread_Local = 1ull<<40,
+	OdinDocEntityFlag_Var_Static       = 1ull<<41,
 };
 
 struct OdinDocEntity {

+ 9 - 0
src/docs_writer.cpp

@@ -756,6 +756,14 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
 			doc_type.types = odin_write_slice(w, types, gb_count_of(types));
 		}
 		break;
+		
+	case Type_Matrix:
+		doc_type.kind = OdinDocType_Matrix;
+		doc_type.elem_count_len = 2;
+		doc_type.elem_counts[0] = type->Matrix.row_count;
+		doc_type.elem_counts[1] = type->Matrix.column_count;
+		doc_type.types = odin_doc_type_as_slice(w, type->Matrix.elem);
+		break;
 	}
 
 	if (dst) {
@@ -842,6 +850,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 		if (e->flags & EntityFlag_AutoCast)   { flags |= OdinDocEntityFlag_Param_AutoCast; }
 		if (e->flags & EntityFlag_Ellipsis)   { flags |= OdinDocEntityFlag_Param_Ellipsis; }
 		if (e->flags & EntityFlag_NoAlias)    { flags |= OdinDocEntityFlag_Param_NoAlias;  }
+		if (e->flags & EntityFlag_AnyInt)     { flags |= OdinDocEntityFlag_Param_AnyInt;   }
 	}
 
 	OdinDocString init_string = {};

+ 14 - 0
src/llvm_backend_debug.cpp

@@ -437,6 +437,20 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 			gbString name = type_to_string(type, temporary_allocator());
 			return LLVMDIBuilderCreateStructType(m->debug_builder, nullptr, name, gb_string_length(name), nullptr, 0, 2*word_bits, word_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
 		}
+		
+	case Type_Matrix: {
+		LLVMMetadataRef subscripts[1] = {};
+		subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
+			0ll,
+			matrix_type_total_internal_elems(type)
+		);
+
+		return LLVMDIBuilderCreateArrayType(m->debug_builder,
+			8*cast(uint64_t)type_size_of(type),
+			8*cast(unsigned)type_align_of(type),
+			lb_debug_type(m, type->Matrix.elem),
+			subscripts, gb_count_of(subscripts));
+	}
 	}
 
 	GB_PANIC("Invalid type %s", type_to_string(type));

+ 97 - 14
src/llvm_backend_utility.cpp

@@ -1563,15 +1563,61 @@ LLVMValueRef llvm_vector_broadcast(lbProcedure *p, LLVMValueRef value, unsigned
 	return LLVMBuildShuffleVector(p->builder, single, LLVMGetUndef(LLVMTypeOf(single)), mask, "");
 }
 
+LLVMValueRef llvm_vector_shuffle_reduction(lbProcedure *p, LLVMValueRef value, LLVMOpcode op_code) {
+	LLVMTypeRef original_vector_type = LLVMTypeOf(value);
+	
+	GB_ASSERT(LLVMGetTypeKind(original_vector_type) == LLVMVectorTypeKind);
+	unsigned len = LLVMGetVectorSize(original_vector_type);
+	
+	LLVMValueRef v_zero32 = lb_const_int(p->module, t_u32, 0).value;
+	if (len == 1) {
+		return LLVMBuildExtractElement(p->builder, value, v_zero32, "");
+	}
+	GB_ASSERT((len & (len-1)) == 0);
+	
+	for (unsigned i = len; i != 1; i >>= 1) {
+		unsigned mask_len = i/2;
+		LLVMValueRef lhs_mask = llvm_mask_iota(p->module, 0, mask_len);
+		LLVMValueRef rhs_mask = llvm_mask_iota(p->module, mask_len, mask_len);
+		GB_ASSERT(LLVMTypeOf(lhs_mask) == LLVMTypeOf(rhs_mask));
+
+		LLVMValueRef lhs = LLVMBuildShuffleVector(p->builder, value, LLVMGetUndef(LLVMTypeOf(value)), lhs_mask, "");
+		LLVMValueRef rhs = LLVMBuildShuffleVector(p->builder, value, LLVMGetUndef(LLVMTypeOf(value)), rhs_mask, "");
+		GB_ASSERT(LLVMTypeOf(lhs) == LLVMTypeOf(rhs));
+		
+		value = LLVMBuildBinOp(p->builder, op_code, lhs, rhs, "");
+	}
+	return LLVMBuildExtractElement(p->builder, value, v_zero32, "");
+}
+
+LLVMValueRef llvm_vector_expand_to_power_of_two(lbProcedure *p, LLVMValueRef value) {
+	LLVMTypeRef vector_type = LLVMTypeOf(value);
+	unsigned len = LLVMGetVectorSize(vector_type);
+	if (len == 1) {
+		return value;
+	}
+	if ((len & (len-1)) == 0) {
+		return value;
+	}
+	
+	unsigned expanded_len = cast(unsigned)next_pow2(cast(i64)len);
+	LLVMValueRef mask = llvm_mask_iota(p->module, 0, expanded_len);
+	return LLVMBuildShuffleVector(p->builder, value, LLVMConstNull(vector_type), mask, "");
+}
+
 LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
 	LLVMTypeRef type = LLVMTypeOf(value);
 	GB_ASSERT(LLVMGetTypeKind(type) == LLVMVectorTypeKind);
 	LLVMTypeRef elem = LLVMGetElementType(type);
-	
+	unsigned len = LLVMGetVectorSize(type);
+	if (len == 0) {
+		return LLVMConstNull(type);
+	}
+
 	char const *name = nullptr;
 	i32 value_offset = 0;
 	i32 value_count  = 0;
-	
+
 	switch (LLVMGetTypeKind(elem)) {
 	case LLVMHalfTypeKind:
 	case LLVMFloatTypeKind:
@@ -1589,19 +1635,56 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
 		GB_PANIC("invalid vector type %s", LLVMPrintTypeToString(type));
 		break;
 	}
-	
+
 	unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
-	GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
-	
-	LLVMTypeRef types[1] = {};
-	types[0] = type;
-	
-	LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-	LLVMValueRef values[2] = {};
-	values[0] = LLVMConstNull(elem);
-	values[1] = value;
-	LLVMValueRef call = LLVMBuildCall(p->builder, ip, values+value_offset, value_count, "");
-	return call;
+	if (id != 0 && false) {
+		LLVMTypeRef types[1] = {};
+		types[0] = type;
+		
+		LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+		LLVMValueRef values[2] = {};
+		values[0] = LLVMConstNull(elem);
+		values[1] = value;
+		LLVMValueRef call = LLVMBuildCall(p->builder, ip, values+value_offset, value_count, "");
+		return call;
+	}
+
+	// Manual reduce
+#if 0
+	LLVMValueRef sum = LLVMBuildExtractElement(p->builder, value, lb_const_int(p->module, t_u32, 0).value, "");
+	for (unsigned i = 0; i < len; i++) {
+		LLVMValueRef val = LLVMBuildExtractElement(p->builder, value, lb_const_int(p->module, t_u32, i).value, "");
+		if (LLVMGetTypeKind(elem) == LLVMIntegerTypeKind) {
+			sum = LLVMBuildAdd(p->builder, sum, val, "");
+		} else {
+			sum = LLVMBuildFAdd(p->builder, sum, val, "");
+		}
+	}
+	return sum;
+#else
+	LLVMOpcode op_code = LLVMFAdd;
+	if (LLVMGetTypeKind(elem) == LLVMIntegerTypeKind) {
+		op_code = LLVMAdd;
+	}
+
+	unsigned len_pow_2 = prev_pow2(len);
+	if (len_pow_2 == len) {
+		return llvm_vector_shuffle_reduction(p, value, op_code);
+	} else {
+		GB_ASSERT(len_pow_2 < len);
+		LLVMValueRef lower_mask = llvm_mask_iota(p->module, 0, len_pow_2);
+		LLVMValueRef upper_mask = llvm_mask_iota(p->module, len_pow_2, len-len_pow_2);
+		LLVMValueRef lower = LLVMBuildShuffleVector(p->builder, value, LLVMGetUndef(LLVMTypeOf(value)), lower_mask, "");
+		LLVMValueRef upper = LLVMBuildShuffleVector(p->builder, value, LLVMGetUndef(LLVMTypeOf(value)), upper_mask, "");
+		upper = llvm_vector_expand_to_power_of_two(p, upper);
+
+		LLVMValueRef lower_reduced = llvm_vector_shuffle_reduction(p, lower, op_code);
+		LLVMValueRef upper_reduced = llvm_vector_shuffle_reduction(p, upper, op_code);
+		GB_ASSERT(LLVMTypeOf(lower_reduced) == LLVMTypeOf(upper_reduced));
+
+		return LLVMBuildBinOp(p->builder, op_code, lower_reduced, upper_reduced, "");
+	}
+#endif
 }
 
 LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {