mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-08 00:28:49 +00:00
bigint: refactor to big.Int instead of bigint.Int.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package bigint
|
||||
package big
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
@@ -13,7 +13,6 @@ package bigint
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
import "core:fmt"
|
||||
|
||||
/*
|
||||
===========================
|
||||
@@ -56,7 +55,7 @@ add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) {
|
||||
dest = a + digit;
|
||||
*/
|
||||
add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
||||
dest := dest; x := a; digit := digit;
|
||||
dest := dest; digit := digit;
|
||||
assert_initialized(dest); assert_initialized(a);
|
||||
|
||||
/*
|
||||
@@ -212,7 +211,7 @@ sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) {
|
||||
dest = a - digit;
|
||||
*/
|
||||
sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
|
||||
dest := dest; x := a; digit := digit;
|
||||
dest := dest; digit := digit;
|
||||
assert_initialized(dest); assert_initialized(a);
|
||||
|
||||
/*
|
||||
@@ -1,4 +1,4 @@
|
||||
package bigint
|
||||
package big
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
@@ -35,7 +35,13 @@ _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,
|
||||
@@ -44,8 +50,8 @@ Sign :: enum u8 {
|
||||
Int :: struct {
|
||||
used: int,
|
||||
allocated: int,
|
||||
sign: Sign,
|
||||
digit: [dynamic]DIGIT,
|
||||
sign: Sign,
|
||||
};
|
||||
|
||||
Comparison_Flag :: enum i8 {
|
||||
@@ -72,7 +78,7 @@ Error :: enum i8 {
|
||||
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 */
|
||||
Second_MSB_On = 3, /* force 2nd MSB to 1 */
|
||||
};
|
||||
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>.
|
||||
@@ -1,5 +1,5 @@
|
||||
//+ignore
|
||||
package bigint
|
||||
package big
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
@@ -60,9 +60,9 @@ demo :: proc() {
|
||||
defer destroy(b);
|
||||
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);
|
||||
|
||||
@@ -108,4 +108,10 @@ main :: proc() {
|
||||
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>.
|
||||
@@ -11,7 +11,6 @@ package bigint
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
import "core:fmt"
|
||||
|
||||
/*
|
||||
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.
|
||||
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;
|
||||
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);
|
||||
if err == .OK {
|
||||
set(a, u, minimize);
|
||||
set(a, src, minimize);
|
||||
}
|
||||
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.
|
||||
@@ -123,7 +137,7 @@ copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Optimize.
|
||||
*/
|
||||
extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) {
|
||||
if count > _WORD_BITS || count < 1 {
|
||||
return 0, .Invalid_Input;
|
||||
@@ -1,4 +1,4 @@
|
||||
package bigint
|
||||
package big
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
@@ -9,8 +9,6 @@ package bigint
|
||||
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);
|
||||
if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX {
|
||||
@@ -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>.
|
||||
@@ -11,21 +11,20 @@ package bigint
|
||||
This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa).
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import "core:slice"
|
||||
|
||||
/*
|
||||
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;
|
||||
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.
|
||||
@@ -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.
|
||||
*/
|
||||
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;
|
||||
radix = radix if radix > 0 else 10;
|
||||
|
||||
s: string;
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
radix = radix if radix > 0 else 10;
|
||||
if radix < 2 || radix > 64 {
|
||||
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;
|
||||
}
|
||||
|
||||
mask := _WORD(radix - 1);
|
||||
// mask := _WORD(radix - 1);
|
||||
shift := int(log_n(DIGIT(radix), 2));
|
||||
count := int(count_bits(a));
|
||||
digit: _WORD;
|
||||
// digit: _WORD;
|
||||
|
||||
for offset := 0; offset < count; offset += 4 {
|
||||
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