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

This commit is contained in:
Jeroen van Rijn
2021-08-11 20:59:50 +02:00
parent baef0c291d
commit 9dba17cf87
11 changed files with 834 additions and 715 deletions
@@ -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];
+2
View File
@@ -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;
}
} }
+206
View File
@@ -0,0 +1,206 @@
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains logical operations like `and`, `or` and `xor`.
*/
/*
The `and`, `or` and `xor` binops differ in two lines only.
We could handle those with a switch, but that adds overhead.
*/
/*
2's complement `and`, returns `dest = a & b;`
*/
and :: proc(dest, a, b: ^Int) -> (err: Error) {
assert_initialized(dest); assert_initialized(a); assert_initialized(b);
used := max(a.used, b.used) + 1;
neg: bool;
neg = is_neg(a) && is_neg(b);
ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1);
/*
Grow the destination to accomodate the result.
*/
if err = grow(dest, used); err != .OK {
return err;
}
for i := 0; i < used; i += 1 {
x, y: DIGIT;
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK;
ac >>= _DIGIT_BITS;
} else {
x = 0 if i >= a.used else a.digit[i];
}
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK;
bc >>= _DIGIT_BITS;
} else {
y = 0 if i >= b.used else b.digit[i];
}
dest.digit[i] = x & y;
/*
Convert to to sign-magnitude if negative.
*/
if neg {
cc += ~dest.digit[i] & _MASK;
dest.digit[i] = cc & _MASK;
cc >>= _DIGIT_BITS;
}
}
dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest);
return .OK;
}
/*
2's complement `or`, returns `dest = a | b;`
*/
or :: proc(dest, a, b: ^Int) -> (err: Error) {
assert_initialized(dest); assert_initialized(a); assert_initialized(b);
used := max(a.used, b.used) + 1;
neg: bool;
neg = is_neg(a) || is_neg(b);
ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1);
/*
Grow the destination to accomodate the result.
*/
if err = grow(dest, used); err != .OK {
return err;
}
for i := 0; i < used; i += 1 {
x, y: DIGIT;
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK;
ac >>= _DIGIT_BITS;
} else {
x = 0 if i >= a.used else a.digit[i];
}
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK;
bc >>= _DIGIT_BITS;
} else {
y = 0 if i >= b.used else b.digit[i];
}
dest.digit[i] = x | y;
/*
Convert to to sign-magnitude if negative.
*/
if neg {
cc += ~dest.digit[i] & _MASK;
dest.digit[i] = cc & _MASK;
cc >>= _DIGIT_BITS;
}
}
dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest);
return .OK;
}
/*
2's complement `xor`, returns `dest = a ~ b;`
*/
xor :: proc(dest, a, b: ^Int) -> (err: Error) {
assert_initialized(dest); assert_initialized(a); assert_initialized(b);
used := max(a.used, b.used) + 1;
neg: bool;
neg = is_neg(a) != is_neg(b);
ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1);
/*
Grow the destination to accomodate the result.
*/
if err = grow(dest, used); err != .OK {
return err;
}
for i := 0; i < used; i += 1 {
x, y: DIGIT;
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
ac += _MASK if i >= a.used else (~a.digit[i] & _MASK);
x = ac & _MASK;
ac >>= _DIGIT_BITS;
} else {
x = 0 if i >= a.used else a.digit[i];
}
/*
Convert to 2's complement if negative.
*/
if is_neg(a) {
bc += _MASK if i >= b.used else (~b.digit[i] & _MASK);
y = bc & _MASK;
bc >>= _DIGIT_BITS;
} else {
y = 0 if i >= b.used else b.digit[i];
}
dest.digit[i] = x ~ y;
/*
Convert to to sign-magnitude if negative.
*/
if neg {
cc += ~dest.digit[i] & _MASK;
dest.digit[i] = cc & _MASK;
cc >>= _DIGIT_BITS;
}
}
dest.used = used;
dest.sign = .Negative if neg else .Zero_or_Positive;
clamp(dest);
return .OK;
}
@@ -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));
-3
View File
@@ -1,3 +0,0 @@
@echo off
odin run .
rem -vet
-113
View File
@@ -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);
}