mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
bigint: refactor to big.Int instead of bigint.Int.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
odin run . -vet
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
};
|
||||
Reference in New Issue
Block a user