aboutsummaryrefslogtreecommitdiff
path: root/core/math
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2021-07-18 02:17:51 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2021-08-11 20:59:50 +0200
commit341e8a3c99b0fb402ad53cb4937c72efbf68e54c (patch)
treeb132bc5011d036b304c63ba998c618f6056c8c61 /core/math
parente3d8ac559de102a7600f54eacb8aba4b54b0e428 (diff)
bigint: itoa works for numbers <= 120 bits.
Diffstat (limited to 'core/math')
-rw-r--r--core/math/bigint/example.odin6
-rw-r--r--core/math/bigint/helpers.odin2
-rw-r--r--core/math/bigint/radix.odin73
3 files changed, 61 insertions, 20 deletions
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;
-
- log, err = log_n(t, DIGIT(radix));
+ /*
+ 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,
+ };
+
+ 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;
}
/*