mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-07 16:18:52 +00:00
big: Add atoi.
This commit is contained in:
@@ -13,7 +13,6 @@ package big
|
|||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===========================
|
===========================
|
||||||
User-level routines
|
User-level routines
|
||||||
@@ -68,11 +67,10 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
|||||||
/*
|
/*
|
||||||
Grow destination as required.
|
Grow destination as required.
|
||||||
*/
|
*/
|
||||||
if dest != a {
|
|
||||||
if err = grow(dest, a.used + 1); err != .None {
|
if err = grow(dest, a.used + 1); err != .None {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
All parameters have been initialized.
|
All parameters have been initialized.
|
||||||
We can now safely ignore errors from comparison routines.
|
We can now safely ignore errors from comparison routines.
|
||||||
@@ -87,14 +85,16 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
|||||||
*/
|
*/
|
||||||
if p, _ := is_pos(dest); p && (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 .None;
|
dest.used += 1;
|
||||||
|
return clamp(dest);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Can be subtracted from dest.digit[0] without underflow.
|
Can be subtracted from dest.digit[0] without underflow.
|
||||||
*/
|
*/
|
||||||
if n, _ := is_neg(a); n && (dest.digit[0] > digit) {
|
if n, _ := is_neg(a); n && (dest.digit[0] > digit) {
|
||||||
dest.digit[0] -= digit;
|
dest.digit[0] -= digit;
|
||||||
return .None;
|
dest.used += 1;
|
||||||
|
return clamp(dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,7 +561,6 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) {
|
|||||||
*/
|
*/
|
||||||
dest.digit[ix] = DIGIT(carry);
|
dest.digit[ix] = DIGIT(carry);
|
||||||
dest.used = src.used + 1;
|
dest.used = src.used + 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Zero unused digits.
|
Zero unused digits.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ _SQR_TOOM_CUTOFF,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
print :: proc(name: string, a: ^Int, base := i8(16)) {
|
print :: proc(name: string, a: ^Int, base := i8(10)) {
|
||||||
as, err := itoa(a, base);
|
as, err := itoa(a, base);
|
||||||
defer delete(as);
|
defer delete(as);
|
||||||
cb, _ := count_bits(a);
|
cb, _ := count_bits(a);
|
||||||
@@ -67,12 +67,11 @@ demo :: proc() {
|
|||||||
destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
|
destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
|
||||||
defer destroy(destination, source, quotient, remainder, numerator, denominator);
|
defer destroy(destination, source, quotient, remainder, numerator, denominator);
|
||||||
|
|
||||||
err = set (numerator, 3);
|
err = set (numerator, 2);
|
||||||
err = set (denominator, 2);
|
err = set (denominator, 1);
|
||||||
err = set (quotient, 5);
|
err = set (quotient, u128(1 << 120));
|
||||||
err = zero(remainder);
|
err = zero(remainder);
|
||||||
|
err = pow(remainder, numerator, 120);
|
||||||
err = mulmod(remainder, numerator, denominator, quotient);
|
|
||||||
if err != .None {
|
if err != .None {
|
||||||
fmt.printf("Error: %v\n", err);
|
fmt.printf("Error: %v\n", err);
|
||||||
} else {
|
} else {
|
||||||
@@ -81,6 +80,21 @@ demo :: proc() {
|
|||||||
print("quotient ", quotient, 10);
|
print("quotient ", quotient, 10);
|
||||||
print("remainder ", remainder, 10);
|
print("remainder ", remainder, 10);
|
||||||
}
|
}
|
||||||
|
if c, _ := cmp(quotient, remainder); c == 0 {
|
||||||
|
fmt.println("c == r");
|
||||||
|
} else {
|
||||||
|
fmt.println("c != r");
|
||||||
|
}
|
||||||
|
|
||||||
|
foozle := "-1329227995784915872903807060280344576";
|
||||||
|
err = atoi(destination, foozle, 10);
|
||||||
|
if err != .None {
|
||||||
|
fmt.printf("Error %v while parsing `%v`", err, foozle);
|
||||||
|
} else {
|
||||||
|
print("destination", destination);
|
||||||
|
err = add(remainder, remainder, destination);
|
||||||
|
print("remainder + destination", remainder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import "core:mem"
|
|||||||
/*
|
/*
|
||||||
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) {
|
int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
|
||||||
a := a; radix := radix;
|
a := a; radix := radix;
|
||||||
if err = clear_if_uninitialized(a); err != .None {
|
if err = clear_if_uninitialized(a); err != .None {
|
||||||
return "", err;
|
return "", err;
|
||||||
@@ -52,7 +52,7 @@ 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);
|
written, err = int_itoa_raw(a, radix, buffer, size, zero_terminate);
|
||||||
|
|
||||||
return string(buffer[:written]), err;
|
return string(buffer[:written]), err;
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ 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) {
|
int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) {
|
||||||
a := a; radix := radix;
|
a := a; radix := radix;
|
||||||
if err = clear_if_uninitialized(a); err != .None {
|
if err = clear_if_uninitialized(a); err != .None {
|
||||||
return "", err;
|
return "", err;
|
||||||
@@ -71,7 +71,7 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -
|
|||||||
radix = radix if radix > 0 else 10;
|
radix = radix if radix > 0 else 10;
|
||||||
|
|
||||||
s: string;
|
s: string;
|
||||||
s, err = itoa_string(a, radix, true, allocator);
|
s, err = int_itoa_string(a, radix, true, allocator);
|
||||||
return cstring(raw_data(s)), err;
|
return cstring(raw_data(s)), err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -
|
|||||||
it'll result in buffer overflows, as we use it to avoid reversing at the end
|
it'll result in buffer overflows, as we use it to avoid reversing at the end
|
||||||
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) {
|
int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
|
||||||
a := a; radix := radix; size := size;
|
a := a; radix := radix; size := size;
|
||||||
if err = clear_if_uninitialized(a); err != .None {
|
if err = clear_if_uninitialized(a); err != .None {
|
||||||
return 0, err;
|
return 0, err;
|
||||||
@@ -228,11 +228,96 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
|
|||||||
return _itoa_raw_full(a, radix, buffer, zero_terminate);
|
return _itoa_raw_full(a, radix, buffer, zero_terminate);
|
||||||
}
|
}
|
||||||
|
|
||||||
itoa :: proc{itoa_string, itoa_raw};
|
itoa :: proc{int_itoa_string, int_itoa_raw};
|
||||||
int_to_string :: itoa;
|
int_to_string :: int_itoa_string;
|
||||||
int_to_cstring :: itoa_cstring;
|
int_to_cstring :: int_itoa_cstring;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read a string [ASCII] in a given radix.
|
||||||
|
*/
|
||||||
|
int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) {
|
||||||
|
input := input;
|
||||||
|
/*
|
||||||
|
Make sure the radix is ok.
|
||||||
|
*/
|
||||||
|
if radix < 2 || radix > 64 {
|
||||||
|
return .Invalid_Argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set the integer to the default of zero.
|
||||||
|
*/
|
||||||
|
if err = zero(res); err != .None { return err; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
We'll interpret an empty string as zero.
|
||||||
|
*/
|
||||||
|
if len(input) == 0 {
|
||||||
|
return .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the leading digit is a minus set the sign to negative.
|
||||||
|
Given the above early out, the length should be at least 1.
|
||||||
|
*/
|
||||||
|
sign := Sign.Zero_or_Positive;
|
||||||
|
if input[0] == '-' {
|
||||||
|
input = input[1:];
|
||||||
|
sign = .Negative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Process each digit of the string.
|
||||||
|
*/
|
||||||
|
ch: rune;
|
||||||
|
for len(input) > 0 {
|
||||||
|
/* if the radix <= 36 the conversion is case insensitive
|
||||||
|
* this allows numbers like 1AB and 1ab to represent the same value
|
||||||
|
* [e.g. in hex]
|
||||||
|
*/
|
||||||
|
|
||||||
|
ch = rune(input[0]);
|
||||||
|
if radix <= 36 && ch >= 'a' && ch <= 'z' {
|
||||||
|
ch += 'a' - 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := ch - '+';
|
||||||
|
if RADIX_TABLE_REVERSE_SIZE <= pos {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
y := RADIX_TABLE_REVERSE[pos];
|
||||||
|
/* if the char was found in the map
|
||||||
|
* and is less than the given radix add it
|
||||||
|
* to the number, otherwise exit the loop.
|
||||||
|
*/
|
||||||
|
if y >= u8(radix) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = mul(res, res, DIGIT(radix)); err != .None { return err; }
|
||||||
|
if err = add(res, res, DIGIT(y)); err != .None { return err; }
|
||||||
|
|
||||||
|
input = input[1:];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If an illegal character was found, fail.
|
||||||
|
*/
|
||||||
|
if len(input) > 0 && ch != 0 && ch != '\r' && ch != '\n' {
|
||||||
|
return .Invalid_Argument;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Set the sign only if res != 0.
|
||||||
|
*/
|
||||||
|
if res.used > 0 {
|
||||||
|
res.sign = sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
atoi :: proc { int_atoi, };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We size for `string` by default.
|
We size for `string` by default.
|
||||||
@@ -331,7 +416,7 @@ _log_bases :: [65]u32{
|
|||||||
Characters used in radix conversions.
|
Characters used in radix conversions.
|
||||||
*/
|
*/
|
||||||
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
|
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
|
||||||
RADIX_TABLE_REVERSE := [80]u8{
|
RADIX_TABLE_REVERSE := [RADIX_TABLE_REVERSE_SIZE]u8{
|
||||||
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
|
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
|
||||||
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
|
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
|
||||||
0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */
|
0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */
|
||||||
@@ -341,6 +426,7 @@ RADIX_TABLE_REVERSE := [80]u8{
|
|||||||
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
|
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
|
||||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
|
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
|
||||||
};
|
};
|
||||||
|
RADIX_TABLE_REVERSE_SIZE :: 80;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Stores a bignum as a ASCII string in a given radix (2..64)
|
Stores a bignum as a ASCII string in a given radix (2..64)
|
||||||
|
|||||||
Reference in New Issue
Block a user