big: Finish big ZII refactor.

This commit is contained in:
Jeroen van Rijn
2021-07-21 13:46:37 +02:00
parent d9efa6c8b5
commit 7648f2e655
8 changed files with 405 additions and 324 deletions

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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);

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)
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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}

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.
*/
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;
}
/*