From 7648f2e655b0e280bfb049eb02634d0f5cd136ac Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 Jul 2021 13:46:37 +0200 Subject: [PATCH] big: Finish big ZII refactor. --- core/math/big/basic.odin | 178 ++++++++++++--------- core/math/big/{bigint.odin => common.odin} | 32 ++-- core/math/big/compare.odin | 154 ++++++++++++------ core/math/big/example.odin | 6 +- core/math/big/helpers.odin | 147 ++++++++--------- core/math/big/log.odin | 27 ++-- core/math/big/logical.odin | 87 +++++----- core/math/big/radix.odin | 98 +++++++----- 8 files changed, 405 insertions(+), 324 deletions(-) rename core/math/big/{bigint.odin => common.odin} (87%) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 8a50543ca..4e923526a 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -25,11 +25,19 @@ import "core:intrinsics" */ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if dest == nil || a == nil || b == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) || !is_initialized(b) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return err; } + if err = clear_if_uninitialized(b); err != .None { + return err; + } + if err = clear_if_uninitialized(dest); err != .None { + return err; + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ /* Handle both negative or both positive. @@ -44,7 +52,7 @@ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { Subtract the one with the greater magnitude from the other. The result gets the sign of the one with the greater magnitude. */ - if cmp_mag(x, y) == .Less_Than { + if c, _ := cmp_mag(a, b); c == -1 { x, y = y, x; } @@ -60,11 +68,22 @@ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - if dest == nil || a == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return err; } + /* + Grow destination as required. + */ + if dest != a { + if err = grow(dest, a.used + 1); err != .None { + return err; + } + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ + /* Fast paths for destination and input Int being the same. */ @@ -72,46 +91,44 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow. */ - if is_pos(dest) && (dest.digit[0] + digit < _DIGIT_MAX) { + if p, _ := is_pos(dest); p && (dest.digit[0] + digit < _DIGIT_MAX) { dest.digit[0] += digit; - return .OK; + return .None; } /* Can be subtracted from dest.digit[0] without underflow. */ - if is_neg(a) && (dest.digit[0] > digit) { + if n, _ := is_neg(a); n && (dest.digit[0] > digit) { dest.digit[0] -= digit; - return .OK; + return .None; } } - /* - Grow destination as required. - */ - if err = grow(dest, a.used + 1); err != .OK { - return err; - } - /* If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit` */ - if is_neg(a) && (a.used > 1 || a.digit[0] >= digit) { + if n, _ := is_neg(a); n && (a.used > 1 || a.digit[0] >= digit) { /* Temporarily fix `a`'s sign. */ - t := a; - t.sign = .Zero_or_Positive; + a.sign = .Zero_or_Positive; /* dest = |a| - digit */ - err = sub(dest, t, digit); + if err = sub(dest, a, digit); err != .None { + /* + Restore a's sign. + */ + a.sign = .Negative; + return err; + } /* Restore sign and set `dest` sign. */ + a.sign = .Negative; dest.sign = .Negative; - clamp(dest); - return err; + return clamp(dest); } /* @@ -122,7 +139,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* If `a` is positive */ - if is_pos(a) { + if p, _ := is_pos(a); p { /* Add digits, use `carry`. */ @@ -166,9 +183,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - - return .OK; + return clamp(dest); } /* @@ -176,11 +191,19 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - if dest == nil || number == nil || decrease == nil { - return .Nil_Pointer_Passed; - } else if !(is_initialized(number) && is_initialized(decrease)) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(dest); err != .None { + return err; } + if err = clear_if_uninitialized(x); err != .None { + return err; + } + if err = clear_if_uninitialized(y); err != .None { + return err; + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ if x.sign != y.sign { /* @@ -195,12 +218,16 @@ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { Subtract a positive from a positive, OR negative from a negative. First, take the difference between their magnitudes, then... */ - if cmp_mag(x, y) == .Less_Than { + if c, _ := cmp_mag(x, y); c == -1 { /* The second has a larger magnitude. The result has the *opposite* sign from the first number. */ - dest.sign = .Negative if is_pos(x) else .Zero_or_Positive; + if p, _ := is_pos(x); p { + dest.sign = .Negative; + } else { + dest.sign = .Zero_or_Positive; + } x, y = y, x; } else { /* @@ -220,11 +247,21 @@ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - if dest == nil || a == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(dest); err != .None { + return err; } + /* + Grow destination as required. + */ + if dest != a { + if err = grow(dest, a.used + 1); err != .None { + return err; + } + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ /* Fast paths for destination and input Int being the same. @@ -233,31 +270,23 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Fast path for `dest` is negative and unsigned addition doesn't overflow the lowest digit. */ - if is_neg(dest) && (dest.digit[0] + digit < _DIGIT_MAX) { + if n, _ := is_neg(dest); n && (dest.digit[0] + digit < _DIGIT_MAX) { dest.digit[0] += digit; - return .OK; + return .None; } /* Can be subtracted from dest.digit[0] without underflow. */ - if is_pos(a) && (dest.digit[0] > digit) { + if p, _ := is_pos(a); p && (dest.digit[0] > digit) { dest.digit[0] -= digit; - return .OK; + return .None; } } - /* - Grow destination as required. - */ - err = grow(dest, a.used + 1); - if err != .OK { - return err; - } - /* If `a` is negative, just do an unsigned addition (with fudged signs). */ - if is_neg(a) { + if n, _ := is_neg(a); n { t := a; t.sign = .Zero_or_Positive; @@ -273,7 +302,9 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* if `a`<= digit, simply fix the single digit. */ - if a.used == 1 && (a.digit[0] <= digit || is_zero(a)) { + z, _ := is_zero(a); + + if a.used == 1 && (a.digit[0] <= digit) || z { dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit; dest.sign = .Negative; dest.used = 1; @@ -303,9 +334,7 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - - return .OK; + return clamp(dest); } /* @@ -320,10 +349,11 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if dest == nil || a == nil || b == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) || !is_initialized(b) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(x); err != .None { + return err; + } + if err = clear_if_uninitialized(y); err != .None { + return err; } old_used, min_used, max_used, i: int; @@ -336,10 +366,13 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { max_used = y.used; old_used = dest.used; - if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != .OK { + if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } dest.used = max_used + 1; + /* + All parameters have been initialized. + */ /* Zero the carry */ carry := DIGIT(0); @@ -393,9 +426,7 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - - return .OK; + return clamp(dest); } /* @@ -404,10 +435,11 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - if dest == nil || number == nil || decrease == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(number) || !is_initialized(decrease) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(x); err != .None { + return err; + } + if err = clear_if_uninitialized(y); err != .None { + return err; } old_used := dest.used; @@ -415,10 +447,13 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { max_used := x.used; i: int; - if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != .OK { + if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } dest.used = max_used; + /* + All parameters have been initialized. + */ borrow := DIGIT(0); @@ -465,6 +500,5 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - return .OK; + return clamp(dest); } \ No newline at end of file diff --git a/core/math/big/bigint.odin b/core/math/big/common.odin similarity index 87% rename from core/math/big/bigint.odin rename to core/math/big/common.odin index f2c9366ad..bcbfafaef 100644 --- a/core/math/big/bigint.odin +++ b/core/math/big/common.odin @@ -53,27 +53,21 @@ Int :: struct { sign: Sign, }; -Comparison_Flag :: enum i8 { - Less_Than = -1, - Equal = 0, - Greater_Than = 1, +/* + Errors are a strict superset of runtime.Allocation_Error. +*/ +Error :: enum byte { + None = 0, + Out_Of_Memory = 1, + Invalid_Pointer = 2, + Invalid_Argument = 3, - /* One of the numbers was uninitialized */ - Uninitialized = -127, -}; + Unknown_Error = 4, + Max_Iterations_Reached = 5, + Buffer_Overflow = 6, + Integer_Overflow = 7, -Error :: enum i8 { - OK = 0, - Unknown_Error = -1, - Out_of_Memory = -2, - Invalid_Input = -3, - Max_Iterations_Reached = -4, - Buffer_Overflow = -5, - Integer_Overflow = -6, - Nil_Pointer_Passed = -7, - Int_Not_Initialized = -8, - - Unimplemented = -127, + Unimplemented = 127, }; Primality_Flag :: enum u8 { diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index 142a3bfe3..43e872ce1 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -22,61 +22,83 @@ int_is_initialized :: proc(a: ^Int) -> bool { return raw.cap >= _MIN_DIGIT_COUNT; } -int_is_zero :: proc(a: ^Int) -> bool { - return is_initialized(a) && a.used == 0; -} - -int_is_positive :: proc(a: ^Int) -> bool { - return is_initialized(a) && a.sign == .Zero_or_Positive; -} - -int_is_negative :: proc(a: ^Int) -> bool { - return is_initialized(a) && a.sign == .Negative; -} - -int_is_even :: proc(a: ^Int) -> bool { - if is_initialized(a) { - if is_zero(a) { - return true; - } - if a.used > 0 && a.digit[0] & 1 == 0 { - return true; - } +int_is_zero :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; } - return false; + return a.used == 0, .None; } -int_is_odd :: proc(a: ^Int) -> bool { - if is_initialized(a) { - return !is_even(a); +int_is_positive :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; } - return false; + return a.sign == .Zero_or_Positive, .None; +} + +int_is_negative :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + return a.sign == .Negative, .None; +} + +int_is_even :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + + res, err = is_zero(a); + if err != .None { + return false, err; + } else if res == true { + return true, .None; + } + + res = false; + if a.used > 0 && a.digit[0] & 1 == 0 { + res = true; + } + return res, .None; +} + +int_is_odd :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + + res, err = is_even(a); + return !res, err; } platform_int_is_power_of_two :: proc(a: int) -> bool { return ((a) != 0) && (((a) & ((a) - 1)) == 0); } -int_is_power_of_two :: proc(a: ^Int) -> (res: bool) { +int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + /* Early out for Int == 0. */ if a.used == 0 { - return false; + return false, .None; } /* For an `Int` to be a power of two, its top limb has to be a power of two. */ if !platform_int_is_power_of_two(int(a.digit[a.used - 1])) { - return false; + return false, .None; } /* That was the only limb, so it's a power of two. */ if a.used == 1 { - return true; + return true, .None; } /* @@ -84,74 +106,102 @@ int_is_power_of_two :: proc(a: ^Int) -> (res: bool) { */ for i := 1; i < a.used; i += 1 { if a.digit[i - 1] != 0 { - return false; + return false, .None; } } - return true; + return true, .None; } /* Compare two `Int`s, signed. */ -int_compare :: proc(a, b: ^Int) -> Comparison_Flag { - if !is_initialized(a) { return .Uninitialized; } - if !is_initialized(b) { return .Uninitialized; } +int_compare :: proc(a, b: ^Int) -> (res: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } + if err = clear_if_uninitialized(b); err != .None { + return 0, err; + } + + neg: bool; + if neg, err = is_negative(a); err != .None { + return 0, err; + } /* Compare based on sign */ if a.sign != b.sign { - return .Less_Than if is_negative(a) else .Greater_Than; + res = -1 if neg else +1; + return res, .None; } - x, y := a, b; /* If negative, compare in the opposite direction */ - if is_neg(a) { - x, y = b, a; + if neg { + return cmp_mag(b, a); } - return cmp_mag(x, y); + return cmp_mag(a, b); } /* Compare an `Int` to an unsigned number upto the size of the backing type. */ -int_compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag { - if !is_initialized(a) { return .Uninitialized; } +int_compare_digit :: proc(a: ^Int, u: DIGIT) -> (res: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } /* Compare based on sign */ - if is_neg(a) { - return .Less_Than; + neg: bool; + if neg, err = is_neg(a); err != .None { + return 0, err; + } + if neg { + return -1, .None; } /* Compare based on magnitude */ if a.used > 1 { - return .Greater_Than; + return +1, .None; } /* Compare the only digit in `a` to `u`. */ if a.digit[0] != u { - return .Greater_Than if a.digit[0] > u else .Less_Than; + if a.digit[0] > u { + return +1, .None; + } + return -1, .None; } - return .Equal; + return 0, .None; } /* Compare the magnitude of two `Int`s, unsigned. */ -int_compare_magnitude :: proc(a, b: ^Int) -> Comparison_Flag { - if !is_initialized(a) { return .Uninitialized; } - if !is_initialized(b) { return .Uninitialized; } +int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } + if err = clear_if_uninitialized(b); err != .None { + return 0, err; + } /* Compare based on used digits */ if a.used != b.used { - return .Greater_Than if a.used > b.used else .Less_Than; + if a.used > b.used { + return +1, .None; + } + return -1, .None; } /* Same number of used digits, compare based on their value */ for n := a.used - 1; n >= 0; n -= 1 { if a.digit[n] != b.digit[n] { - return .Greater_Than if a.digit[n] > b.digit[n] else .Less_Than; + if a.digit[n] > b.digit[n] { + return +1, .None; + } + return -1, .None; } } - return .Equal; + return 0, .None; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 616cbc553..3c90719f1 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -44,7 +44,7 @@ _SQR_TOOM_CUTOFF, print :: proc(name: string, a: ^Int, base := i8(16)) { as, err := itoa(a, base); defer delete(as); - if err == .OK { + if err == .None { cb, _ := count_bits(a); fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); } else { @@ -80,10 +80,10 @@ demo :: proc() { print("c", c); fmt.println("\n\n=== Set a to (1 << 120) - 1 ==="); - if err = power_of_two(a, 120); err != .OK { + if err = power_of_two(a, 120); err != .None { fmt.printf("Error %v while setting a to 1 << 120.\n", err); } - if err = sub(a, a, 1); err != .OK { + if err = sub(a, a, 1); err != .None { fmt.printf("Error %v while subtracting 1 from a\n", err); } print("a", a, 16); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 5a2ea8962..af3d27e0f 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -30,15 +30,10 @@ int_destroy :: proc(integers: ..^Int) { int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) where intrinsics.type_is_integer(T) { src := src; - /* - Check that dest is usable. - */ - if dest == nil { - return .Nil_Pointer_Passed; + if err = clear_if_uninitialized(dest); err != .None { + return err; } - if err = _grow_if_uninitialized(dest, minimize); err != .OK { return err; } - dest.used = 0; dest.sign = .Zero_or_Positive if src >= 0 else .Negative; src = abs(src); @@ -49,7 +44,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : src >>= _DIGIT_BITS; } _zero_unused(dest); - return .OK; + return .None; } set :: proc { int_set_from_integer, int_copy }; @@ -58,27 +53,20 @@ set :: proc { int_set_from_integer, int_copy }; Copy one `Int` to another. */ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { - /* - Check that src and dest are usable. - */ - if dest == nil || src == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(src) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(src); err != .None { + return err; } - /* If dest == src, do nothing */ if (dest == src) { - return .OK; + return .None; } - /* Grow `dest` to fit `src`. If `dest` is not yet initialized, it will be using `allocator`. */ - if err = grow(dest, src.used, false, allocator); err != .OK { + if err = grow(dest, src.used, false, allocator); err != .None { return err; } @@ -91,7 +79,7 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error dest.used = src.used; dest.sign = src.sign; _zero_unused(dest); - return .OK; + return .None; } copy :: proc { int_copy, }; @@ -100,26 +88,23 @@ copy :: proc { int_copy, }; */ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* - Check that src and dest are usable. + Check that src is usable. */ - if dest == nil || src == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(src) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(src); err != .None { + return err; } - /* If `dest == src`, just fix `dest`'s sign. */ if (dest == src) { dest.sign = .Zero_or_Positive; - return .OK; + return .None; } /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != .OK { + if err = copy(dest, src, allocator); err != .None { return err; } @@ -127,7 +112,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) Fix sign. */ dest.sign = .Zero_or_Positive; - return .OK; + return .None; } platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { @@ -140,27 +125,29 @@ abs :: proc{int_abs, platform_abs}; */ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* - Check that src and dest are usable. + Check that src is usable. */ - if dest == nil || src == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(src) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(src); err != .None { + return err; } - /* If `dest == src`, just fix `dest`'s sign. */ - sign := Sign.Negative if !(is_zero(src) && is_neg(src)) else Sign.Zero_or_Positive; - if dest == src { - dest.sign = sign; - return .OK; + sign := Sign.Zero_or_Positive; + if z, _ := is_zero(src); z { + sign = .Negative; + } + if n, _ := is_neg(src); n { + sign = .Negative; + } + if (dest == src) { + dest.sign = sign; + return .None; } - /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != .OK { + if err = copy(dest, src, allocator); err != .None { return err; } @@ -168,7 +155,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { Fix sign. */ dest.sign = sign; - return .OK; + return .None; } /* @@ -176,22 +163,20 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { */ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { /* - Check that `a` is usable. + Check that `a`is usable. */ - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } limb := bit_offset / _DIGIT_BITS; if limb < 0 || limb >= a.used { - return 0, .Invalid_Input; + return 0, .Invalid_Argument; } i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); - return 1 if ((a.digit[limb] & i) != 0) else 0, .OK; + return 1 if ((a.digit[limb] & i) != 0) else 0, .None; } /* @@ -199,16 +184,14 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { */ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* - Check that `a` is usable. + Check that `a`is usable. */ - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } if count > _WORD_BITS || count < 1 { - return 0, .Invalid_Input; + return 0, .Invalid_Argument; } v: DIGIT; @@ -216,7 +199,7 @@ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { for shift := 0; shift < count; shift += 1 { o := offset + shift; v, e = extract_bit(a, o); - if e != .OK { + if e != .None { break; } res = res + _WORD(v) << uint(shift); @@ -230,7 +213,7 @@ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { */ shrink :: proc(a: ^Int) -> (err: Error) { if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } needed := max(_MIN_DIGIT_COUNT, a.used); @@ -238,12 +221,12 @@ shrink :: proc(a: ^Int) -> (err: Error) { if a.used != needed { return grow(a, needed); } - return .OK; + return .None; } int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } raw := transmute(mem.Raw_Dynamic_Array)a.digit; @@ -269,9 +252,9 @@ int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := conte Let's see if the allocation/resize worked as expected. */ if len(a.digit) != needed { - return .Out_of_Memory; + return .Out_Of_Memory; } - return .OK; + return .None; } grow :: proc { int_grow, }; @@ -280,7 +263,7 @@ grow :: proc { int_grow, }; */ int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } raw := transmute(mem.Raw_Dynamic_Array)a.digit; @@ -299,14 +282,14 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != .OK { + if err = clear(a, minimize, allocator); err != .None { return err; } a.used = 1; a.digit[0] = 1; a.sign = .Zero_or_Positive; - return .OK; + return .None; } one :: proc { int_one, }; @@ -314,14 +297,14 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != .OK { + if err = clear(a, minimize, allocator); err != .None { return err; } a.used = 1; a.digit[0] = 1; a.sign = .Negative; - return .OK; + return .None; } minus_one :: proc { int_minus_one, }; @@ -332,18 +315,18 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Check that `a` is usable. */ if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } if power < 0 || power > _MAX_BIT_COUNT { - return .Invalid_Input; + return .Invalid_Argument; } /* Grow to accomodate the single bit. */ a.used = (power / _DIGIT_BITS) + 1; - if err = grow(a, a.used); err != .OK { + if err = grow(a, a.used); err != .None { return err; } /* @@ -355,23 +338,21 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Set the bit. */ a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS)); - return .OK; + return .None; } /* Count bits in an `Int`. */ count_bits :: proc(a: ^Int) -> (count: int, err: Error) { - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } /* Fast path for zero. */ - if is_zero(a) { - return 0, .OK; + if z, _ := is_zero(a); z { + return 0, .None; } /* Get the number of DIGITs and use it. @@ -404,11 +385,11 @@ _zero_unused :: proc(a: ^Int) { } } -_grow_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { +clear_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { if !is_initialized(dest) { return grow(dest, _MIN_DIGIT_COUNT if minimize else _DEFAULT_DIGIT_COUNT); } - return .OK; + return .None; } /* @@ -418,19 +399,17 @@ _grow_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { Typically very fast. Also fixes the sign if there are no more leading digits. */ clamp :: proc(a: ^Int) -> (err: Error) { - if a == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return err; } for a.used > 0 && a.digit[a.used - 1] == 0 { a.used -= 1; } - if is_zero(a) { + if z, _ := is_zero(a); z { a.sign = .Zero_or_Positive; } - return .OK; + return .None; } \ No newline at end of file diff --git a/core/math/big/log.odin b/core/math/big/log.odin index 9c4fc3a2a..bb918f690 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -10,13 +10,18 @@ package big */ log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if base < 2 || DIGIT(base) > _DIGIT_MAX { + return -1, .Invalid_Argument; } - if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX { - return -1, .Invalid_Input; + + if err = clear_if_uninitialized(a); err != .None { + return -1, err; + } + if n, _ := is_neg(a); n { + return -1, .Invalid_Argument; + } + if z, _ := is_zero(a); z { + return -1, .Invalid_Argument; } /* @@ -80,14 +85,14 @@ log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { Therefore, we return 0. */ if a < base { - return 0, .OK; + return 0, .None; } /* If a number equals the base, the log is 1. */ if a == base { - return 1, .OK; + return 1, .None; } N := _WORD(a); @@ -116,13 +121,13 @@ log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { bracket_low = bracket_mid; } if N == bracket_mid { - return mid, .OK; + return mid, .None; } } if bracket_high == N { - return high, .OK; + return high, .None; } else { - return low, .OK; + return low, .None; } } \ No newline at end of file diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 6c857a2f2..47537d092 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -20,29 +20,33 @@ package big 2's complement `and`, returns `dest = a & b;` */ and :: proc(dest, a, b: ^Int) -> (err: Error) { - assert_initialized(dest); assert_initialized(a); assert_initialized(b); - + if err = clear_if_uninitialized(a); err != .None { + return err; + } + if err = clear_if_uninitialized(b); err != .None { + return err; + } used := max(a.used, b.used) + 1; - neg: bool; - - neg = is_neg(a) && is_neg(b); - - ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .OK { + if err = grow(dest, used); err != .None { return err; } + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a && neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + for i := 0; i < used; i += 1 { x, y: DIGIT; /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_a { ac += _MASK if i >= a.used else (~a.digit[i] & _MASK); x = ac & _MASK; ac >>= _DIGIT_BITS; @@ -53,7 +57,7 @@ and :: proc(dest, a, b: ^Int) -> (err: Error) { /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_b { bc += _MASK if i >= b.used else (~b.digit[i] & _MASK); y = bc & _MASK; bc >>= _DIGIT_BITS; @@ -75,37 +79,40 @@ and :: proc(dest, a, b: ^Int) -> (err: Error) { dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - clamp(dest); - return .OK; + return clamp(dest); } /* 2's complement `or`, returns `dest = a | b;` */ or :: proc(dest, a, b: ^Int) -> (err: Error) { - assert_initialized(dest); assert_initialized(a); assert_initialized(b); - + if err = clear_if_uninitialized(a); err != .None { + return err; + } + if err = clear_if_uninitialized(b); err != .None { + return err; + } used := max(a.used, b.used) + 1; - neg: bool; - - neg = is_neg(a) || is_neg(b); - - ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .OK { + if err = grow(dest, used); err != .None { return err; } + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a || neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + for i := 0; i < used; i += 1 { x, y: DIGIT; /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_a { ac += _MASK if i >= a.used else (~a.digit[i] & _MASK); x = ac & _MASK; ac >>= _DIGIT_BITS; @@ -116,7 +123,7 @@ or :: proc(dest, a, b: ^Int) -> (err: Error) { /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_b { bc += _MASK if i >= b.used else (~b.digit[i] & _MASK); y = bc & _MASK; bc >>= _DIGIT_BITS; @@ -138,37 +145,40 @@ or :: proc(dest, a, b: ^Int) -> (err: Error) { dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - clamp(dest); - return .OK; + return clamp(dest); } /* 2's complement `xor`, returns `dest = a ~ b;` */ xor :: proc(dest, a, b: ^Int) -> (err: Error) { - assert_initialized(dest); assert_initialized(a); assert_initialized(b); - + if err = clear_if_uninitialized(a); err != .None { + return err; + } + if err = clear_if_uninitialized(b); err != .None { + return err; + } used := max(a.used, b.used) + 1; - neg: bool; - - neg = is_neg(a) != is_neg(b); - - ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .OK { + if err = grow(dest, used); err != .None { return err; } + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a != neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + for i := 0; i < used; i += 1 { x, y: DIGIT; /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_a { ac += _MASK if i >= a.used else (~a.digit[i] & _MASK); x = ac & _MASK; ac >>= _DIGIT_BITS; @@ -179,7 +189,7 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) { /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_b { bc += _MASK if i >= b.used else (~b.digit[i] & _MASK); y = bc & _MASK; bc >>= _DIGIT_BITS; @@ -201,6 +211,5 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) { dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - clamp(dest); - return .OK; + return clamp(dest); } \ No newline at end of file diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 0dd07672c..8b4abc76d 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -19,8 +19,10 @@ import "core:strings" This version of `itoa` allocates one behalf of the caller. The caller must free the string. */ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { - radix := radix; - assert_initialized(a); + a := a; radix := radix; + if err = clear_if_uninitialized(a); err != .None { + return "", err; + } /* Radix defaults to 10. */ @@ -32,14 +34,13 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator */ /* - Calculate the size of the buffer we need. + Calculate the size of the buffer we need, and */ size: int; - size, err = radix_size(a, radix, zero_terminate); /* Exit if calculating the size returned an error. */ - if err != .OK { + if size, err = radix_size(a, radix, zero_terminate); err != .None { f := strings.clone(fallback(a), allocator); if zero_terminate { c := strings.clone_to_cstring(f); @@ -57,14 +58,13 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator Write the digits out into the buffer. */ written: int; - written, err = itoa_raw(a, radix, buffer, size, zero_terminate); + if written, err = itoa_raw(a, radix, buffer, size, zero_terminate); err == .None { + return string(buffer[:written]), .None; + } /* For now, delete the buffer and fall back to the below on failure. */ - if err == .OK { - return string(buffer[:written]), .OK; - } delete(buffer); fallback :: proc(a: ^Int, print_raw := false) -> string { @@ -86,8 +86,10 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator This version of `itoa` allocates one behalf of the caller. The caller must free the string. */ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) { - radix := radix; - assert_initialized(a); + a := a; radix := radix; + if err = clear_if_uninitialized(a); err != .None { + return "", err; + } /* Radix defaults to 10. */ @@ -119,21 +121,25 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) - and having to perform a buffer overflow check each character. */ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) { - radix := radix; - assert_initialized(a); size := size; + a := a; radix := radix; size := size; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } /* Radix defaults to 10. */ radix = radix if radix > 0 else 10; if radix < 2 || radix > 64 { - return 0, .Invalid_Input; + return 0, .Invalid_Argument; } /* We weren't given a size. Let's compute it. */ if size == -1 { - size, err = radix_size(a, radix, zero_terminate); + if size, err = radix_size(a, radix, zero_terminate); err != .None { + return 0, err; + } } /* @@ -146,7 +152,8 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina /* Fast path for when `Int` == 0 or the entire `Int` fits in a single radix digit. */ - if is_zero(a) || (a.used == 1 && a.digit[0] < DIGIT(radix)) { + z, _ := is_zero(a); + if z || (a.used == 1 && a.digit[0] < DIGIT(radix)) { if zero_terminate { available -= 1; buffer[available] = 0; @@ -154,12 +161,12 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina available -= 1; buffer[available] = RADIX_TABLE[a.digit[0]]; - if is_neg(a) { + if n, _ := is_neg(a); n { available -= 1; buffer[available] = '-'; } - return len(buffer) - available, .OK; + return len(buffer) - available, .None; } /* @@ -179,11 +186,11 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina val = q; } - if is_neg(a) { + if n, _ := is_neg(a); n { available -= 1; buffer[available] = '-'; } - return len(buffer) - available, .OK; + return len(buffer) - available, .None; } /* At least 3 DIGITs are in use if we made it this far. @@ -202,24 +209,23 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina // mask := _WORD(radix - 1); shift, err = log_n(DIGIT(radix), 2); count, err = count_bits(a); - // digit: _WORD; + digit: _WORD; for offset := 0; offset < count; offset += 4 { bits_to_get := int(min(count - offset, shift)); - digit, err := extract_bits(a, offset, bits_to_get); - if err != .OK { - return len(buffer) - available, .Invalid_Input; + if digit, err = extract_bits(a, offset, bits_to_get); err != .None { + return len(buffer) - available, .Invalid_Argument; } available -= 1; buffer[available] = RADIX_TABLE[digit]; } - if is_neg(a) { + if n, _ := is_neg(a); n { available -= 1; buffer[available] = '-'; } - return len(buffer) - available, .OK; + return len(buffer) - available, .None; } return -1, .Unimplemented; @@ -233,37 +239,41 @@ int_to_cstring :: itoa_cstring; We size for `string`, not `cstring`. */ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, err: Error) { + a := a; if radix < 2 || radix > 64 { - return -1, .Invalid_Input; + return -1, .Invalid_Argument; + } + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } - if is_zero(a) { - if zero_terminate { - return 2, .OK; - } - return 1, .OK; - } + if z, _ := is_zero(a); z { + if zero_terminate { + return 2, .None; + } + return 1, .None; + } - /* + /* Calculate `log` on a temporary "copy" with its sign set to positive. - */ + */ t := &Int{ used = a.used, sign = .Zero_or_Positive, digit = a.digit, }; - size, err = log_n(t, DIGIT(radix)); - if err != .OK { - return; - } + if size, err = log_n(t, DIGIT(radix)); err != .None { + return 0, err; + } - /* + /* log truncates to zero, so we need to add one more, and one for `-` if negative. - */ - size += 2 if is_neg(a) else 1; - size += 1 if zero_terminate else 0; - return size, .OK; + */ + n, _ := is_neg(a); + size += 2 if n else 1; + size += 1 if zero_terminate else 0; + return size, .None; } /*