From ee724f183a64372f987ad2f0c95fc913c1830151 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Wed, 27 Oct 2021 04:27:36 +0000 Subject: [PATCH 01/17] core:math/bits: Improve add/sub/mul_u64 * `add_u32`/`add_u64`/`add_uint` are now constant time * `sub_u32`/`sub_u64`/`sub_uint` are now constant time * `mul_u64` now uses `u128` --- core/math/bits/bits.odin | 72 +++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/core/math/bits/bits.odin b/core/math/bits/bits.odin index e52ebaab3..bff984cc7 100644 --- a/core/math/bits/bits.odin +++ b/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 } From ccab715bbc3153e2fb832b1d5b2bc061d8900da6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 27 Oct 2021 23:26:17 +0100 Subject: [PATCH 02/17] Add support for matrix type in doc format --- src/docs_format.cpp | 1 + src/docs_writer.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/docs_format.cpp b/src/docs_format.cpp index ee9842534..98ac13c8b 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -82,6 +82,7 @@ enum OdinDocTypeKind : u32 { OdinDocType_RelativePointer = 20, OdinDocType_RelativeSlice = 21, OdinDocType_MultiPointer = 22, + OdinDocType_Matrix = 23, }; enum OdinDocTypeFlag_Basic : u32 { diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 451c44c4e..25aa68167 100644 --- a/src/docs_writer.cpp +++ b/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) { From 42a8ac7096ada6f585d10233f4439ca82eab580c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 27 Oct 2021 23:26:39 +0100 Subject: [PATCH 03/17] Add support for matrix type in `core:odin/doc-format` --- core/odin/doc-format/doc_format.odin | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index 6821f5ef9..0b910d698 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -167,6 +167,7 @@ Type_Kind :: enum u32le { Relative_Pointer = 20, Relative_Slice = 21, Multi_Pointer = 22, + Matrix = 23, } Type_Elems_Cap :: 4 @@ -197,6 +198,7 @@ Type :: struct { // .SOA_Struct_Fixed - 1 count: 0=len // .Bit_Set - 2 count: 0=lower, 1=upper // .Simd_Vector - 1 count: 0=len + // .Matrix - 2count: 0=row_count, 1=column_count elem_count_len: u32le, elem_counts: [Type_Elems_Cap]i64le, @@ -224,6 +226,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: From 208ba2c1163fb955ab74580c6bb6877db1c375d3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 27 Oct 2021 23:30:12 +0100 Subject: [PATCH 04/17] Comment clean up --- core/odin/doc-format/doc_format.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index 0b910d698..090b520d8 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -193,12 +193,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 - // .Matrix - 2count: 0=row_count, 1=column_count + // .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, From 47bef7a5572ef2a95293aa37bed653ba589cc9e8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 27 Oct 2021 23:34:56 +0100 Subject: [PATCH 05/17] Update doc-format --- core/odin/doc-format/doc_format.odin | 12 +++++++----- src/docs_format.cpp | 27 ++++++++++++++------------- src/docs_writer.cpp | 1 + 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index 090b520d8..8fa9f453c 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/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, diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 98ac13c8b..38cdd9508 100644 --- a/src/docs_format.cpp +++ b/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 { @@ -155,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<<29, - OdinDocEntityFlag_Var_Thread_Local = 1<<9, - OdinDocEntityFlag_Var_Static = 1<<10, + OdinDocEntityFlag_Var_Thread_Local = 1ull<<40, + OdinDocEntityFlag_Var_Static = 1ull<<41, }; struct OdinDocEntity { diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 25aa68167..1603ca22c 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -850,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 = {}; From 793a6479efea663d1873afd5151dfe035ae72322 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 27 Oct 2021 23:37:04 +0100 Subject: [PATCH 06/17] Fix typo --- src/docs_format.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 38cdd9508..4cdb19a68 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -167,7 +167,7 @@ enum OdinDocEntityFlag : u64 { OdinDocEntityFlag_Param_NoAlias = 1ull<<7, OdinDocEntityFlag_Param_AnyInt = 1ull<<8, - OdinDocEntityFlag_Type_Alias = 1ull<<29, + OdinDocEntityFlag_Type_Alias = 1ull<<20, OdinDocEntityFlag_Var_Thread_Local = 1ull<<40, OdinDocEntityFlag_Var_Static = 1ull<<41, From 90d587df13c31e0c1c10d73287a3368df7cfad00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 27 Oct 2021 23:49:37 +0100 Subject: [PATCH 07/17] Support `matrix` type in `core:odin` --- core/odin/ast/ast.odin | 17 +++++++++++ core/odin/ast/clone.odin | 9 +++++- core/odin/ast/walk.odin | 8 +++++ core/odin/parser/parser.odin | 56 ++++++++++++++++++++++++++-------- core/odin/tokenizer/token.odin | 14 +++++---- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 2ed5c2b53..6de82c5d4 100644 --- a/core/odin/ast/ast.odin +++ b/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, +} \ No newline at end of file diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index 143971a70..1e3058678 100644 --- a/core/odin/ast/clone.odin +++ b/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) } diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin index f394a5e33..d0d17cc9e 100644 --- a/core/odin/ast/walk.odin +++ b/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) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index d6935c925..7660005e0 100644 --- a/core/odin/parser/parser.odin +++ b/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 + 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 + 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 diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 43a8552fa..35cb566be 100644 --- a/core/odin/tokenizer/token.odin +++ b/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 From 5b7f27316509344e9c20f2901c8ad867e51a08a7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 28 Oct 2021 00:49:09 +0100 Subject: [PATCH 08/17] Add `matrix_type` to demo.odin --- examples/demo/demo.odin | 207 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index a035a0f5e..bc543b823 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -2203,6 +2203,212 @@ 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 instructions effectively on modern hardware + // + // 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 + // + // 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 +2444,6 @@ main :: proc() { or_else_operator() or_return_operator() arbitrary_precision_maths() + matrix_type() } } From 0a1ef1e59da47a15a04967a407ff3e8a7a2ffea4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 28 Oct 2021 00:50:03 +0100 Subject: [PATCH 09/17] Improve comment --- examples/demo/demo.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index bc543b823..96e770241 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -2367,7 +2367,8 @@ matrix_type :: proc() { // 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 instructions effectively on modern hardware + // 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). From 70793236abc278dd51ca577b35ca1757851380d3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 28 Oct 2021 00:57:10 +0100 Subject: [PATCH 10/17] Support `llvm_vector_reduce_add` if the LLVM intrinsic is not supported --- examples/demo/demo.odin | 4 +++- src/llvm_backend_utility.cpp | 35 ++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 96e770241..c287e4054 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -2373,7 +2373,9 @@ matrix_type :: proc() { // 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 + // 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 diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index e2249171c..eccf01319 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1567,6 +1567,10 @@ 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; @@ -1591,17 +1595,30 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) { } unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); - GB_ASSERT_MSG(id != 0, "Unable to find %s", name); + if (id != 0) { + 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; + } - LLVMTypeRef types[1] = {}; - types[0] = type; + // Manual reduce - 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; + 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; } LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) { From 3794d2417db51ba1e50fa9f3e0d7df973328e6b0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 28 Oct 2021 15:01:13 +0100 Subject: [PATCH 11/17] Write a `log(n)` fallback for `llvm_vector_reduce_add` This may be what LLVM does at any rate --- src/common.cpp | 12 ++++++- src/llvm_backend_utility.cpp | 66 +++++++++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index bebae6ab3..7af7026b9 100644 --- a/src/common.cpp +++ b/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; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index eccf01319..8ba23089a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1563,6 +1563,40 @@ 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) { + LLVMValueRef v_zero32 = lb_const_int(p->module, t_u32, 0).value; + unsigned len = LLVMGetVectorSize(LLVMTypeOf(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) { + LLVMValueRef lhs_mask = llvm_mask_iota(p->module, 0, i/2); + LLVMValueRef rhs_mask = llvm_mask_iota(p->module, i/2, i); + LLVMValueRef lhs = LLVMBuildShuffleVector(p->builder, value, LLVMGetUndef(LLVMTypeOf(value)), lhs_mask, ""); + LLVMValueRef rhs = LLVMBuildShuffleVector(p->builder, value, LLVMGetUndef(LLVMTypeOf(value)), rhs_mask, ""); + + 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); @@ -1571,11 +1605,11 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) { 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: @@ -1593,7 +1627,7 @@ 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)); if (id != 0) { LLVMTypeRef types[1] = {}; @@ -1606,9 +1640,9 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef 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, ""); @@ -1619,6 +1653,28 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) { } } 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 { + 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); + + return LLVMBuildBinOp(p->builder, op_code, lower_reduced, upper_reduced, ""); + } +#endif } LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) { From ef29ffeb21bbe7f84e5c0e71cdef546ccf292432 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 28 Oct 2021 15:09:20 +0100 Subject: [PATCH 12/17] Correct `llvm_vector_shuffle_reduction` --- src/llvm_backend_utility.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 8ba23089a..14fb8280e 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1564,18 +1564,26 @@ LLVMValueRef llvm_vector_broadcast(lbProcedure *p, LLVMValueRef value, unsigned } 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; - unsigned len = LLVMGetVectorSize(LLVMTypeOf(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) { - LLVMValueRef lhs_mask = llvm_mask_iota(p->module, 0, i/2); - LLVMValueRef rhs_mask = llvm_mask_iota(p->module, i/2, i); + 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, ""); } @@ -1629,7 +1637,7 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) { } unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); - if (id != 0) { + if (id != 0 && false) { LLVMTypeRef types[1] = {}; types[0] = type; @@ -1663,6 +1671,7 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) { 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, ""); @@ -1670,7 +1679,8 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) { 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); + 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, ""); } From a8425cfb47757e58b3b38e8a29f6d67af1dad651 Mon Sep 17 00:00:00 2001 From: Phil Date: Thu, 30 Sep 2021 09:49:00 -0700 Subject: [PATCH 13/17] Add a stopwatch to time.odin --- core/time/time.odin | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/core/time/time.odin b/core/time/time.odin index 1fcb20cf8..d1406e88e 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -43,6 +43,36 @@ Weekday :: enum int { Saturday, } +Stopwatch :: struct { + running: bool, + _start_time: Tick, + _stop_time: Duration, +} + +start :: proc(using stopwatch: ^Stopwatch) { + if !running { + _start_time = tick_now() + running = true + } +} + +stop :: proc(using stopwatch: ^Stopwatch) { + if running { + _stop_time += tick_diff(_start_time, tick_now()) + running = false + } +} + +reset :: proc(using stopwatch: ^Stopwatch) { + _stop_time = {} + running = false +} + +duration :: proc(using stopwatch: Stopwatch) -> Duration { + if !running { return _stop_time } + return _stop_time + 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) } From bbccf9ddbf65d639fd3c194b2e91f2d1110496d2 Mon Sep 17 00:00:00 2001 From: Phil Date: Sun, 3 Oct 2021 15:25:41 -0700 Subject: [PATCH 14/17] Add clock overloads for Duration and Stopwatch --- core/time/time.odin | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/core/time/time.odin b/core/time/time.odin index d1406e88e..7880b8da4 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -135,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 } @@ -148,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(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 @@ -166,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 { From e4ce0171834c6b4b5e35a7f26ac512698769775b Mon Sep 17 00:00:00 2001 From: Phil H Date: Thu, 28 Oct 2021 14:07:10 -0700 Subject: [PATCH 15/17] better naming conventions for stopwatch procedures + fields --- core/time/time.odin | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/time/time.odin b/core/time/time.odin index 7880b8da4..fddb09d85 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -46,31 +46,31 @@ Weekday :: enum int { Stopwatch :: struct { running: bool, _start_time: Tick, - _stop_time: Duration, + _accumulation: Duration, } -start :: proc(using stopwatch: ^Stopwatch) { +stopwatch_start :: proc(using stopwatch: ^Stopwatch) { if !running { _start_time = tick_now() running = true } } -stop :: proc(using stopwatch: ^Stopwatch) { +stopwatch_stop :: proc(using stopwatch: ^Stopwatch) { if running { - _stop_time += tick_diff(_start_time, tick_now()) + _accumulation += tick_diff(_start_time, tick_now()) running = false } } -reset :: proc(using stopwatch: ^Stopwatch) { - _stop_time = {} +stopwatch_reset :: proc(using stopwatch: ^Stopwatch) { + _accumulation = {} running = false } -duration :: proc(using stopwatch: Stopwatch) -> Duration { - if !running { return _stop_time } - return _stop_time + tick_diff(_start_time, tick_now()) +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 { @@ -171,7 +171,7 @@ clock_from_duration :: proc(d: Duration) -> (hour, min, sec: int) { } clock_from_stopwatch :: proc(s: Stopwatch) -> (hour, min, sec: int) { - return clock_from_duration(duration(s)) + return clock_from_duration(stopwatch_duration(s)) } clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) { From 7b8b2f042ccffc55ecdfa784a7374cd87d4f5dbc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 29 Oct 2021 00:13:22 +0100 Subject: [PATCH 16/17] Add debug type information for `matrix` --- src/llvm_backend_debug.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 83b6ac4ae..1d14de32e 100644 --- a/src/llvm_backend_debug.cpp +++ b/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)); From 87952fdb8ed56fcf926bea81e3247ff3c6395e31 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 29 Oct 2021 12:40:54 +0100 Subject: [PATCH 17/17] `big.Rat` (Experimental) --- core/math/big/api.odin | 14 + core/math/big/helpers.odin | 36 ++- core/math/big/internal.odin | 17 +- core/math/big/public.odin | 21 +- core/math/big/rat.odin | 540 ++++++++++++++++++++++++++++++++++++ 5 files changed, 614 insertions(+), 14 deletions(-) create mode 100644 core/math/big/rat.odin diff --git a/core/math/big/api.odin b/core/math/big/api.odin index 8c9d2e799..c9be04da0 100644 --- a/core/math/big/api.odin +++ b/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 { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index ceb3fcf60..6d13d32bb 100644 --- a/core/math/big/helpers.odin +++ b/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) + } +} diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index d8e5157a4..4702e76a3 100644 --- a/core/math/big/internal.odin +++ b/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`|. diff --git a/core/math/big/public.odin b/core/math/big/public.odin index f0fc16f36..2673a262f 100644 --- a/core/math/big/public.odin +++ b/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. diff --git a/core/math/big/rat.odin b/core/math/big/rat.odin new file mode 100644 index 000000000..8acd8c2c6 --- /dev/null +++ b/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 + 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<>= 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) +} \ No newline at end of file