bigint: refactor to big.Int instead of bigint.Int.

This commit is contained in:
Jeroen van Rijn
2021-08-11 20:59:50 +02:00
parent baef0c291d
commit 9dba17cf87
11 changed files with 834 additions and 715 deletions
+454
View File
@@ -0,0 +1,454 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains basic arithmetic operations like `add` and `sub`.
*/
import "core:mem"
import "core:intrinsics"
/*
===========================
User-level routines
===========================
*/
/*
High-level addition. Handles sign.
*/
add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) {
dest := dest; x := a; y := b;
assert_initialized(dest); assert_initialized(a); assert_initialized(b);
/*
Handle both negative or both positive.
*/
if x.sign == y.sign {
dest.sign = x.sign;
return _add(dest, x, y);
}
/*
One positive, the other negative.
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 {
x, y = y, x;
}
dest.sign = x.sign;
return _sub(dest, x, y);
}
/*
Adds the unsigned `DIGIT` immediate to an `Int`,
such that the `DIGIT` doesn't have to be turned into an `Int` first.
dest = a + digit;
*/
add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
dest := dest; digit := digit;
assert_initialized(dest); assert_initialized(a);
/*
Fast paths for destination and input Int being the same.
*/
if dest == a {
/*
Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow.
*/
if is_pos(dest) && (dest.digit[0] + digit < _DIGIT_MAX) {
dest.digit[0] += digit;
return .OK;
}
/*
Can be subtracted from dest.digit[0] without underflow.
*/
if is_neg(a) && (dest.digit[0] > digit) {
dest.digit[0] -= digit;
return .OK;
}
}
/*
Grow destination as required.
*/
err = grow(dest, a.used + 1);
if 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) {
/*
Temporarily fix `a`'s sign.
*/
t := a;
t.sign = .Zero_or_Positive;
/*
dest = |a| - digit
*/
err = sub(dest, t, digit);
/*
Restore sign and set `dest` sign.
*/
dest.sign = .Negative;
clamp(dest);
return err;
}
/*
Remember the currently used number of digits in `dest`.
*/
old_used := dest.used;
/*
If `a` is positive
*/
if is_pos(a) {
/*
Add digits, use `carry`.
*/
i: int;
carry := digit;
for i = 0; i < a.used; i += 1 {
dest.digit[i] = a.digit[i] + carry;
carry = dest.digit[i] >> _DIGIT_BITS;
dest.digit[i] &= _MASK;
}
/*
Set final carry.
*/
dest.digit[i] = carry;
/*
Set `dest` size.
*/
dest.used = a.used + 1;
} else {
/*
`a` was negative and |a| < digit.
*/
dest.used = 1;
/*
The result is a single DIGIT.
*/
dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit;
}
/*
Sign is always positive.
*/
dest.sign = .Zero_or_Positive;
zero_count := old_used - dest.used;
/*
Zero remainder.
*/
if zero_count > 0 {
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
}
/*
Adjust dest.used based on leading zeroes.
*/
clamp(dest);
return .OK;
}
add :: proc{add_two_ints, add_digit};
/*
High-level subtraction, dest = number - decrease. Handles signs.
*/
sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) {
dest := dest; x := number; y := decrease;
assert_initialized(number); assert_initialized(decrease); assert_initialized(dest);
if x.sign != y.sign {
/*
Subtract a negative from a positive, OR subtract a positive from a negative.
In either case, ADD their magnitudes and use the sign of the first number.
*/
dest.sign = x.sign;
return _add(dest, x, y);
}
/*
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 {
/*
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;
x, y = y, x;
} else {
/*
The first has a larger or equal magnitude.
Copy the sign from the first.
*/
dest.sign = x.sign;
}
return _sub(dest, x, y);
}
/*
Adds the unsigned `DIGIT` immediate to an `Int`,
such that the `DIGIT` doesn't have to be turned into an `Int` first.
dest = a - digit;
*/
sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
dest := dest; digit := digit;
assert_initialized(dest); assert_initialized(a);
/*
Fast paths for destination and input Int being the same.
*/
if dest == a {
/*
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) {
dest.digit[0] += digit;
return .OK;
}
/*
Can be subtracted from dest.digit[0] without underflow.
*/
if is_pos(a) && (dest.digit[0] > digit) {
dest.digit[0] -= digit;
return .OK;
}
}
/*
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) {
t := a;
t.sign = .Zero_or_Positive;
err = add(dest, t, digit);
dest.sign = .Negative;
clamp(dest);
return err;
}
old_used := dest.used;
/*
if `a`<= digit, simply fix the single digit.
*/
if a.used == 1 && (a.digit[0] <= digit || is_zero(a)) {
dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit;
dest.sign = .Negative;
dest.used = 1;
} else {
dest.sign = .Zero_or_Positive;
dest.used = a.used;
/*
Subtract with carry.
*/
carry := digit;
for i := 0; i < a.used; i += 1 {
dest.digit[i] = a.digit[i] - carry;
carry := dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
dest.digit[i] &= _MASK;
}
}
zero_count := old_used - dest.used;
/*
Zero remainder.
*/
if zero_count > 0 {
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
}
/*
Adjust dest.used based on leading zeroes.
*/
clamp(dest);
return .OK;
}
sub :: proc{sub_two_ints, sub_digit};
/*
==========================
Low-level routines
==========================
*/
/*
Low-level addition, unsigned.
Handbook of Applied Cryptography, algorithm 14.7.
*/
_add :: proc(dest, a, b: ^Int) -> (err: Error) {
dest := dest; x := a; y := b;
assert_initialized(a); assert_initialized(b); assert_initialized(dest);
old_used, min_used, max_used, i: int;
if x.used < y.used {
x, y = y, x;
}
min_used = x.used;
max_used = y.used;
old_used = dest.used;
err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT));
if err != .OK {
return err;
}
dest.used = max_used + 1;
/* Zero the carry */
carry := DIGIT(0);
for i = 0; i < min_used; i += 1 {
/*
Compute the sum one _DIGIT at a time.
dest[i] = a[i] + b[i] + carry;
*/
dest.digit[i] = x.digit[i] + y.digit[i] + carry;
/*
Compute carry
*/
carry = dest.digit[i] >> _DIGIT_BITS;
/*
Mask away carry from result digit.
*/
dest.digit[i] &= _MASK;
}
if min_used != max_used {
/*
Now copy higher words, if any, in A+B.
If A or B has more digits, add those in.
*/
for ; i < max_used; i += 1 {
dest.digit[i] = x.digit[i] + carry;
/*
Compute carry
*/
carry = dest.digit[i] >> _DIGIT_BITS;
/*
Mask away carry from result digit.
*/
dest.digit[i] &= _MASK;
}
}
/*
Add remaining carry.
*/
dest.digit[i] = carry;
zero_count := old_used - dest.used;
/*
Zero remainder.
*/
if zero_count > 0 {
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
}
/*
Adjust dest.used based on leading zeroes.
*/
clamp(dest);
return .OK;
}
/*
Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|.
Handbook of Applied Cryptography, algorithm 14.9.
*/
_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) {
dest := dest; x := number; y := decrease;
assert_initialized(number); assert_initialized(decrease); assert_initialized(dest);
old_used := dest.used;
min_used := y.used;
max_used := x.used;
i: int;
err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT));
if err != .OK {
return err;
}
dest.used = max_used;
borrow := DIGIT(0);
for i = 0; i < min_used; i += 1 {
dest.digit[i] = (x.digit[i] - y.digit[i] - borrow);
/*
borrow = carry bit of dest[i]
Note this saves performing an AND operation since if a carry does occur,
it will propagate all the way to the MSB.
As a result a single shift is enough to get the carry.
*/
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
/*
Clear borrow from dest[i].
*/
dest.digit[i] &= _MASK;
}
/*
Now copy higher words if any, e.g. if A has more digits than B
*/
for ; i < max_used; i += 1 {
dest.digit[i] = x.digit[i] - borrow;
/*
borrow = carry bit of dest[i]
Note this saves performing an AND operation since if a carry does occur,
it will propagate all the way to the MSB.
As a result a single shift is enough to get the carry.
*/
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
/*
Clear borrow from dest[i].
*/
dest.digit[i] &= _MASK;
}
zero_count := old_used - dest.used;
/*
Zero remainder.
*/
if zero_count > 0 {
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
}
/*
Adjust dest.used based on leading zeroes.
*/
clamp(dest);
return .OK;
}
+137
View File
@@ -0,0 +1,137 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:intrinsics"
/*
Tunables
*/
_LOW_MEMORY :: #config(BIGINT_SMALL_MEMORY, false);
when _LOW_MEMORY {
_DEFAULT_DIGIT_COUNT :: 8;
} else {
_DEFAULT_DIGIT_COUNT :: 32;
}
_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF);
_SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF);
_MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF);
_SQR_TOOM_CUTOFF :: #config(SQR_TOOM_CUTOFF, _DEFAULT_SQR_TOOM_CUTOFF);
/*
These defaults were tuned on an AMD A8-6600K (64-bit) using libTomMath's `make tune`.
TODO(Jeroen): Port this tuning algorithm and tune them for more modern processors.
*/
_DEFAULT_MUL_KARATSUBA_CUTOFF :: 80;
_DEFAULT_SQR_KARATSUBA_CUTOFF :: 120;
_DEFAULT_MUL_TOOM_CUTOFF :: 350;
_DEFAULT_SQR_TOOM_CUTOFF :: 400;
/*
TODO(Jeroen): Decide whether to turn `Sign` into `Flags :: bit_set{Flag; u8}`.
This would hold the sign and float class, as appropriate, and would allow us
to set an `Int` to +/- Inf, or NaN.
The operations would need to be updated to propagate these as expected.
*/
Sign :: enum u8 {
Zero_or_Positive = 0,
Negative = 1,
};
Int :: struct {
used: int,
allocated: int,
digit: [dynamic]DIGIT,
sign: Sign,
};
Comparison_Flag :: enum i8 {
Less_Than = -1,
Equal = 0,
Greater_Than = 1,
/* One of the numbers was uninitialized */
Uninitialized = -127,
};
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,
Unimplemented = -127,
};
Primality_Flag :: enum u8 {
Blum_Blum_Shub = 0, /* BBS style prime */
Safe = 1, /* Safe prime (p-1)/2 == prime */
Second_MSB_On = 3, /* force 2nd MSB to 1 */
};
Primality_Flags :: bit_set[Primality_Flag; u8];
/*
How do we store the Ints?
Minimum number of available digits in `Int`, `_DEFAULT_DIGIT_COUNT` >= `_MIN_DIGIT_COUNT`
- Must be at least 3 for `_div_school`.
- Must be large enough such that `init_integer` can store `u128` in the `Int` without growing.
*/
_MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS);
#assert(_DEFAULT_DIGIT_COUNT >= _MIN_DIGIT_COUNT);
/*
Maximum number of digits.
- Must be small enough such that `_bit_count` does not overflow.
- Must be small enough such that `_radix_size` for base 2 does not overflow.
`_radix_size` needs two additional bytes for zero termination and sign.
*/
_MAX_BIT_COUNT :: (max(int) - 2);
_MAX_DIGIT_COUNT :: _MAX_BIT_COUNT / _DIGIT_BITS;
when size_of(rawptr) == 8 {
/*
We can use u128 as an intermediary.
*/
DIGIT :: distinct(u64);
_WORD :: distinct(u128);
} else {
DIGIT :: distinct(u32);
_WORD :: distinct(u64);
}
#assert(size_of(_WORD) == 2 * size_of(DIGIT));
_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT);
_WORD_TYPE_BITS :: 8 * size_of(_WORD);
_DIGIT_BITS :: _DIGIT_TYPE_BITS - 4;
_WORD_BITS :: 2 * _DIGIT_BITS;
_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1);
_DIGIT_MAX :: _MASK;
_MAX_COMBA :: 1 << (_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) ;
_WARRAY :: 1 << ((_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) + 1);
Order :: enum i8 {
LSB_First = -1,
MSB_First = 1,
};
Endianness :: enum i8 {
Little = -1,
Platform = 0,
Big = 1,
};
+2
View File
@@ -0,0 +1,2 @@
@echo off
odin run . -vet
+155
View File
@@ -0,0 +1,155 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:intrinsics"
is_initialized :: proc(a: ^Int) -> bool {
return a != rawptr(uintptr(0));
}
is_zero :: proc(a: ^Int) -> bool {
return is_initialized(a) && a.used == 0;
}
is_positive :: proc(a: ^Int) -> bool {
return is_initialized(a) && a.sign == .Zero_or_Positive;
}
is_pos :: is_positive;;
is_negative :: proc(a: ^Int) -> bool {
return is_initialized(a) && a.sign == .Negative;
}
is_neg :: is_negative;
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;
}
is_odd :: proc(a: ^Int) -> bool {
if is_initialized(a) {
return !is_even(a);
}
return false;
}
is_power_of_two_small :: proc(a: int) -> bool {
return ((a) != 0) && (((a) & ((a) - 1)) == 0);
}
is_power_of_two_large :: proc(a: ^Int) -> (res: bool) {
/*
Early out for Int == 0.
*/
if a.used == 0 {
return false;
}
/*
For an `Int` to be a power of two, its top limb has to be a power of two.
*/
if !is_power_of_two_small(int(a.digit[a.used - 1])) {
return false;
}
/*
That was the only limb, so it's a power of two.
*/
if a.used == 1 {
return true;
}
/*
For an Int to be a power of two, all limbs except the top one have to be zero.
*/
for i := 1; i < a.used; i += 1 {
if a.digit[i - 1] != 0 {
return false;
}
}
return true;
}
is_power_of_two :: proc{is_power_of_two_small, is_power_of_two_large};
/*
Compare two `Int`s, signed.
*/
compare :: proc(a, b: ^Int) -> Comparison_Flag {
if !is_initialized(a) { return .Uninitialized; }
if !is_initialized(b) { return .Uninitialized; }
/* Compare based on sign */
if a.sign != b.sign {
return .Less_Than if is_negative(a) else .Greater_Than;
}
x, y := a, b;
/* If negative, compare in the opposite direction */
if is_neg(a) {
x, y = b, a;
}
return cmp_mag(x, y);
}
cmp :: compare;
/*
Compare the magnitude of two `Int`s, unsigned.
*/
compare_magnitude :: proc(a, b: ^Int) -> Comparison_Flag {
if !is_initialized(a) { return .Uninitialized; }
if !is_initialized(b) { return .Uninitialized; }
/* Compare based on used digits */
if a.used != b.used {
return .Greater_Than if a.used > b.used else .Less_Than;
}
/* 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;
}
}
return .Equal;
}
cmp_mag :: compare_magnitude;
/*
Compare an `Int` to an unsigned number upto the size of the backing type.
*/
compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag {
if !is_initialized(a) { return .Uninitialized; }
/* Compare based on sign */
if is_neg(a) {
return .Less_Than;
}
/* Compare based on magnitude */
if a.used > 1 {
return .Greater_Than;
}
/* Compare the only digit in `a` to `u`. */
if a.digit[0] != u {
return .Greater_Than if a.digit[0] > u else .Less_Than;
}
return .Equal;
}
+117
View File
@@ -0,0 +1,117 @@
//+ignore
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:fmt"
import "core:mem"
print_configation :: proc() {
fmt.printf(
`Configuration:
DIGIT_BITS %v
MIN_DIGIT_COUNT %v
MAX_DIGIT_COUNT %v
EFAULT_DIGIT_COUNT %v
MAX_COMBA %v
WARRAY %v
MUL_KARATSUBA_CUTOFF %v
SQR_KARATSUBA_CUTOFF %v
MUL_TOOM_CUTOFF %v
SQR_TOOM_CUTOFF %v
`, _DIGIT_BITS,
_MIN_DIGIT_COUNT,
_MAX_DIGIT_COUNT,
_DEFAULT_DIGIT_COUNT,
_MAX_COMBA,
_WARRAY,
_MUL_KARATSUBA_CUTOFF,
_SQR_KARATSUBA_CUTOFF,
_MUL_TOOM_CUTOFF,
_SQR_TOOM_CUTOFF,
);
fmt.println();
}
print :: proc(name: string, a: ^Int, base := i8(16)) {
as, err := itoa(a, base);
defer delete(as);
if err == .OK {
fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, count_bits(a), as);
} else {
fmt.printf("%v (error: %v): %v\n", name, err, a);
}
}
demo :: proc() {
a, b, c: ^Int;
err: Error;
defer destroy(a);
defer destroy(b);
defer destroy(c);
a, err = init(512);
b, err = init(a);
c, err = init(-4);
print("a", a, 2);
print("b", b, 2);
print("c", c, 2);
fmt.println("=== a = a & b ===");
err = and(a, a, b);
fmt.printf("a &= b error: %v\n", err);
print("a", a, 2);
print("b", b, 10);
fmt.println("\n\n=== b = abs(c) ===");
c.sign = .Negative;
abs(b, c); // copy c to b.
print("b", b);
print("c", c);
fmt.println("\n\n=== Set a to (1 << 120) - 1 ===");
if err = power_of_two(a, 120); err != .OK {
fmt.printf("Error %v while setting a to 1 << 120.\n", err);
}
if err = sub(a, a, 1); err != .OK {
fmt.printf("Error %v while subtracting 1 from a\n", err);
}
print("a", a, 16);
fmt.println("Expected a to be: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
}
main :: proc() {
ta := mem.Tracking_Allocator{};
mem.tracking_allocator_init(&ta, context.allocator);
context.allocator = mem.tracking_allocator(&ta);
// print_configation();
demo();
if len(ta.allocation_map) > 0 {
for _, v in ta.allocation_map {
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location);
}
}
if len(ta.bad_free_array) > 0 {
fmt.println("Bad frees:");
for v in ta.bad_free_array {
fmt.println(v);
}
}
}
+458
View File
@@ -0,0 +1,458 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:mem"
import "core:intrinsics"
/*
Deallocates the backing memory of an Int.
*/
destroy :: proc(a: ^Int, allocator_zeroes := false, free_int := true, loc := #caller_location) {
if !is_initialized(a) {
// Nothing to do.
return;
}
if !allocator_zeroes {
mem.zero_slice(a.digit[:]);
}
free(&a.digit[0]);
a.used = 0;
a.allocated = 0;
if free_int {
free(a);
}
}
/*
Creates and returns a new `Int`.
*/
init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (a: ^Int, err: Error) {
/*
Allocating a new variable.
*/
a = new(Int, allocator);
a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, size, size, allocator);
a.allocated = 0;
a.used = 0;
a.sign = .Zero_or_Positive;
if len(a.digit) != size {
return a, .Out_of_Memory;
}
a.allocated = size;
if !allocator_zeroes {
_zero_unused(a);
}
return a, .OK;
}
/*
Initialize from a signed or unsigned integer.
Inits a new `Int` and then calls the appropriate `set` routine.
*/
init_from_integer :: proc(src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) where intrinsics.type_is_integer(T) {
n := _DEFAULT_DIGIT_COUNT;
if minimize {
n = _MIN_DIGIT_COUNT;
}
a, err = init_new(allocator_zeroes, allocator, n);
if err == .OK {
set(a, src, minimize);
}
return;
}
/*
Initialize an `Int` as a copy from another `Int`.
*/
init_copy :: proc(src: ^Int, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) {
if !is_initialized(src) {
return nil, .Invalid_Input;
}
a, err = init_new(allocator_zeroes, allocator, src.used);
if err == .OK {
copy(a, src);
}
return;
}
init :: proc{init_new, init_from_integer, init_copy};
/*
Helpers to set an `Int` to a specific value.
*/
set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) where intrinsics.type_is_integer(T) {
n := n;
assert_initialized(a, loc);
a.used = 0;
a.sign = .Zero_or_Positive if n >= 0 else .Negative;
n = abs(n);
for n != 0 {
a.digit[a.used] = DIGIT(n) & _MASK;
a.used += 1;
n >>= _DIGIT_BITS;
}
if minimize {
shrink(a);
}
_zero_unused(a);
}
set :: proc{set_integer};
/*
Copy one `Int` to another.
*/
copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
/*
If dest == src, do nothing
*/
if (dest == src) {
return .OK;
}
/*
Check they're both initialized.
*/
if !(is_initialized(dest) && is_initialized(src)) {
return .Invalid_Input;
}
/*
Grow `dest` to fit `src`.
*/
if err = grow(dest, src.used); err != .OK {
return err;
}
/*
Copy everything over and zero high digits.
*/
assert(dest.allocated >= src.used);
for v, i in src.digit[:src.used+1] {
dest.digit[i] = v;
}
dest.used = src.used;
dest.sign = src.sign;
_zero_unused(dest);
return .OK;
}
/*
Set `dest` to |`src`|.
*/
abs_bigint :: proc(dest, src: ^Int) -> (err: Error) {
/*
If `dest == src`, just fix `dest`'s sign.
*/
if (dest == src) {
dest.sign = .Zero_or_Positive;
return .OK;
}
/*
Check they're both initialized.
*/
if !(is_initialized(dest) && is_initialized(src)) {
return .Invalid_Input;
}
/*
Copy `src` to `dest`
*/
if err = copy(dest, src); err != .OK {
return err;
}
/*
Fix sign.
*/
dest.sign = .Zero_or_Positive;
return .OK;
}
abs_integer :: proc(n: $T) -> T where intrinsics.type_is_integer(T) {
return n if n >= 0 else -n;
}
abs :: proc{abs_bigint, abs_integer};
/*
Set `dest` to `-src`.
*/
neg :: proc(dest, src: ^Int) -> (err: Error) {
/*
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;
}
/*
Check they're both initialized.
*/
if !(is_initialized(dest) && is_initialized(src)) {
return .Invalid_Input;
}
/*
Copy `src` to `dest`
*/
if err = copy(dest, src); err != .OK {
return err;
}
/*
Fix sign.
*/
dest.sign = sign;
return .OK;
}
/*
Helpers to extract values from the `Int`.
*/
extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) {
limb := bit_offset / _DIGIT_BITS;
if limb < 0 || limb >= a.used {
return 0, .Invalid_Input;
}
i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS)));
return 1 if ((a.digit[limb] & i) != 0) else 0, .OK;
}
/*
TODO: Optimize.
*/
extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) {
if count > _WORD_BITS || count < 1 {
return 0, .Invalid_Input;
}
v: DIGIT;
e: Error;
for shift := 0; shift < count; shift += 1 {
o := offset + shift;
v, e = extract_bit(a, o);
if e != .OK {
break;
}
res = res + _WORD(v) << uint(shift);
}
return res, e;
}
/*
Resize backing store.
*/
shrink :: proc(a: ^Int) -> (err: Error) {
needed := max(_MIN_DIGIT_COUNT, a.used);
if a.used != needed {
return grow(a, needed);
}
return .OK;
}
grow :: proc(a: ^Int, n: int, allow_shrink := false) -> (err: Error) {
assert_initialized(a);
/*
By default, calling `grow` with `n` <= a.allocated won't resize.
With `allow_shrink` set to `true`, will call resize and shrink the `Int` as a result.
*/
/*
We need at least _MIN_DIGIT_COUNT or a.used digits, whichever is bigger.
*/
needed := max(_MIN_DIGIT_COUNT, a.used);
/*
The caller is asking for `n`. Let's be accomodating.
*/
needed = max(needed, n);
/*
If `allow_shrink` == `false`, we need to needed >= `a.allocated`.
*/
if !allow_shrink {
needed = max(needed, a.allocated);
}
if a.allocated != needed {
resize(&a.digit, needed);
if len(a.digit) != needed {
return .Out_of_Memory;
}
}
// a.used = min(size, a.used);
a.allocated = needed;
return .OK;
}
/*
Clear `Int` and resize it to the default size.
*/
clear :: proc(a: ^Int) -> (err: Error) {
assert_initialized(a);
mem.zero_slice(a.digit[:]);
a.sign = .Zero_or_Positive;
a.used = 0;
grow(a, _DEFAULT_DIGIT_COUNT);
return .OK;
}
/*
Set the `Int` to 0 and optionally shrink it to the minimum backing size.
*/
zero :: proc(a: ^Int, minimize := false) -> (err: Error) {
assert_initialized(a);
a.sign = .Zero_or_Positive;
a.used = 0;
mem.zero_slice(a.digit[a.used:]);
if minimize {
return shrink(a);
}
return .OK;
}
/*
Set the `Int` to 1 and optionally shrink it to the minimum backing size.
*/
one :: proc(a: ^Int, minimize := false) -> (err: Error) {
assert_initialized(a);
a.sign = .Zero_or_Positive;
a.used = 1;
a.digit[0] = 1;
mem.zero_slice(a.digit[a.used:]);
if minimize {
return shrink(a);
}
return .OK;
}
/*
Set the `Int` to -1 and optionally shrink it to the minimum backing size.
*/
minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) {
assert_initialized(a);
a.sign = .Negative;
a.used = 1;
a.digit[0] = 1;
mem.zero_slice(a.digit[a.used:]);
if minimize {
return shrink(a);
}
return .OK;
}
power_of_two :: proc(a: ^Int, power: int) -> (err: Error) {
assert_initialized(a);
/*
*/
if power < 0 || power > _MAX_BIT_COUNT {
return .Invalid_Input;
}
/*
Grow to accomodate the single bit.
*/
a.used = (power / _DIGIT_BITS) + 1;
if err = grow(a, a.used); err != .OK {
return err;
}
/*
Zero the entirety.
*/
mem.zero_slice(a.digit[:]);
/*
Set the bit.
*/
a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS));
return .OK;
}
/*
Count bits in an `Int`.
*/
count_bits :: proc(a: ^Int) -> (count: int) {
assert_initialized(a);
/*
Fast path for zero.
*/
if is_zero(a) {
return 0;
}
/*
Get the number of DIGITs and use it.
*/
count = (a.used - 1) * _DIGIT_BITS;
/*
Take the last DIGIT and count the bits in it.
*/
clz := int(intrinsics.count_leading_zeros(a.digit[a.used - 1]));
count += (_DIGIT_TYPE_BITS - clz);
return;
}
/*
Internal helpers.
*/
assert_initialized :: proc(a: ^Int, loc := #caller_location) {
assert(is_initialized(a), "`Int` was not properly initialized.", loc);
}
_zero_unused :: proc(a: ^Int) {
assert_initialized(a);
if a.used < a.allocated {
mem.zero_slice(a.digit[a.used:]);
}
}
clamp :: proc(a: ^Int) {
assert_initialized(a);
/*
Trim unused digits
This is used to ensure that leading zero digits are
trimmed and the leading "used" digit will be non-zero.
Typically very fast. Also fixes the sign if there
are no more leading digits.
*/
for a.used > 0 && a.digit[a.used - 1] == 0 {
a.used -= 1;
}
if is_zero(a) {
a.sign = .Zero_or_Positive;
}
}
+123
View File
@@ -0,0 +1,123 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) {
assert_initialized(a);
if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX {
return -1, .Invalid_Input;
}
/*
Fast path for bases that are a power of two.
*/
if is_power_of_two(int(base)) {
return _log_power_of_two(a, base), .OK;
}
/*
Fast path for `Int`s that fit within a single `DIGIT`.
*/
if a.used == 1 {
return log_n_digit(a.digit[0], DIGIT(base)), .OK;
}
// if (MP_HAS(S_MP_LOG)) {
// return s_mp_log(a, (mp_digit)base, c);
// }
return -1, .Unimplemented;
}
log_n :: proc{log_n_int, log_n_digit};
/*
Returns the log2 of an `Int`, provided `base` is a power of two.
Don't call it if it isn't.
*/
_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int) {
base := base;
y: int;
for y = 0; base & 1 == 0; {
y += 1;
base >>= 1;
}
return (count_bits(a) - 1) / y;
}
/*
*/
small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) {
exponent := exponent; base := base;
result = _WORD(1);
for exponent != 0 {
if exponent & 1 == 1 {
result *= base;
}
exponent >>= 1;
base *= base;
}
return result;
}
log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int) {
/*
If the number is smaller than the base, it fits within a fraction.
Therefore, we return 0.
*/
if a < base {
return 0;
}
/*
If a number equals the base, the log is 1.
*/
if a == base {
return 1;
}
N := _WORD(a);
bracket_low := _WORD(1);
bracket_high := _WORD(base);
high := 1;
low := 0;
for bracket_high < N {
low = high;
bracket_low = bracket_high;
high <<= 1;
bracket_high *= bracket_high;
}
for high - low > 1 {
mid := (low + high) >> 1;
bracket_mid := bracket_low * small_pow(_WORD(base), _WORD(mid - low));
if N < bracket_mid {
high = mid;
bracket_high = bracket_mid;
}
if N > bracket_mid {
low = mid;
bracket_low = bracket_mid;
}
if N == bracket_mid {
return mid;
}
}
if bracket_high == N {
return high;
} else {
return low;
}
}
+206
View File
@@ -0,0 +1,206 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains logical operations like `and`, `or` and `xor`.
*/
/*
The `and`, `or` and `xor` binops differ in two lines only.
We could handle those with a switch, but that adds overhead.
*/
/*
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);
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 {
return err;
}
for i := 0; i < used; i += 1 {
x, y: DIGIT;
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK;
ac >>= _DIGIT_BITS;
} else {
x = 0 if i >= a.used else a.digit[i];
}
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK;
bc >>= _DIGIT_BITS;
} else {
y = 0 if i >= b.used else b.digit[i];
}
dest.digit[i] = x & y;
/*
Convert to to sign-magnitude if negative.
*/
if neg {
cc += ~dest.digit[i] & _MASK;
dest.digit[i] = cc & _MASK;
cc >>= _DIGIT_BITS;
}
}
dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest);
return .OK;
}
/*
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);
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 {
return err;
}
for i := 0; i < used; i += 1 {
x, y: DIGIT;
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK;
ac >>= _DIGIT_BITS;
} else {
x = 0 if i >= a.used else a.digit[i];
}
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK;
bc >>= _DIGIT_BITS;
} else {
y = 0 if i >= b.used else b.digit[i];
}
dest.digit[i] = x | y;
/*
Convert to to sign-magnitude if negative.
*/
if neg {
cc += ~dest.digit[i] & _MASK;
dest.digit[i] = cc & _MASK;
cc >>= _DIGIT_BITS;
}
}
dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest);
return .OK;
}
/*
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);
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 {
return err;
}
for i := 0; i < used; i += 1 {
x, y: DIGIT;
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK;
ac >>= _DIGIT_BITS;
} else {
x = 0 if i >= a.used else a.digit[i];
}
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK;
bc >>= _DIGIT_BITS;
} else {
y = 0 if i >= b.used else b.digit[i];
}
dest.digit[i] = x ~ y;
/*
Convert to to sign-magnitude if negative.
*/
if neg {
cc += ~dest.digit[i] & _MASK;
dest.digit[i] = cc & _MASK;
cc >>= _DIGIT_BITS;
}
}
dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest);
return .OK;
}
+282
View File
@@ -0,0 +1,282 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa).
*/
import "core:intrinsics"
import "core:fmt"
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);
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10;
/*
TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer.
Then after the digits are written and the string is reversed
*/
/*
Calculate the size of the buffer we need.
*/
size: int;
size, err = radix_size(a, radix, zero_terminate);
/*
Exit if calculating the size returned an error.
*/
if err != .OK {
f := strings.clone(fallback(a), allocator);
if zero_terminate {
c := strings.clone_to_cstring(f);
return string(c), err;
}
return f, err;
}
/*
Allocate the buffer we need.
*/
buffer := make([]u8, size);
/*
Write the digits out into the buffer.
*/
written: int;
written, err = itoa_raw(a, radix, buffer, size, zero_terminate);
/*
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 {
if print_raw {
return fmt.tprintf("%v", a);
}
sign := "-" if a.sign == .Negative else "";
if a.used <= 2 {
v := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]);
return fmt.tprintf("%v%v", sign, v);
} else {
return fmt.tprintf("[%2d/%2d] %v%v", a.used, a.allocated, sign, a.digit[:a.used]);
}
}
return strings.clone(fallback(a), allocator), .Unimplemented;
}
/*
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);
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10;
s: string;
s, err = itoa_string(a, radix, true, allocator);
return cstring(raw_data(s)), err;
}
/*
A low-level `itoa` using a caller-provided buffer. `itoa_string` and `itoa_cstring` use this.
You can use also use it if you want to pre-allocate a buffer and optionally reuse it.
Use `radix_size` or `radix_size_estimate` to determine a buffer size big enough.
You can pass the output of `radix_size` to `size` if you've previously called it to size
the output buffer. If you haven't, this routine will call it. This way it knows if the buffer
is the appropriate size, and we can write directly in place without a reverse step at the end.
=== === === IMPORTANT === === ===
If you determined the buffer size using `radix_size_estimate`, or have a buffer
that you reuse that you know is large enough, don't pass this size unless you know what you are doing,
because we will always write backwards starting at last byte of the buffer.
Keep in mind that if you set `size` yourself and it's smaller than the buffer,
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.
*/
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;
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10;
if radix < 2 || radix > 64 {
return 0, .Invalid_Input;
}
/*
We weren't given a size. Let's compute it.
*/
if size == -1 {
size, err = radix_size(a, radix, zero_terminate);
}
/*
Early exit if the buffer we were given is too small.
*/
available := len(buffer);
if available < size {
return 0, .Buffer_Overflow;
}
/*
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)) {
if zero_terminate {
available -= 1;
buffer[available] = 0;
}
available -= 1;
buffer[available] = RADIX_TABLE[a.digit[0]];
if is_neg(a) {
available -= 1;
buffer[available] = '-';
}
return len(buffer) - available, .OK;
}
/*
Fast path for when `Int` fits within a `_WORD`.
*/
if a.used == 1 || a.used == 2 {
if zero_terminate {
available -= 1;
buffer[available] = 0;
}
val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]);
for val > 0 {
q := val / _WORD(radix);
available -= 1;
buffer[available] = RADIX_TABLE[val - (q * _WORD(radix))];
val = q;
}
if is_neg(a) {
available -= 1;
buffer[available] = '-';
}
return len(buffer) - available, .OK;
}
/*
At least 3 DIGITs are in use if we made it this far.
*/
/*
Fast path for radixes that are a power of two.
*/
if is_power_of_two(int(radix)) {
if zero_terminate {
available -= 1;
buffer[available] = 0;
}
// mask := _WORD(radix - 1);
shift := int(log_n(DIGIT(radix), 2));
count := int(count_bits(a));
// 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;
}
available -= 1;
buffer[available] = RADIX_TABLE[digit];
}
if is_neg(a) {
available -= 1;
buffer[available] = '-';
}
return len(buffer) - available, .OK;
}
return -1, .Unimplemented;
}
itoa :: proc{itoa_string, itoa_raw};
int_to_string :: itoa;
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) {
if radix < 2 || radix > 64 {
return -1, .Invalid_Input;
}
if is_zero(a) {
if zero_terminate {
return 2, .OK;
}
return 1, .OK;
}
/*
Calculate `log` on a temporary "copy" with its sign set to positive.
*/
t := &Int{
used = a.used,
allocated = a.allocated,
sign = .Zero_or_Positive,
digit = a.digit,
};
size, err = log_n(t, DIGIT(radix));
if err != .OK {
return;
}
/*
log 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;
}
/*
Characters used in radix conversions.
*/
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
RADIX_TABLE_REVERSE := [80]u8{
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* IJKLMNOPQR */
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, /* STUVWXYZ[\ */
0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
};