big: Finish big ZII refactor.

This commit is contained in:
Jeroen van Rijn
2021-08-11 20:59:50 +02:00
parent d9efa6c8b5
commit 7648f2e655
8 changed files with 404 additions and 323 deletions
+106 -72
View File
@@ -25,11 +25,19 @@ import "core:intrinsics"
*/ */
int_add :: proc(dest, a, b: ^Int) -> (err: Error) { int_add :: proc(dest, a, b: ^Int) -> (err: Error) {
dest := dest; x := a; y := b; dest := dest; x := a; y := b;
if dest == nil || a == nil || b == nil { if err = clear_if_uninitialized(a); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(a) || !is_initialized(b) {
return .Int_Not_Initialized;
} }
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. 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. Subtract the one with the greater magnitude from the other.
The result gets the sign of the one with the greater magnitude. 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; 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) { int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
dest := dest; digit := digit; dest := dest; digit := digit;
if dest == nil || a == nil { if err = clear_if_uninitialized(a); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(a) {
return .Int_Not_Initialized;
} }
/*
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. 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. 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; dest.digit[0] += digit;
return .OK; return .None;
} }
/* /*
Can be subtracted from dest.digit[0] without underflow. 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; 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 `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. Temporarily fix `a`'s sign.
*/ */
t := a; a.sign = .Zero_or_Positive;
t.sign = .Zero_or_Positive;
/* /*
dest = |a| - digit 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. Restore sign and set `dest` sign.
*/ */
a.sign = .Negative;
dest.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 `a` is positive
*/ */
if is_pos(a) { if p, _ := is_pos(a); p {
/* /*
Add digits, use `carry`. 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. Adjust dest.used based on leading zeroes.
*/ */
clamp(dest); return clamp(dest);
return .OK;
} }
/* /*
@@ -176,11 +191,19 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
*/ */
int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) {
dest := dest; x := number; y := decrease; dest := dest; x := number; y := decrease;
if dest == nil || number == nil || decrease == nil { if err = clear_if_uninitialized(dest); err != .None {
return .Nil_Pointer_Passed; return err;
} 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;
}
/*
All parameters have been initialized.
We can now safely ignore errors from comparison routines.
*/
if x.sign != y.sign { 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. Subtract a positive from a positive, OR negative from a negative.
First, take the difference between their magnitudes, then... 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 second has a larger magnitude.
The result has the *opposite* sign from the first number. 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; x, y = y, x;
} else { } 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) { int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
dest := dest; digit := digit; dest := dest; digit := digit;
if dest == nil || a == nil { if err = clear_if_uninitialized(dest); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(a) {
return .Int_Not_Initialized;
} }
/*
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. 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. 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; dest.digit[0] += digit;
return .OK; return .None;
} }
/* /*
Can be subtracted from dest.digit[0] without underflow. 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; 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 `a` is negative, just do an unsigned addition (with fudged signs).
*/ */
if is_neg(a) { if n, _ := is_neg(a); n {
t := a; t := a;
t.sign = .Zero_or_Positive; 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`<= 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.digit[0] = digit - a.digit[0] if a.used == 1 else digit;
dest.sign = .Negative; dest.sign = .Negative;
dest.used = 1; 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. Adjust dest.used based on leading zeroes.
*/ */
clamp(dest); return clamp(dest);
return .OK;
} }
/* /*
@@ -320,10 +349,11 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
*/ */
_int_add :: proc(dest, a, b: ^Int) -> (err: Error) { _int_add :: proc(dest, a, b: ^Int) -> (err: Error) {
dest := dest; x := a; y := b; dest := dest; x := a; y := b;
if dest == nil || a == nil || b == nil { if err = clear_if_uninitialized(x); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(a) || !is_initialized(b) { }
return .Int_Not_Initialized; if err = clear_if_uninitialized(y); err != .None {
return err;
} }
old_used, min_used, max_used, i: int; 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; max_used = y.used;
old_used = dest.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; return err;
} }
dest.used = max_used + 1; dest.used = max_used + 1;
/*
All parameters have been initialized.
*/
/* Zero the carry */ /* Zero the carry */
carry := DIGIT(0); carry := DIGIT(0);
@@ -393,9 +426,7 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) {
/* /*
Adjust dest.used based on leading zeroes. Adjust dest.used based on leading zeroes.
*/ */
clamp(dest); return clamp(dest);
return .OK;
} }
/* /*
@@ -404,10 +435,11 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) {
*/ */
_int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) {
dest := dest; x := number; y := decrease; dest := dest; x := number; y := decrease;
if dest == nil || number == nil || decrease == nil { if err = clear_if_uninitialized(x); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(number) || !is_initialized(decrease) { }
return .Int_Not_Initialized; if err = clear_if_uninitialized(y); err != .None {
return err;
} }
old_used := dest.used; old_used := dest.used;
@@ -415,10 +447,13 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) {
max_used := x.used; max_used := x.used;
i: int; 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; return err;
} }
dest.used = max_used; dest.used = max_used;
/*
All parameters have been initialized.
*/
borrow := DIGIT(0); borrow := DIGIT(0);
@@ -465,6 +500,5 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) {
/* /*
Adjust dest.used based on leading zeroes. Adjust dest.used based on leading zeroes.
*/ */
clamp(dest); return clamp(dest);
return .OK;
} }
@@ -53,27 +53,21 @@ Int :: struct {
sign: Sign, sign: Sign,
}; };
Comparison_Flag :: enum i8 { /*
Less_Than = -1, Errors are a strict superset of runtime.Allocation_Error.
Equal = 0, */
Greater_Than = 1, Error :: enum byte {
None = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
/* One of the numbers was uninitialized */ Unknown_Error = 4,
Uninitialized = -127, Max_Iterations_Reached = 5,
}; Buffer_Overflow = 6,
Integer_Overflow = 7,
Error :: enum i8 { Unimplemented = 127,
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,
}; };
Primality_Flag :: enum u8 { Primality_Flag :: enum u8 {
+102 -52
View File
@@ -22,61 +22,83 @@ int_is_initialized :: proc(a: ^Int) -> bool {
return raw.cap >= _MIN_DIGIT_COUNT; return raw.cap >= _MIN_DIGIT_COUNT;
} }
int_is_zero :: proc(a: ^Int) -> bool { int_is_zero :: proc(a: ^Int) -> (res: bool, err: Error) {
return is_initialized(a) && a.used == 0; if err = clear_if_uninitialized(a); err != .None {
} return false, err;
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;
}
} }
return false; return a.used == 0, .None;
} }
int_is_odd :: proc(a: ^Int) -> bool { int_is_positive :: proc(a: ^Int) -> (res: bool, err: Error) {
if is_initialized(a) { if err = clear_if_uninitialized(a); err != .None {
return !is_even(a); 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 { platform_int_is_power_of_two :: proc(a: int) -> bool {
return ((a) != 0) && (((a) & ((a) - 1)) == 0); 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. Early out for Int == 0.
*/ */
if a.used == 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. 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])) { 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. That was the only limb, so it's a power of two.
*/ */
if a.used == 1 { 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 { for i := 1; i < a.used; i += 1 {
if a.digit[i - 1] != 0 { if a.digit[i - 1] != 0 {
return false; return false, .None;
} }
} }
return true; return true, .None;
} }
/* /*
Compare two `Int`s, signed. Compare two `Int`s, signed.
*/ */
int_compare :: proc(a, b: ^Int) -> Comparison_Flag { int_compare :: proc(a, b: ^Int) -> (res: int, err: Error) {
if !is_initialized(a) { return .Uninitialized; } if err = clear_if_uninitialized(a); err != .None {
if !is_initialized(b) { return .Uninitialized; } 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 */ /* Compare based on sign */
if a.sign != b.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 negative, compare in the opposite direction */
if is_neg(a) { if neg {
x, y = b, a; 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. Compare an `Int` to an unsigned number upto the size of the backing type.
*/ */
int_compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag { int_compare_digit :: proc(a: ^Int, u: DIGIT) -> (res: int, err: Error) {
if !is_initialized(a) { return .Uninitialized; } if err = clear_if_uninitialized(a); err != .None {
return 0, err;
}
/* Compare based on sign */ /* Compare based on sign */
if is_neg(a) { neg: bool;
return .Less_Than; if neg, err = is_neg(a); err != .None {
return 0, err;
}
if neg {
return -1, .None;
} }
/* Compare based on magnitude */ /* Compare based on magnitude */
if a.used > 1 { if a.used > 1 {
return .Greater_Than; return +1, .None;
} }
/* Compare the only digit in `a` to `u`. */ /* Compare the only digit in `a` to `u`. */
if a.digit[0] != 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. Compare the magnitude of two `Int`s, unsigned.
*/ */
int_compare_magnitude :: proc(a, b: ^Int) -> Comparison_Flag { int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) {
if !is_initialized(a) { return .Uninitialized; } if err = clear_if_uninitialized(a); err != .None {
if !is_initialized(b) { return .Uninitialized; } return 0, err;
}
if err = clear_if_uninitialized(b); err != .None {
return 0, err;
}
/* Compare based on used digits */ /* Compare based on used digits */
if a.used != b.used { 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 */ /* Same number of used digits, compare based on their value */
for n := a.used - 1; n >= 0; n -= 1 { for n := a.used - 1; n >= 0; n -= 1 {
if a.digit[n] != b.digit[n] { 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;
} }
+3 -3
View File
@@ -44,7 +44,7 @@ _SQR_TOOM_CUTOFF,
print :: proc(name: string, a: ^Int, base := i8(16)) { print :: proc(name: string, a: ^Int, base := i8(16)) {
as, err := itoa(a, base); as, err := itoa(a, base);
defer delete(as); defer delete(as);
if err == .OK { if err == .None {
cb, _ := count_bits(a); cb, _ := count_bits(a);
fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as);
} else { } else {
@@ -80,10 +80,10 @@ demo :: proc() {
print("c", c); print("c", c);
fmt.println("\n\n=== Set a to (1 << 120) - 1 ==="); 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); 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); fmt.printf("Error %v while subtracting 1 from a\n", err);
} }
print("a", a, 16); print("a", a, 16);
+62 -83
View File
@@ -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) int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error)
where intrinsics.type_is_integer(T) { where intrinsics.type_is_integer(T) {
src := src; src := src;
/* if err = clear_if_uninitialized(dest); err != .None {
Check that dest is usable. return err;
*/
if dest == nil {
return .Nil_Pointer_Passed;
} }
if err = _grow_if_uninitialized(dest, minimize); err != .OK { return err; }
dest.used = 0; dest.used = 0;
dest.sign = .Zero_or_Positive if src >= 0 else .Negative; dest.sign = .Zero_or_Positive if src >= 0 else .Negative;
src = abs(src); src = abs(src);
@@ -49,7 +44,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator :
src >>= _DIGIT_BITS; src >>= _DIGIT_BITS;
} }
_zero_unused(dest); _zero_unused(dest);
return .OK; return .None;
} }
set :: proc { int_set_from_integer, int_copy }; 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. Copy one `Int` to another.
*/ */
int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
/* if err = clear_if_uninitialized(src); err != .None {
Check that src and dest are usable. return err;
*/
if dest == nil || src == nil {
return .Nil_Pointer_Passed;
} else if !is_initialized(src) {
return .Int_Not_Initialized;
} }
/* /*
If dest == src, do nothing If dest == src, do nothing
*/ */
if (dest == src) { if (dest == src) {
return .OK; return .None;
} }
/* /*
Grow `dest` to fit `src`. Grow `dest` to fit `src`.
If `dest` is not yet initialized, it will be using `allocator`. 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; return err;
} }
@@ -91,7 +79,7 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error
dest.used = src.used; dest.used = src.used;
dest.sign = src.sign; dest.sign = src.sign;
_zero_unused(dest); _zero_unused(dest);
return .OK; return .None;
} }
copy :: proc { int_copy, }; copy :: proc { int_copy, };
@@ -100,26 +88,23 @@ copy :: proc { int_copy, };
*/ */
int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { 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 { if err = clear_if_uninitialized(src); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(src) {
return .Int_Not_Initialized;
} }
/* /*
If `dest == src`, just fix `dest`'s sign. If `dest == src`, just fix `dest`'s sign.
*/ */
if (dest == src) { if (dest == src) {
dest.sign = .Zero_or_Positive; dest.sign = .Zero_or_Positive;
return .OK; return .None;
} }
/* /*
Copy `src` to `dest` Copy `src` to `dest`
*/ */
if err = copy(dest, src, allocator); err != .OK { if err = copy(dest, src, allocator); err != .None {
return err; return err;
} }
@@ -127,7 +112,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error)
Fix sign. Fix sign.
*/ */
dest.sign = .Zero_or_Positive; dest.sign = .Zero_or_Positive;
return .OK; return .None;
} }
platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { 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) { 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 { if err = clear_if_uninitialized(src); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(src) {
return .Int_Not_Initialized;
} }
/* /*
If `dest == src`, just fix `dest`'s sign. If `dest == src`, just fix `dest`'s sign.
*/ */
sign := Sign.Negative if !(is_zero(src) && is_neg(src)) else Sign.Zero_or_Positive; sign := Sign.Zero_or_Positive;
if dest == src { if z, _ := is_zero(src); z {
sign = .Negative;
}
if n, _ := is_neg(src); n {
sign = .Negative;
}
if (dest == src) {
dest.sign = sign; dest.sign = sign;
return .OK; return .None;
} }
/* /*
Copy `src` to `dest` Copy `src` to `dest`
*/ */
if err = copy(dest, src, allocator); err != .OK { if err = copy(dest, src, allocator); err != .None {
return err; return err;
} }
@@ -168,7 +155,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
Fix sign. Fix sign.
*/ */
dest.sign = 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) { 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 { if err = clear_if_uninitialized(a); err != .None {
return 0, .Nil_Pointer_Passed; return 0, err;
} else if !is_initialized(a) {
return 0, .Int_Not_Initialized;
} }
limb := bit_offset / _DIGIT_BITS; limb := bit_offset / _DIGIT_BITS;
if limb < 0 || limb >= a.used { if limb < 0 || limb >= a.used {
return 0, .Invalid_Input; return 0, .Invalid_Argument;
} }
i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); 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) { 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 { if err = clear_if_uninitialized(a); err != .None {
return 0, .Nil_Pointer_Passed; return 0, err;
} else if !is_initialized(a) {
return 0, .Int_Not_Initialized;
} }
if count > _WORD_BITS || count < 1 { if count > _WORD_BITS || count < 1 {
return 0, .Invalid_Input; return 0, .Invalid_Argument;
} }
v: DIGIT; 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 { for shift := 0; shift < count; shift += 1 {
o := offset + shift; o := offset + shift;
v, e = extract_bit(a, o); v, e = extract_bit(a, o);
if e != .OK { if e != .None {
break; break;
} }
res = res + _WORD(v) << uint(shift); 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) { shrink :: proc(a: ^Int) -> (err: Error) {
if a == nil { if a == nil {
return .Nil_Pointer_Passed; return .Invalid_Pointer;
} }
needed := max(_MIN_DIGIT_COUNT, a.used); needed := max(_MIN_DIGIT_COUNT, a.used);
@@ -238,12 +221,12 @@ shrink :: proc(a: ^Int) -> (err: Error) {
if a.used != needed { if a.used != needed {
return grow(a, needed); return grow(a, needed);
} }
return .OK; return .None;
} }
int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) {
if a == nil { if a == nil {
return .Nil_Pointer_Passed; return .Invalid_Pointer;
} }
raw := transmute(mem.Raw_Dynamic_Array)a.digit; 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. Let's see if the allocation/resize worked as expected.
*/ */
if len(a.digit) != needed { if len(a.digit) != needed {
return .Out_of_Memory; return .Out_Of_Memory;
} }
return .OK; return .None;
} }
grow :: proc { int_grow, }; grow :: proc { int_grow, };
@@ -280,7 +263,7 @@ grow :: proc { int_grow, };
*/ */
int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
if a == nil { if a == nil {
return .Nil_Pointer_Passed; return .Invalid_Pointer;
} }
raw := transmute(mem.Raw_Dynamic_Array)a.digit; 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. 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) { 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; return err;
} }
a.used = 1; a.used = 1;
a.digit[0] = 1; a.digit[0] = 1;
a.sign = .Zero_or_Positive; a.sign = .Zero_or_Positive;
return .OK; return .None;
} }
one :: proc { int_one, }; 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. 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) { 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; return err;
} }
a.used = 1; a.used = 1;
a.digit[0] = 1; a.digit[0] = 1;
a.sign = .Negative; a.sign = .Negative;
return .OK; return .None;
} }
minus_one :: proc { int_minus_one, }; 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. Check that `a` is usable.
*/ */
if a == nil { if a == nil {
return .Nil_Pointer_Passed; return .Invalid_Pointer;
} }
if power < 0 || power > _MAX_BIT_COUNT { if power < 0 || power > _MAX_BIT_COUNT {
return .Invalid_Input; return .Invalid_Argument;
} }
/* /*
Grow to accomodate the single bit. Grow to accomodate the single bit.
*/ */
a.used = (power / _DIGIT_BITS) + 1; a.used = (power / _DIGIT_BITS) + 1;
if err = grow(a, a.used); err != .OK { if err = grow(a, a.used); err != .None {
return err; return err;
} }
/* /*
@@ -355,23 +338,21 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) {
Set the bit. Set the bit.
*/ */
a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS)); a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS));
return .OK; return .None;
} }
/* /*
Count bits in an `Int`. Count bits in an `Int`.
*/ */
count_bits :: proc(a: ^Int) -> (count: int, err: Error) { count_bits :: proc(a: ^Int) -> (count: int, err: Error) {
if a == nil { if err = clear_if_uninitialized(a); err != .None {
return 0, .Nil_Pointer_Passed; return 0, err;
} else if !is_initialized(a) {
return 0, .Int_Not_Initialized;
} }
/* /*
Fast path for zero. Fast path for zero.
*/ */
if is_zero(a) { if z, _ := is_zero(a); z {
return 0, .OK; return 0, .None;
} }
/* /*
Get the number of DIGITs and use it. 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) { if !is_initialized(dest) {
return grow(dest, _MIN_DIGIT_COUNT if minimize else _DEFAULT_DIGIT_COUNT); 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. Typically very fast. Also fixes the sign if there are no more leading digits.
*/ */
clamp :: proc(a: ^Int) -> (err: Error) { clamp :: proc(a: ^Int) -> (err: Error) {
if a == nil { if err = clear_if_uninitialized(a); err != .None {
return .Nil_Pointer_Passed; return err;
} else if !is_initialized(a) {
return .Int_Not_Initialized;
} }
for a.used > 0 && a.digit[a.used - 1] == 0 { for a.used > 0 && a.digit[a.used - 1] == 0 {
a.used -= 1; a.used -= 1;
} }
if is_zero(a) { if z, _ := is_zero(a); z {
a.sign = .Zero_or_Positive; a.sign = .Zero_or_Positive;
} }
return .OK; return .None;
} }
+16 -11
View File
@@ -10,13 +10,18 @@ package big
*/ */
log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) {
if a == nil { if base < 2 || DIGIT(base) > _DIGIT_MAX {
return 0, .Nil_Pointer_Passed; return -1, .Invalid_Argument;
} else if !is_initialized(a) {
return 0, .Int_Not_Initialized;
} }
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. Therefore, we return 0.
*/ */
if a < base { if a < base {
return 0, .OK; return 0, .None;
} }
/* /*
If a number equals the base, the log is 1. If a number equals the base, the log is 1.
*/ */
if a == base { if a == base {
return 1, .OK; return 1, .None;
} }
N := _WORD(a); N := _WORD(a);
@@ -116,13 +121,13 @@ log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) {
bracket_low = bracket_mid; bracket_low = bracket_mid;
} }
if N == bracket_mid { if N == bracket_mid {
return mid, .OK; return mid, .None;
} }
} }
if bracket_high == N { if bracket_high == N {
return high, .OK; return high, .None;
} else { } else {
return low, .OK; return low, .None;
} }
} }
+48 -39
View File
@@ -20,29 +20,33 @@ package big
2's complement `and`, returns `dest = a & b;` 2's complement `and`, returns `dest = a & b;`
*/ */
and :: proc(dest, a, b: ^Int) -> (err: Error) { 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; 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. Grow the destination to accomodate the result.
*/ */
if err = grow(dest, used); err != .OK { if err = grow(dest, used); err != .None {
return err; 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 { for i := 0; i < used; i += 1 {
x, y: DIGIT; x, y: DIGIT;
/* /*
Convert to 2's complement if negative. 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); ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK; x = ac & _MASK;
ac >>= _DIGIT_BITS; ac >>= _DIGIT_BITS;
@@ -53,7 +57,7 @@ and :: proc(dest, a, b: ^Int) -> (err: Error) {
/* /*
Convert to 2's complement if negative. 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); bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK; y = bc & _MASK;
bc >>= _DIGIT_BITS; bc >>= _DIGIT_BITS;
@@ -75,37 +79,40 @@ and :: proc(dest, a, b: ^Int) -> (err: Error) {
dest.used = used; dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive; dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest); return clamp(dest);
return .OK;
} }
/* /*
2's complement `or`, returns `dest = a | b;` 2's complement `or`, returns `dest = a | b;`
*/ */
or :: proc(dest, a, b: ^Int) -> (err: Error) { 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; 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. Grow the destination to accomodate the result.
*/ */
if err = grow(dest, used); err != .OK { if err = grow(dest, used); err != .None {
return err; 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 { for i := 0; i < used; i += 1 {
x, y: DIGIT; x, y: DIGIT;
/* /*
Convert to 2's complement if negative. 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); ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK; x = ac & _MASK;
ac >>= _DIGIT_BITS; ac >>= _DIGIT_BITS;
@@ -116,7 +123,7 @@ or :: proc(dest, a, b: ^Int) -> (err: Error) {
/* /*
Convert to 2's complement if negative. 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); bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK; y = bc & _MASK;
bc >>= _DIGIT_BITS; bc >>= _DIGIT_BITS;
@@ -138,37 +145,40 @@ or :: proc(dest, a, b: ^Int) -> (err: Error) {
dest.used = used; dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive; dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest); return clamp(dest);
return .OK;
} }
/* /*
2's complement `xor`, returns `dest = a ~ b;` 2's complement `xor`, returns `dest = a ~ b;`
*/ */
xor :: proc(dest, a, b: ^Int) -> (err: Error) { 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; 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. Grow the destination to accomodate the result.
*/ */
if err = grow(dest, used); err != .OK { if err = grow(dest, used); err != .None {
return err; 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 { for i := 0; i < used; i += 1 {
x, y: DIGIT; x, y: DIGIT;
/* /*
Convert to 2's complement if negative. 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); ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK; x = ac & _MASK;
ac >>= _DIGIT_BITS; ac >>= _DIGIT_BITS;
@@ -179,7 +189,7 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) {
/* /*
Convert to 2's complement if negative. 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); bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK; y = bc & _MASK;
bc >>= _DIGIT_BITS; bc >>= _DIGIT_BITS;
@@ -201,6 +211,5 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) {
dest.used = used; dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive; dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest); return clamp(dest);
return .OK;
} }
+54 -44
View File
@@ -19,8 +19,10 @@ import "core:strings"
This version of `itoa` allocates one behalf of the caller. The caller must free the string. 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) { itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
radix := radix; a := a; radix := radix;
assert_initialized(a); if err = clear_if_uninitialized(a); err != .None {
return "", err;
}
/* /*
Radix defaults to 10. 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: int;
size, err = radix_size(a, radix, zero_terminate);
/* /*
Exit if calculating the size returned an error. 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); f := strings.clone(fallback(a), allocator);
if zero_terminate { if zero_terminate {
c := strings.clone_to_cstring(f); 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. Write the digits out into the buffer.
*/ */
written: int; 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. For now, delete the buffer and fall back to the below on failure.
*/ */
if err == .OK {
return string(buffer[:written]), .OK;
}
delete(buffer); delete(buffer);
fallback :: proc(a: ^Int, print_raw := false) -> string { 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. 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) { itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) {
radix := radix; a := a; radix := radix;
assert_initialized(a); if err = clear_if_uninitialized(a); err != .None {
return "", err;
}
/* /*
Radix defaults to 10. 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. 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) { itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
radix := radix; a := a; radix := radix; size := size;
assert_initialized(a); size := size; if err = clear_if_uninitialized(a); err != .None {
return 0, err;
}
/* /*
Radix defaults to 10. Radix defaults to 10.
*/ */
radix = radix if radix > 0 else 10; radix = radix if radix > 0 else 10;
if radix < 2 || radix > 64 { if radix < 2 || radix > 64 {
return 0, .Invalid_Input; return 0, .Invalid_Argument;
} }
/* /*
We weren't given a size. Let's compute it. We weren't given a size. Let's compute it.
*/ */
if size == -1 { 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. 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 { if zero_terminate {
available -= 1; available -= 1;
buffer[available] = 0; buffer[available] = 0;
@@ -154,12 +161,12 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
available -= 1; available -= 1;
buffer[available] = RADIX_TABLE[a.digit[0]]; buffer[available] = RADIX_TABLE[a.digit[0]];
if is_neg(a) { if n, _ := is_neg(a); n {
available -= 1; available -= 1;
buffer[available] = '-'; 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; val = q;
} }
if is_neg(a) { if n, _ := is_neg(a); n {
available -= 1; available -= 1;
buffer[available] = '-'; 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. 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); // mask := _WORD(radix - 1);
shift, err = log_n(DIGIT(radix), 2); shift, err = log_n(DIGIT(radix), 2);
count, err = count_bits(a); count, err = count_bits(a);
// digit: _WORD; digit: _WORD;
for offset := 0; offset < count; offset += 4 { for offset := 0; offset < count; offset += 4 {
bits_to_get := int(min(count - offset, shift)); bits_to_get := int(min(count - offset, shift));
digit, err := extract_bits(a, offset, bits_to_get); if digit, err = extract_bits(a, offset, bits_to_get); err != .None {
if err != .OK { return len(buffer) - available, .Invalid_Argument;
return len(buffer) - available, .Invalid_Input;
} }
available -= 1; available -= 1;
buffer[available] = RADIX_TABLE[digit]; buffer[available] = RADIX_TABLE[digit];
} }
if is_neg(a) { if n, _ := is_neg(a); n {
available -= 1; available -= 1;
buffer[available] = '-'; buffer[available] = '-';
} }
return len(buffer) - available, .OK; return len(buffer) - available, .None;
} }
return -1, .Unimplemented; return -1, .Unimplemented;
@@ -233,37 +239,41 @@ int_to_cstring :: itoa_cstring;
We size for `string`, not `cstring`. We size for `string`, not `cstring`.
*/ */
radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, err: Error) { radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, err: Error) {
a := a;
if radix < 2 || radix > 64 { 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 z, _ := is_zero(a); z {
if zero_terminate { if zero_terminate {
return 2, .OK; return 2, .None;
} }
return 1, .OK; return 1, .None;
} }
/* /*
Calculate `log` on a temporary "copy" with its sign set to positive. Calculate `log` on a temporary "copy" with its sign set to positive.
*/ */
t := &Int{ t := &Int{
used = a.used, used = a.used,
sign = .Zero_or_Positive, sign = .Zero_or_Positive,
digit = a.digit, digit = a.digit,
}; };
size, err = log_n(t, DIGIT(radix)); if size, err = log_n(t, DIGIT(radix)); err != .None {
if err != .OK { return 0, err;
return; }
}
/* /*
log truncates to zero, so we need to add one more, and one for `-` if negative. log truncates to zero, so we need to add one more, and one for `-` if negative.
*/ */
size += 2 if is_neg(a) else 1; n, _ := is_neg(a);
size += 1 if zero_terminate else 0; size += 2 if n else 1;
return size, .OK; size += 1 if zero_terminate else 0;
return size, .None;
} }
/* /*