diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 166088b4e..008b1eb08 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -46,13 +46,13 @@ demo :: proc() { as, bs, cs: string; err: Error; - a, err = init(4); + a, err = init(-512); defer destroy(a); as, err = itoa(a); fmt.printf("a: %v, err: %v\n\n", as, err); delete(as); - b, err = init(4); + b, err = init(42); defer destroy(b); bs, err = itoa(b); fmt.printf("b: %s, err: %v\n\n", bs, err); @@ -68,7 +68,7 @@ demo :: proc() { err = sub(c, a, b); fmt.printf("Error: %v\n", err); - as, err = itoa(a); + as, err = itoa(a, 8); bs, err = itoa(b); cs, err = itoa(c); fmt.printf("a: %v, bits: %v\n", as, count_bits(a)); diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin index 869bd3e26..3b6e108d7 100644 --- a/core/math/bigint/helpers.odin +++ b/core/math/bigint/helpers.odin @@ -88,6 +88,7 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) a.used = 0; a.sign = .Zero_or_Positive if n >= 0 else .Negative; n = abs(n); + for n != 0 { a.digit[a.used] = DIGIT(n) & _MASK; a.used += 1; @@ -96,7 +97,6 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) if minimize { shrink(a); } - _zero_unused(a); } diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin index 5c2a8aef2..8e6780904 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -114,6 +114,9 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> ( Radix defaults to 10. */ radix := radix if radix > 0 else 10; + if radix < 2 || radix > 64 { + return 0, .Invalid_Input; + } /* Early exit if we were given an empty buffer. @@ -123,7 +126,7 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> ( return 0, .Buffer_Overflow; } /* - Early exit if `Int` == 0 or the entire `Int` fits in a single radix digit. + 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)) { needed := 2 if is_neg(a) else 1; @@ -148,15 +151,51 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> ( return written, .OK; } + /* + Fast path for when `Int` fits within a `_WORD`. + */ + if a.used == 1 || a.used == 2 { + val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); + for val > 0 { + if available == 0 { + return written, .Buffer_Overflow; + } + + q := val / _WORD(radix); + buffer[written] = RADIX_TABLE[val - (q * _WORD(radix))]; + available -= 1; + written += 1; + + val = q; + } + if is_neg(a) { + if available == 0 { + return written, .Buffer_Overflow; + } + buffer[written] = '-'; + available -= 1; + written += 1; + } + /* + Reverse output. + */ + slice.reverse(buffer[:written]); + if zero_terminate { + if available == 0 { + return written, .Buffer_Overflow; + } + buffer[written] = 0; + written += 1; + } + return written, .OK; + } /* Fast path for radixes that are a power of two. */ if is_power_of_two(int(radix)) { - - - return len(buffer), .OK; + // return len(buffer), .OK; } return -1, .Unimplemented; @@ -170,8 +209,6 @@ int_to_cstring :: itoa_cstring; We size for `string`, not `cstring`. */ radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) { - t := a; - if radix < 2 || radix > 64 { return -1, .Invalid_Input; } @@ -180,22 +217,26 @@ radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) { return 1, .OK; } - t.sign = .Zero_or_Positive; - log: int; + /* + Calculate `log` on a temporary "copy" with its sign set to positive. + */ + t := &Int{ + used = a.used, + allocated = a.allocated, + sign = .Zero_or_Positive, + digit = a.digit, + }; - log, err = log_n(t, DIGIT(radix)); + size, err = log_n(t, DIGIT(radix)); if err != .OK { - return log, err; + return; } /* log truncates to zero, so we need to add one more, and one for `-` if negative. */ - if is_neg(a) { - return log + 2, .OK; - } else { - return log + 1, .OK; - } + size += 2 if is_neg(a) else 1; + return size, .OK; } /*