bigint: Added some more helpers.

This commit is contained in:
Jeroen van Rijn
2021-07-19 23:00:08 +02:00
parent cccd290834
commit baef0c291d
4 changed files with 296 additions and 33 deletions

View File

@@ -93,7 +93,8 @@ _MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS);
- Must be small enough such that `_radix_size` for base 2 does not overflow.
`_radix_size` needs two additional bytes for zero termination and sign.
*/
_MAX_DIGIT_COUNT :: (max(int) - 2) / _DIGIT_BITS;
_MAX_BIT_COUNT :: (max(int) - 2);
_MAX_DIGIT_COUNT :: _MAX_BIT_COUNT / _DIGIT_BITS;
when size_of(rawptr) == 8 {
/*

View File

@@ -41,48 +41,58 @@ _SQR_TOOM_CUTOFF,
fmt.println();
}
print :: proc(name: string, a: ^Int, base := i8(16)) {
as, err := itoa(a, base);
defer delete(as);
if err == .OK {
fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, count_bits(a), as);
} else {
fmt.printf("%v (error: %v): %v\n", name, err, a);
}
}
demo :: proc() {
a, b, c: ^Int;
as, bs, cs: string;
err: Error;
a, b, c: ^Int;
err: Error;
a, err = init();
a.digit[2] = 512;
a.used = 3;
defer destroy(a);
as, err = itoa(a, 16);
fmt.printf("a: %v, err: %v\n\n", as, err);
delete(as);
b, err = init(42);
defer destroy(b);
bs, err = itoa(b, 16);
fmt.printf("b: %s, err: %v\n\n", bs, err);
delete(bs);
defer destroy(c);
a, err = init(1+4+16+64);
b, err = init(1+2+8+32+128);
c, err = init(-4);
defer destroy(c);
cs, err = itoa(c, 16);
fmt.printf("c: %s, err: %v\n\n", cs, err);
delete(cs);
// cstr: cstring;
// defer delete(cstr);
print("a", a, 2);
print("b", b, 2);
print("c", c, 2);
// cstr, err = itoa_cstring(a);
// fmt.printf("cstring: %v, err: %v\n\n", cstr, err);
fmt.println("=== a = a & b ===");
err = and(a, a, b);
fmt.printf("a &= b error: %v\n", err);
fmt.println("=== Add ===");
err = sub(c, a, b);
print("a", a, 2);
print("b", b, 10);
fmt.printf("Error: %v\n", err);
as, err = itoa(a, 16);
bs, err = itoa(b, 16);
cs, err = itoa(c, 16);
fmt.printf("a: %v, bits: %v\n", as, count_bits(a));
fmt.printf("b: %v, bits: %v\n", bs, count_bits(b));
fmt.printf("c: %v, bits: %v\n", cs, count_bits(c));
delete(as); delete(bs); delete(cs);
fmt.println("\n\n=== b = abs(c) ===");
c.sign = .Negative;
abs(b, c); // copy c to b.
print("b", b);
print("c", c);
fmt.println("\n\n=== Set a to (1 << 120) - 1 ===");
if err = power_of_two(a, 120); err != .OK {
fmt.printf("Error %v while setting a to 1 << 120.\n", err);
}
if err = sub(a, a, 1); err != .OK {
fmt.printf("Error %v while subtracting 1 from a\n", err);
}
print("a", a, 16);
fmt.println("Expected a to be: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
}
main :: proc() {

View File

@@ -102,6 +102,116 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location)
set :: proc{set_integer};
/*
Copy one `Int` to another.
*/
copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
/*
If dest == src, do nothing
*/
if (dest == src) {
return .OK;
}
/*
Check they're both initialized.
*/
if !(is_initialized(dest) && is_initialized(src)) {
return .Invalid_Input;
}
/*
Grow `dest` to fit `src`.
*/
if err = grow(dest, min(src.used, _DEFAULT_DIGIT_COUNT)); err != .OK {
return err;
}
/*
Copy everything over and zero high digits.
*/
assert(dest.allocated >= src.used);
for v, i in src.digit[:src.used+1] {
dest.digit[i] = v;
}
dest.used = src.used;
dest.sign = src.sign;
_zero_unused(dest);
return .OK;
}
/*
Set `dest` to |`src`|.
*/
abs_bigint :: proc(dest, src: ^Int) -> (err: Error) {
/*
If `dest == src`, just fix `dest`'s sign.
*/
if (dest == src) {
dest.sign = .Zero_or_Positive;
return .OK;
}
/*
Check they're both initialized.
*/
if !(is_initialized(dest) && is_initialized(src)) {
return .Invalid_Input;
}
/*
Copy `src` to `dest`
*/
if err = copy(dest, src); err != .OK {
return err;
}
/*
Fix sign.
*/
dest.sign = .Zero_or_Positive;
return .OK;
}
abs_integer :: proc(n: $T) -> T where intrinsics.type_is_integer(T) {
return n if n >= 0 else -n;
}
abs :: proc{abs_bigint, abs_integer};
/*
Set `dest` to `-src`.
*/
neg :: proc(dest, src: ^Int) -> (err: Error) {
/*
If `dest == src`, just fix `dest`'s sign.
*/
sign := Sign.Negative if !(is_zero(src) && is_neg(src)) else Sign.Zero_or_Positive;
if dest == src {
dest.sign = sign;
return .OK;
}
/*
Check they're both initialized.
*/
if !(is_initialized(dest) && is_initialized(src)) {
return .Invalid_Input;
}
/*
Copy `src` to `dest`
*/
if err = copy(dest, src); err != .OK {
return err;
}
/*
Fix sign.
*/
dest.sign = sign;
return .OK;
}
/*
Helpers to extract values from the `Int`.
*/
@@ -245,6 +355,35 @@ minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) {
return .OK;
}
power_of_two :: proc(a: ^Int, power: int) -> (err: Error) {
assert_initialized(a);
/*
*/
if power < 0 || power > _MAX_BIT_COUNT {
return .Invalid_Input;
}
/*
Grow to accomodate the single bit.
*/
a.used = (power / _DIGIT_BITS) + 1;
if err = grow(a, a.used); err != .OK {
return err;
}
/*
Zero the entirety.
*/
mem.zero_slice(a.digit[:]);
/*
Set the bit.
*/
a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS));
return .OK;
}
/*
Count bits in an `Int`.
*/

View File

@@ -0,0 +1,113 @@
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);
}