mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
bigint: refactor to big.Int instead of bigint.Int.
This commit is contained in:
@@ -1,455 +1,454 @@
|
|||||||
package bigint
|
package big
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
Made available under Odin's BSD-2 license.
|
Made available under Odin's BSD-2 license.
|
||||||
|
|
||||||
A BigInt implementation in Odin.
|
A BigInt implementation in Odin.
|
||||||
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
|
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.
|
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`.
|
This file contains basic arithmetic operations like `add` and `sub`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
import "core:fmt"
|
|
||||||
|
/*
|
||||||
/*
|
===========================
|
||||||
===========================
|
User-level routines
|
||||||
User-level routines
|
===========================
|
||||||
===========================
|
*/
|
||||||
*/
|
|
||||||
|
/*
|
||||||
/*
|
High-level addition. Handles sign.
|
||||||
High-level addition. Handles sign.
|
*/
|
||||||
*/
|
add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) {
|
||||||
add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) {
|
dest := dest; x := a; y := b;
|
||||||
dest := dest; x := a; y := b;
|
assert_initialized(dest); assert_initialized(a); assert_initialized(b);
|
||||||
assert_initialized(dest); assert_initialized(a); assert_initialized(b);
|
|
||||||
|
/*
|
||||||
/*
|
Handle both negative or both positive.
|
||||||
Handle both negative or both positive.
|
*/
|
||||||
*/
|
if x.sign == y.sign {
|
||||||
if x.sign == y.sign {
|
dest.sign = x.sign;
|
||||||
dest.sign = x.sign;
|
return _add(dest, x, y);
|
||||||
return _add(dest, x, y);
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
One positive, the other negative.
|
||||||
One positive, the other negative.
|
Subtract the one with the greater magnitude from the other.
|
||||||
Subtract the one with the greater magnitude from the other.
|
The result gets the sign of the one with the greater magnitude.
|
||||||
The result gets the sign of the one with the greater magnitude.
|
*/
|
||||||
*/
|
if cmp_mag(x, y) == .Less_Than {
|
||||||
if cmp_mag(x, y) == .Less_Than {
|
x, y = y, x;
|
||||||
x, y = y, x;
|
}
|
||||||
}
|
|
||||||
|
dest.sign = x.sign;
|
||||||
dest.sign = x.sign;
|
return _sub(dest, x, y);
|
||||||
return _sub(dest, x, y);
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Adds the unsigned `DIGIT` immediate to an `Int`,
|
||||||
Adds the unsigned `DIGIT` immediate to an `Int`,
|
such that the `DIGIT` doesn't have to be turned into an `Int` first.
|
||||||
such that the `DIGIT` doesn't have to be turned into an `Int` first.
|
|
||||||
|
dest = a + digit;
|
||||||
dest = a + digit;
|
*/
|
||||||
*/
|
add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
||||||
add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
dest := dest; digit := digit;
|
||||||
dest := dest; x := a; digit := digit;
|
assert_initialized(dest); assert_initialized(a);
|
||||||
assert_initialized(dest); assert_initialized(a);
|
|
||||||
|
/*
|
||||||
/*
|
Fast paths for destination and input Int being the same.
|
||||||
Fast paths for destination and input Int being the same.
|
*/
|
||||||
*/
|
if dest == a {
|
||||||
if dest == a {
|
/*
|
||||||
/*
|
Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow.
|
||||||
Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow.
|
*/
|
||||||
*/
|
if is_pos(dest) && (dest.digit[0] + digit < _DIGIT_MAX) {
|
||||||
if is_pos(dest) && (dest.digit[0] + digit < _DIGIT_MAX) {
|
dest.digit[0] += digit;
|
||||||
dest.digit[0] += digit;
|
return .OK;
|
||||||
return .OK;
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Can be subtracted from dest.digit[0] without underflow.
|
||||||
Can be subtracted from dest.digit[0] without underflow.
|
*/
|
||||||
*/
|
if is_neg(a) && (dest.digit[0] > digit) {
|
||||||
if is_neg(a) && (dest.digit[0] > digit) {
|
dest.digit[0] -= digit;
|
||||||
dest.digit[0] -= digit;
|
return .OK;
|
||||||
return .OK;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Grow destination as required.
|
||||||
Grow destination as required.
|
*/
|
||||||
*/
|
err = grow(dest, a.used + 1);
|
||||||
err = grow(dest, a.used + 1);
|
if err != .OK {
|
||||||
if err != .OK {
|
return err;
|
||||||
return err;
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit`
|
||||||
If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit`
|
*/
|
||||||
*/
|
if is_neg(a) && (a.used > 1 || a.digit[0] >= digit) {
|
||||||
if is_neg(a) && (a.used > 1 || a.digit[0] >= digit) {
|
/*
|
||||||
/*
|
Temporarily fix `a`'s sign.
|
||||||
Temporarily fix `a`'s sign.
|
*/
|
||||||
*/
|
t := a;
|
||||||
t := a;
|
t.sign = .Zero_or_Positive;
|
||||||
t.sign = .Zero_or_Positive;
|
/*
|
||||||
/*
|
dest = |a| - digit
|
||||||
dest = |a| - digit
|
*/
|
||||||
*/
|
err = sub(dest, t, digit);
|
||||||
err = sub(dest, t, digit);
|
/*
|
||||||
/*
|
Restore sign and set `dest` sign.
|
||||||
Restore sign and set `dest` sign.
|
*/
|
||||||
*/
|
dest.sign = .Negative;
|
||||||
dest.sign = .Negative;
|
clamp(dest);
|
||||||
clamp(dest);
|
|
||||||
|
return err;
|
||||||
return err;
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Remember the currently used number of digits in `dest`.
|
||||||
Remember the currently used number of digits in `dest`.
|
*/
|
||||||
*/
|
old_used := dest.used;
|
||||||
old_used := dest.used;
|
|
||||||
|
/*
|
||||||
/*
|
If `a` is positive
|
||||||
If `a` is positive
|
*/
|
||||||
*/
|
if is_pos(a) {
|
||||||
if is_pos(a) {
|
/*
|
||||||
/*
|
Add digits, use `carry`.
|
||||||
Add digits, use `carry`.
|
*/
|
||||||
*/
|
i: int;
|
||||||
i: int;
|
carry := digit;
|
||||||
carry := digit;
|
for i = 0; i < a.used; i += 1 {
|
||||||
for i = 0; i < a.used; i += 1 {
|
dest.digit[i] = a.digit[i] + carry;
|
||||||
dest.digit[i] = a.digit[i] + carry;
|
carry = dest.digit[i] >> _DIGIT_BITS;
|
||||||
carry = dest.digit[i] >> _DIGIT_BITS;
|
dest.digit[i] &= _MASK;
|
||||||
dest.digit[i] &= _MASK;
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Set final carry.
|
||||||
Set final carry.
|
*/
|
||||||
*/
|
dest.digit[i] = carry;
|
||||||
dest.digit[i] = carry;
|
/*
|
||||||
/*
|
Set `dest` size.
|
||||||
Set `dest` size.
|
*/
|
||||||
*/
|
dest.used = a.used + 1;
|
||||||
dest.used = a.used + 1;
|
} else {
|
||||||
} else {
|
/*
|
||||||
/*
|
`a` was negative and |a| < digit.
|
||||||
`a` was negative and |a| < digit.
|
*/
|
||||||
*/
|
dest.used = 1;
|
||||||
dest.used = 1;
|
/*
|
||||||
/*
|
The result is a single DIGIT.
|
||||||
The result is a single DIGIT.
|
*/
|
||||||
*/
|
dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit;
|
||||||
dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit;
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Sign is always positive.
|
||||||
Sign is always positive.
|
*/
|
||||||
*/
|
dest.sign = .Zero_or_Positive;
|
||||||
dest.sign = .Zero_or_Positive;
|
|
||||||
|
zero_count := old_used - dest.used;
|
||||||
zero_count := old_used - dest.used;
|
/*
|
||||||
/*
|
Zero remainder.
|
||||||
Zero remainder.
|
*/
|
||||||
*/
|
if zero_count > 0 {
|
||||||
if zero_count > 0 {
|
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
||||||
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Adjust dest.used based on leading zeroes.
|
||||||
Adjust dest.used based on leading zeroes.
|
*/
|
||||||
*/
|
clamp(dest);
|
||||||
clamp(dest);
|
|
||||||
|
return .OK;
|
||||||
return .OK;
|
}
|
||||||
}
|
|
||||||
|
add :: proc{add_two_ints, add_digit};
|
||||||
add :: proc{add_two_ints, add_digit};
|
|
||||||
|
/*
|
||||||
/*
|
High-level subtraction, dest = number - decrease. Handles signs.
|
||||||
High-level subtraction, dest = number - decrease. Handles signs.
|
*/
|
||||||
*/
|
sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) {
|
||||||
sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) {
|
dest := dest; x := number; y := decrease;
|
||||||
dest := dest; x := number; y := decrease;
|
assert_initialized(number); assert_initialized(decrease); assert_initialized(dest);
|
||||||
assert_initialized(number); assert_initialized(decrease); assert_initialized(dest);
|
|
||||||
|
if x.sign != y.sign {
|
||||||
if x.sign != y.sign {
|
/*
|
||||||
/*
|
Subtract a negative from a positive, OR subtract a positive from a negative.
|
||||||
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.
|
||||||
In either case, ADD their magnitudes and use the sign of the first number.
|
*/
|
||||||
*/
|
dest.sign = x.sign;
|
||||||
dest.sign = x.sign;
|
return _add(dest, x, y);
|
||||||
return _add(dest, x, y);
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Subtract a positive from a positive, OR negative from a negative.
|
||||||
Subtract a positive from a positive, OR negative from a negative.
|
First, take the difference between their magnitudes, then...
|
||||||
First, take the difference between their magnitudes, then...
|
*/
|
||||||
*/
|
if cmp_mag(x, y) == .Less_Than {
|
||||||
if cmp_mag(x, y) == .Less_Than {
|
/*
|
||||||
/*
|
The second has a larger magnitude.
|
||||||
The second has a larger magnitude.
|
The result has the *opposite* sign from the first number.
|
||||||
The result has the *opposite* sign from the first number.
|
*/
|
||||||
*/
|
dest.sign = .Negative if is_pos(x) else .Zero_or_Positive;
|
||||||
dest.sign = .Negative if is_pos(x) else .Zero_or_Positive;
|
x, y = y, x;
|
||||||
x, y = y, x;
|
} else {
|
||||||
} else {
|
/*
|
||||||
/*
|
The first has a larger or equal magnitude.
|
||||||
The first has a larger or equal magnitude.
|
Copy the sign from the first.
|
||||||
Copy the sign from the first.
|
*/
|
||||||
*/
|
dest.sign = x.sign;
|
||||||
dest.sign = x.sign;
|
}
|
||||||
}
|
return _sub(dest, x, y);
|
||||||
return _sub(dest, x, y);
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Adds the unsigned `DIGIT` immediate to an `Int`,
|
||||||
Adds the unsigned `DIGIT` immediate to an `Int`,
|
such that the `DIGIT` doesn't have to be turned into an `Int` first.
|
||||||
such that the `DIGIT` doesn't have to be turned into an `Int` first.
|
|
||||||
|
dest = a - digit;
|
||||||
dest = a - digit;
|
*/
|
||||||
*/
|
sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
||||||
sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
dest := dest; digit := digit;
|
||||||
dest := dest; x := a; digit := digit;
|
assert_initialized(dest); assert_initialized(a);
|
||||||
assert_initialized(dest); assert_initialized(a);
|
|
||||||
|
/*
|
||||||
/*
|
Fast paths for destination and input Int being the same.
|
||||||
Fast paths for destination and input Int being the same.
|
*/
|
||||||
*/
|
if dest == a {
|
||||||
if dest == a {
|
/*
|
||||||
/*
|
Fast path for `dest` is negative and unsigned addition doesn't overflow the lowest digit.
|
||||||
Fast path for `dest` is negative and unsigned addition doesn't overflow the lowest digit.
|
*/
|
||||||
*/
|
if is_neg(dest) && (dest.digit[0] + digit < _DIGIT_MAX) {
|
||||||
if is_neg(dest) && (dest.digit[0] + digit < _DIGIT_MAX) {
|
dest.digit[0] += digit;
|
||||||
dest.digit[0] += digit;
|
return .OK;
|
||||||
return .OK;
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Can be subtracted from dest.digit[0] without underflow.
|
||||||
Can be subtracted from dest.digit[0] without underflow.
|
*/
|
||||||
*/
|
if is_pos(a) && (dest.digit[0] > digit) {
|
||||||
if is_pos(a) && (dest.digit[0] > digit) {
|
dest.digit[0] -= digit;
|
||||||
dest.digit[0] -= digit;
|
return .OK;
|
||||||
return .OK;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Grow destination as required.
|
||||||
Grow destination as required.
|
*/
|
||||||
*/
|
err = grow(dest, a.used + 1);
|
||||||
err = grow(dest, a.used + 1);
|
if err != .OK {
|
||||||
if err != .OK {
|
return err;
|
||||||
return err;
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
If `a` is negative, just do an unsigned addition (with fudged signs).
|
||||||
If `a` is negative, just do an unsigned addition (with fudged signs).
|
*/
|
||||||
*/
|
if is_neg(a) {
|
||||||
if is_neg(a) {
|
t := a;
|
||||||
t := a;
|
t.sign = .Zero_or_Positive;
|
||||||
t.sign = .Zero_or_Positive;
|
|
||||||
|
err = add(dest, t, digit);
|
||||||
err = add(dest, t, digit);
|
dest.sign = .Negative;
|
||||||
dest.sign = .Negative;
|
|
||||||
|
clamp(dest);
|
||||||
clamp(dest);
|
return err;
|
||||||
return err;
|
}
|
||||||
}
|
|
||||||
|
old_used := dest.used;
|
||||||
old_used := dest.used;
|
|
||||||
|
/*
|
||||||
/*
|
if `a`<= digit, simply fix the single digit.
|
||||||
if `a`<= digit, simply fix the single digit.
|
*/
|
||||||
*/
|
if a.used == 1 && (a.digit[0] <= digit || is_zero(a)) {
|
||||||
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.digit[0] = digit - a.digit[0] if a.used == 1 else digit;
|
dest.sign = .Negative;
|
||||||
dest.sign = .Negative;
|
dest.used = 1;
|
||||||
dest.used = 1;
|
} else {
|
||||||
} else {
|
dest.sign = .Zero_or_Positive;
|
||||||
dest.sign = .Zero_or_Positive;
|
dest.used = a.used;
|
||||||
dest.used = a.used;
|
|
||||||
|
/*
|
||||||
/*
|
Subtract with carry.
|
||||||
Subtract with carry.
|
*/
|
||||||
*/
|
carry := digit;
|
||||||
carry := digit;
|
|
||||||
|
for i := 0; i < a.used; i += 1 {
|
||||||
for i := 0; i < a.used; i += 1 {
|
dest.digit[i] = a.digit[i] - carry;
|
||||||
dest.digit[i] = a.digit[i] - carry;
|
carry := dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
|
||||||
carry := dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
|
dest.digit[i] &= _MASK;
|
||||||
dest.digit[i] &= _MASK;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
zero_count := old_used - dest.used;
|
||||||
zero_count := old_used - dest.used;
|
/*
|
||||||
/*
|
Zero remainder.
|
||||||
Zero remainder.
|
*/
|
||||||
*/
|
if zero_count > 0 {
|
||||||
if zero_count > 0 {
|
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
||||||
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Adjust dest.used based on leading zeroes.
|
||||||
Adjust dest.used based on leading zeroes.
|
*/
|
||||||
*/
|
clamp(dest);
|
||||||
clamp(dest);
|
|
||||||
|
return .OK;
|
||||||
return .OK;
|
}
|
||||||
}
|
|
||||||
|
sub :: proc{sub_two_ints, sub_digit};
|
||||||
sub :: proc{sub_two_ints, sub_digit};
|
|
||||||
|
/*
|
||||||
/*
|
==========================
|
||||||
==========================
|
Low-level routines
|
||||||
Low-level routines
|
==========================
|
||||||
==========================
|
*/
|
||||||
*/
|
|
||||||
|
/*
|
||||||
/*
|
Low-level addition, unsigned.
|
||||||
Low-level addition, unsigned.
|
Handbook of Applied Cryptography, algorithm 14.7.
|
||||||
Handbook of Applied Cryptography, algorithm 14.7.
|
*/
|
||||||
*/
|
_add :: proc(dest, a, b: ^Int) -> (err: Error) {
|
||||||
_add :: proc(dest, a, b: ^Int) -> (err: Error) {
|
dest := dest; x := a; y := b;
|
||||||
dest := dest; x := a; y := b;
|
assert_initialized(a); assert_initialized(b); assert_initialized(dest);
|
||||||
assert_initialized(a); assert_initialized(b); assert_initialized(dest);
|
|
||||||
|
old_used, min_used, max_used, i: int;
|
||||||
old_used, min_used, max_used, i: int;
|
|
||||||
|
if x.used < y.used {
|
||||||
if x.used < y.used {
|
x, y = y, x;
|
||||||
x, y = y, x;
|
}
|
||||||
}
|
|
||||||
|
min_used = x.used;
|
||||||
min_used = x.used;
|
max_used = y.used;
|
||||||
max_used = y.used;
|
old_used = dest.used;
|
||||||
old_used = dest.used;
|
|
||||||
|
err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT));
|
||||||
err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT));
|
if err != .OK {
|
||||||
if err != .OK {
|
return err;
|
||||||
return err;
|
}
|
||||||
}
|
dest.used = max_used + 1;
|
||||||
dest.used = max_used + 1;
|
|
||||||
|
/* Zero the carry */
|
||||||
/* Zero the carry */
|
carry := DIGIT(0);
|
||||||
carry := DIGIT(0);
|
|
||||||
|
for i = 0; i < min_used; i += 1 {
|
||||||
for i = 0; i < min_used; i += 1 {
|
/*
|
||||||
/*
|
Compute the sum one _DIGIT at a time.
|
||||||
Compute the sum one _DIGIT at a time.
|
dest[i] = a[i] + b[i] + carry;
|
||||||
dest[i] = a[i] + b[i] + carry;
|
*/
|
||||||
*/
|
dest.digit[i] = x.digit[i] + y.digit[i] + carry;
|
||||||
dest.digit[i] = x.digit[i] + y.digit[i] + carry;
|
|
||||||
|
/*
|
||||||
/*
|
Compute carry
|
||||||
Compute carry
|
*/
|
||||||
*/
|
carry = dest.digit[i] >> _DIGIT_BITS;
|
||||||
carry = dest.digit[i] >> _DIGIT_BITS;
|
/*
|
||||||
/*
|
Mask away carry from result digit.
|
||||||
Mask away carry from result digit.
|
*/
|
||||||
*/
|
dest.digit[i] &= _MASK;
|
||||||
dest.digit[i] &= _MASK;
|
}
|
||||||
}
|
|
||||||
|
if min_used != max_used {
|
||||||
if min_used != max_used {
|
/*
|
||||||
/*
|
Now copy higher words, if any, in A+B.
|
||||||
Now copy higher words, if any, in A+B.
|
If A or B has more digits, add those in.
|
||||||
If A or B has more digits, add those in.
|
*/
|
||||||
*/
|
for ; i < max_used; i += 1 {
|
||||||
for ; i < max_used; i += 1 {
|
dest.digit[i] = x.digit[i] + carry;
|
||||||
dest.digit[i] = x.digit[i] + carry;
|
/*
|
||||||
/*
|
Compute carry
|
||||||
Compute carry
|
*/
|
||||||
*/
|
carry = dest.digit[i] >> _DIGIT_BITS;
|
||||||
carry = dest.digit[i] >> _DIGIT_BITS;
|
/*
|
||||||
/*
|
Mask away carry from result digit.
|
||||||
Mask away carry from result digit.
|
*/
|
||||||
*/
|
dest.digit[i] &= _MASK;
|
||||||
dest.digit[i] &= _MASK;
|
}
|
||||||
}
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Add remaining carry.
|
||||||
Add remaining carry.
|
*/
|
||||||
*/
|
dest.digit[i] = carry;
|
||||||
dest.digit[i] = carry;
|
|
||||||
|
zero_count := old_used - dest.used;
|
||||||
zero_count := old_used - dest.used;
|
/*
|
||||||
/*
|
Zero remainder.
|
||||||
Zero remainder.
|
*/
|
||||||
*/
|
if zero_count > 0 {
|
||||||
if zero_count > 0 {
|
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
||||||
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Adjust dest.used based on leading zeroes.
|
||||||
Adjust dest.used based on leading zeroes.
|
*/
|
||||||
*/
|
clamp(dest);
|
||||||
clamp(dest);
|
|
||||||
|
return .OK;
|
||||||
return .OK;
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|.
|
||||||
Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|.
|
Handbook of Applied Cryptography, algorithm 14.9.
|
||||||
Handbook of Applied Cryptography, algorithm 14.9.
|
*/
|
||||||
*/
|
_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) {
|
||||||
_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) {
|
dest := dest; x := number; y := decrease;
|
||||||
dest := dest; x := number; y := decrease;
|
assert_initialized(number); assert_initialized(decrease); assert_initialized(dest);
|
||||||
assert_initialized(number); assert_initialized(decrease); assert_initialized(dest);
|
|
||||||
|
old_used := dest.used;
|
||||||
old_used := dest.used;
|
min_used := y.used;
|
||||||
min_used := y.used;
|
max_used := x.used;
|
||||||
max_used := x.used;
|
i: int;
|
||||||
i: int;
|
|
||||||
|
err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT));
|
||||||
err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT));
|
if err != .OK {
|
||||||
if err != .OK {
|
return err;
|
||||||
return err;
|
}
|
||||||
}
|
dest.used = max_used;
|
||||||
dest.used = max_used;
|
|
||||||
|
borrow := DIGIT(0);
|
||||||
borrow := DIGIT(0);
|
|
||||||
|
for i = 0; i < min_used; i += 1 {
|
||||||
for i = 0; i < min_used; i += 1 {
|
dest.digit[i] = (x.digit[i] - y.digit[i] - borrow);
|
||||||
dest.digit[i] = (x.digit[i] - y.digit[i] - borrow);
|
/*
|
||||||
/*
|
borrow = carry bit of dest[i]
|
||||||
borrow = carry bit of dest[i]
|
Note this saves performing an AND operation since if a carry does occur,
|
||||||
Note this saves performing an AND operation since if a carry does occur,
|
it will propagate all the way to the MSB.
|
||||||
it will propagate all the way to the MSB.
|
As a result a single shift is enough to get the carry.
|
||||||
As a result a single shift is enough to get the carry.
|
*/
|
||||||
*/
|
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
|
||||||
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
|
/*
|
||||||
/*
|
Clear borrow from dest[i].
|
||||||
Clear borrow from dest[i].
|
*/
|
||||||
*/
|
dest.digit[i] &= _MASK;
|
||||||
dest.digit[i] &= _MASK;
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
/*
|
Now copy higher words if any, e.g. if A has more digits than B
|
||||||
Now copy higher words if any, e.g. if A has more digits than B
|
*/
|
||||||
*/
|
for ; i < max_used; i += 1 {
|
||||||
for ; i < max_used; i += 1 {
|
dest.digit[i] = x.digit[i] - borrow;
|
||||||
dest.digit[i] = x.digit[i] - borrow;
|
/*
|
||||||
/*
|
borrow = carry bit of dest[i]
|
||||||
borrow = carry bit of dest[i]
|
Note this saves performing an AND operation since if a carry does occur,
|
||||||
Note this saves performing an AND operation since if a carry does occur,
|
it will propagate all the way to the MSB.
|
||||||
it will propagate all the way to the MSB.
|
As a result a single shift is enough to get the carry.
|
||||||
As a result a single shift is enough to get the carry.
|
*/
|
||||||
*/
|
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
|
||||||
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
|
/*
|
||||||
/*
|
Clear borrow from dest[i].
|
||||||
Clear borrow from dest[i].
|
*/
|
||||||
*/
|
dest.digit[i] &= _MASK;
|
||||||
dest.digit[i] &= _MASK;
|
}
|
||||||
}
|
|
||||||
|
zero_count := old_used - dest.used;
|
||||||
zero_count := old_used - dest.used;
|
/*
|
||||||
/*
|
Zero remainder.
|
||||||
Zero remainder.
|
*/
|
||||||
*/
|
if zero_count > 0 {
|
||||||
if zero_count > 0 {
|
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
||||||
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
|
}
|
||||||
}
|
/*
|
||||||
/*
|
Adjust dest.used based on leading zeroes.
|
||||||
Adjust dest.used based on leading zeroes.
|
*/
|
||||||
*/
|
clamp(dest);
|
||||||
clamp(dest);
|
return .OK;
|
||||||
return .OK;
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package bigint
|
package big
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
@@ -35,7 +35,13 @@ _DEFAULT_SQR_KARATSUBA_CUTOFF :: 120;
|
|||||||
_DEFAULT_MUL_TOOM_CUTOFF :: 350;
|
_DEFAULT_MUL_TOOM_CUTOFF :: 350;
|
||||||
_DEFAULT_SQR_TOOM_CUTOFF :: 400;
|
_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 {
|
Sign :: enum u8 {
|
||||||
Zero_or_Positive = 0,
|
Zero_or_Positive = 0,
|
||||||
Negative = 1,
|
Negative = 1,
|
||||||
@@ -44,8 +50,8 @@ Sign :: enum u8 {
|
|||||||
Int :: struct {
|
Int :: struct {
|
||||||
used: int,
|
used: int,
|
||||||
allocated: int,
|
allocated: int,
|
||||||
sign: Sign,
|
|
||||||
digit: [dynamic]DIGIT,
|
digit: [dynamic]DIGIT,
|
||||||
|
sign: Sign,
|
||||||
};
|
};
|
||||||
|
|
||||||
Comparison_Flag :: enum i8 {
|
Comparison_Flag :: enum i8 {
|
||||||
@@ -72,7 +78,7 @@ Error :: enum i8 {
|
|||||||
Primality_Flag :: enum u8 {
|
Primality_Flag :: enum u8 {
|
||||||
Blum_Blum_Shub = 0, /* BBS style prime */
|
Blum_Blum_Shub = 0, /* BBS style prime */
|
||||||
Safe = 1, /* Safe prime (p-1)/2 == prime */
|
Safe = 1, /* Safe prime (p-1)/2 == prime */
|
||||||
Second_MSB_On = 3, /* force 2nd MSB to 1 */
|
Second_MSB_On = 3, /* force 2nd MSB to 1 */
|
||||||
};
|
};
|
||||||
Primality_Flags :: bit_set[Primality_Flag; u8];
|
Primality_Flags :: bit_set[Primality_Flag; u8];
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
odin run . -vet
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package bigint
|
package big
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//+ignore
|
//+ignore
|
||||||
package bigint
|
package big
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
@@ -60,9 +60,9 @@ demo :: proc() {
|
|||||||
defer destroy(b);
|
defer destroy(b);
|
||||||
defer destroy(c);
|
defer destroy(c);
|
||||||
|
|
||||||
a, err = init(1+4+16+64);
|
a, err = init(512);
|
||||||
|
|
||||||
b, err = init(1+2+8+32+128);
|
b, err = init(a);
|
||||||
|
|
||||||
c, err = init(-4);
|
c, err = init(-4);
|
||||||
|
|
||||||
@@ -108,4 +108,10 @@ main :: proc() {
|
|||||||
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package bigint
|
package big
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
@@ -11,7 +11,6 @@ package bigint
|
|||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
import "core:fmt"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Deallocates the backing memory of an Int.
|
Deallocates the backing memory of an Int.
|
||||||
@@ -62,7 +61,7 @@ init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size
|
|||||||
Initialize from a signed or unsigned integer.
|
Initialize from a signed or unsigned integer.
|
||||||
Inits a new `Int` and then calls the appropriate `set` routine.
|
Inits a new `Int` and then calls the appropriate `set` routine.
|
||||||
*/
|
*/
|
||||||
init_new_integer :: proc(u: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) where intrinsics.type_is_integer(T) {
|
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;
|
n := _DEFAULT_DIGIT_COUNT;
|
||||||
if minimize {
|
if minimize {
|
||||||
@@ -71,12 +70,27 @@ init_new_integer :: proc(u: $T, minimize := false, allocator_zeroes := true, all
|
|||||||
|
|
||||||
a, err = init_new(allocator_zeroes, allocator, n);
|
a, err = init_new(allocator_zeroes, allocator, n);
|
||||||
if err == .OK {
|
if err == .OK {
|
||||||
set(a, u, minimize);
|
set(a, src, minimize);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc{init_new, init_new_integer};
|
/*
|
||||||
|
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.
|
Helpers to set an `Int` to a specific value.
|
||||||
@@ -123,7 +137,7 @@ copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
|
|||||||
/*
|
/*
|
||||||
Grow `dest` to fit `src`.
|
Grow `dest` to fit `src`.
|
||||||
*/
|
*/
|
||||||
if err = grow(dest, min(src.used, _DEFAULT_DIGIT_COUNT)); err != .OK {
|
if err = grow(dest, src.used); err != .OK {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +240,9 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) {
|
|||||||
return 1 if ((a.digit[limb] & i) != 0) else 0, .OK;
|
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) {
|
extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) {
|
||||||
if count > _WORD_BITS || count < 1 {
|
if count > _WORD_BITS || count < 1 {
|
||||||
return 0, .Invalid_Input;
|
return 0, .Invalid_Input;
|
||||||
@@ -1,125 +1,123 @@
|
|||||||
package bigint
|
package big
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
Made available under Odin's BSD-2 license.
|
Made available under Odin's BSD-2 license.
|
||||||
|
|
||||||
A BigInt implementation in Odin.
|
A BigInt implementation in Odin.
|
||||||
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
|
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.
|
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:fmt"
|
log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) {
|
||||||
|
assert_initialized(a);
|
||||||
log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) {
|
if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX {
|
||||||
assert_initialized(a);
|
return -1, .Invalid_Input;
|
||||||
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.
|
||||||
/*
|
*/
|
||||||
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;
|
||||||
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`.
|
||||||
/*
|
*/
|
||||||
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 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);
|
||||||
// if (MP_HAS(S_MP_LOG)) {
|
// }
|
||||||
// return s_mp_log(a, (mp_digit)base, c);
|
|
||||||
// }
|
return -1, .Unimplemented;
|
||||||
|
}
|
||||||
return -1, .Unimplemented;
|
|
||||||
}
|
log_n :: proc{log_n_int, log_n_digit};
|
||||||
|
|
||||||
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.
|
||||||
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;
|
||||||
_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int) {
|
y: int;
|
||||||
base := base;
|
for y = 0; base & 1 == 0; {
|
||||||
y: int;
|
y += 1;
|
||||||
for y = 0; base & 1 == 0; {
|
base >>= 1;
|
||||||
y += 1;
|
}
|
||||||
base >>= 1;
|
return (count_bits(a) - 1) / y;
|
||||||
}
|
}
|
||||||
return (count_bits(a) - 1) / y;
|
|
||||||
}
|
/*
|
||||||
|
|
||||||
/*
|
*/
|
||||||
|
small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) {
|
||||||
*/
|
exponent := exponent; base := base;
|
||||||
small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) {
|
result = _WORD(1);
|
||||||
exponent := exponent; base := base;
|
|
||||||
result = _WORD(1);
|
for exponent != 0 {
|
||||||
|
if exponent & 1 == 1 {
|
||||||
for exponent != 0 {
|
result *= base;
|
||||||
if exponent & 1 == 1 {
|
}
|
||||||
result *= base;
|
exponent >>= 1;
|
||||||
}
|
base *= base;
|
||||||
exponent >>= 1;
|
}
|
||||||
base *= base;
|
return result;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int) {
|
||||||
|
/*
|
||||||
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 the number is smaller than the base, it fits within a fraction.
|
*/
|
||||||
Therefore, we return 0.
|
if a < base {
|
||||||
*/
|
return 0;
|
||||||
if a < base {
|
}
|
||||||
return 0;
|
|
||||||
}
|
/*
|
||||||
|
If a number equals the base, the log is 1.
|
||||||
/*
|
*/
|
||||||
If a number equals the base, the log is 1.
|
if a == base {
|
||||||
*/
|
return 1;
|
||||||
if a == base {
|
}
|
||||||
return 1;
|
|
||||||
}
|
N := _WORD(a);
|
||||||
|
bracket_low := _WORD(1);
|
||||||
N := _WORD(a);
|
bracket_high := _WORD(base);
|
||||||
bracket_low := _WORD(1);
|
high := 1;
|
||||||
bracket_high := _WORD(base);
|
low := 0;
|
||||||
high := 1;
|
|
||||||
low := 0;
|
for bracket_high < N {
|
||||||
|
low = high;
|
||||||
for bracket_high < N {
|
bracket_low = bracket_high;
|
||||||
low = high;
|
high <<= 1;
|
||||||
bracket_low = bracket_high;
|
bracket_high *= bracket_high;
|
||||||
high <<= 1;
|
}
|
||||||
bracket_high *= bracket_high;
|
|
||||||
}
|
for high - low > 1 {
|
||||||
|
mid := (low + high) >> 1;
|
||||||
for high - low > 1 {
|
bracket_mid := bracket_low * small_pow(_WORD(base), _WORD(mid - low));
|
||||||
mid := (low + high) >> 1;
|
|
||||||
bracket_mid := bracket_low * small_pow(_WORD(base), _WORD(mid - low));
|
if N < bracket_mid {
|
||||||
|
high = mid;
|
||||||
if N < bracket_mid {
|
bracket_high = bracket_mid;
|
||||||
high = mid;
|
}
|
||||||
bracket_high = bracket_mid;
|
if N > bracket_mid {
|
||||||
}
|
low = mid;
|
||||||
if N > bracket_mid {
|
bracket_low = bracket_mid;
|
||||||
low = mid;
|
}
|
||||||
bracket_low = bracket_mid;
|
if N == bracket_mid {
|
||||||
}
|
return mid;
|
||||||
if N == bracket_mid {
|
}
|
||||||
return mid;
|
}
|
||||||
}
|
|
||||||
}
|
if bracket_high == N {
|
||||||
|
return high;
|
||||||
if bracket_high == N {
|
} else {
|
||||||
return high;
|
return low;
|
||||||
} 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;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package bigint
|
package big
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
@@ -11,21 +11,20 @@ package bigint
|
|||||||
This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa).
|
This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:mem"
|
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:slice"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
|
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
|
||||||
*/
|
*/
|
||||||
itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
|
itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
|
||||||
|
radix := radix;
|
||||||
assert_initialized(a);
|
assert_initialized(a);
|
||||||
/*
|
/*
|
||||||
Radix defaults to 10.
|
Radix defaults to 10.
|
||||||
*/
|
*/
|
||||||
radix := radix if radix > 0 else 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.
|
TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer.
|
||||||
@@ -87,11 +86,12 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator
|
|||||||
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
|
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
|
||||||
*/
|
*/
|
||||||
itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) {
|
itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) {
|
||||||
|
radix := radix;
|
||||||
assert_initialized(a);
|
assert_initialized(a);
|
||||||
/*
|
/*
|
||||||
Radix defaults to 10.
|
Radix defaults to 10.
|
||||||
*/
|
*/
|
||||||
radix := radix if radix > 0 else 10;
|
radix = radix if radix > 0 else 10;
|
||||||
|
|
||||||
s: string;
|
s: string;
|
||||||
s, err = itoa_string(a, radix, true, allocator);
|
s, err = itoa_string(a, radix, true, allocator);
|
||||||
@@ -119,11 +119,12 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -
|
|||||||
and having to perform a buffer overflow check each character.
|
and having to perform a buffer overflow check each character.
|
||||||
*/
|
*/
|
||||||
itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
|
itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
|
||||||
|
radix := radix;
|
||||||
assert_initialized(a); size := size;
|
assert_initialized(a); size := size;
|
||||||
/*
|
/*
|
||||||
Radix defaults to 10.
|
Radix defaults to 10.
|
||||||
*/
|
*/
|
||||||
radix := radix if radix > 0 else 10;
|
radix = radix if radix > 0 else 10;
|
||||||
if radix < 2 || radix > 64 {
|
if radix < 2 || radix > 64 {
|
||||||
return 0, .Invalid_Input;
|
return 0, .Invalid_Input;
|
||||||
}
|
}
|
||||||
@@ -197,10 +198,10 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina
|
|||||||
buffer[available] = 0;
|
buffer[available] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mask := _WORD(radix - 1);
|
// mask := _WORD(radix - 1);
|
||||||
shift := int(log_n(DIGIT(radix), 2));
|
shift := int(log_n(DIGIT(radix), 2));
|
||||||
count := int(count_bits(a));
|
count := int(count_bits(a));
|
||||||
digit: _WORD;
|
// digit: _WORD;
|
||||||
|
|
||||||
for offset := 0; offset < count; offset += 4 {
|
for offset := 0; offset < count; offset += 4 {
|
||||||
bits_to_get := int(min(count - offset, shift));
|
bits_to_get := int(min(count - offset, shift));
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
@echo off
|
|
||||||
odin run .
|
|
||||||
rem -vet
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
package bigint
|
|
||||||
|
|
||||||
/*
|
|
||||||
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`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:fmt"
|
|
||||||
|
|
||||||
@private
|
|
||||||
Operator :: enum u8 {
|
|
||||||
And = 1,
|
|
||||||
Or = 2,
|
|
||||||
Xor = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
2's complement `and`, returns `dest = a & b;`
|
|
||||||
*/
|
|
||||||
|
|
||||||
_binary_op :: proc(dest, a, b: ^Int, op: Operator) -> (err: Error) {
|
|
||||||
assert_initialized(dest); assert_initialized(a); assert_initialized(b);
|
|
||||||
|
|
||||||
used := max(a.used, b.used) + 1;
|
|
||||||
neg: bool;
|
|
||||||
|
|
||||||
switch(op) {
|
|
||||||
case .And:
|
|
||||||
neg = is_neg(a) && is_neg(b);
|
|
||||||
case .Or:
|
|
||||||
neg = is_neg(a) || is_neg(b);
|
|
||||||
case .Xor:
|
|
||||||
neg = is_neg(a) != is_neg(b);
|
|
||||||
case:
|
|
||||||
return .Invalid_Input;
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(op) {
|
|
||||||
case .And:
|
|
||||||
dest.digit[i] = x & y;
|
|
||||||
case .Or:
|
|
||||||
dest.digit[i] = x | y;
|
|
||||||
case .Xor:
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
and :: proc(dest, a, b: ^Int) -> (err: Error) {
|
|
||||||
return _binary_op(dest, a, b, .And);
|
|
||||||
}
|
|
||||||
|
|
||||||
or :: proc(dest, a, b: ^Int) -> (err: Error) {
|
|
||||||
return _binary_op(dest, a, b, .Or);
|
|
||||||
}
|
|
||||||
|
|
||||||
xor :: proc(dest, a, b: ^Int) -> (err: Error) {
|
|
||||||
return _binary_op(dest, a, b, .Xor);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user