mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-03 03:32:37 +00:00
bigint: Added some more helpers.
This commit is contained in:
@@ -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 {
|
||||
/*
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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`.
|
||||
*/
|
||||
|
||||
113
core/math/bigint/logical.odin
Normal file
113
core/math/bigint/logical.odin
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user