aboutsummaryrefslogtreecommitdiff
path: root/core/math
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2021-07-19 23:00:08 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2021-08-11 20:59:50 +0200
commitbaef0c291d85fb9d87fa6d1db7959b34c366e361 (patch)
tree45104c80c688fe3752d703f4305b3391d607e3f7 /core/math
parentcccd29083440ae7f93b58d085e70f368293d92bc (diff)
bigint: Added some more helpers.
Diffstat (limited to 'core/math')
-rw-r--r--core/math/bigint/bigint.odin3
-rw-r--r--core/math/bigint/example.odin82
-rw-r--r--core/math/bigint/helpers.odin139
-rw-r--r--core/math/bigint/logical.odin113
4 files changed, 300 insertions, 37 deletions
diff --git a/core/math/bigint/bigint.odin b/core/math/bigint/bigint.odin
index 50934e429..77a1fcb2f 100644
--- a/core/math/bigint/bigint.odin
+++ b/core/math/bigint/bigint.odin
@@ -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 {
/*
diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin
index 38290369d..aded4b3c9 100644
--- a/core/math/bigint/example.odin
+++ b/core/math/bigint/example.odin
@@ -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);
-
- // cstr, err = itoa_cstring(a);
- // fmt.printf("cstring: %v, err: %v\n\n", cstr, err);
-
- fmt.println("=== Add ===");
- err = sub(c, a, b);
-
- 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);
+
+ print("a", a, 2);
+ print("b", b, 2);
+ print("c", c, 2);
+
+ fmt.println("=== a = a & b ===");
+ err = and(a, a, b);
+ fmt.printf("a &= b error: %v\n", err);
+
+ print("a", a, 2);
+ print("b", b, 10);
+
+ 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() {
diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin
index a96eabb2c..ecd9e4968 100644
--- a/core/math/bigint/helpers.odin
+++ b/core/math/bigint/helpers.odin
@@ -103,6 +103,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`.
*/
extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) {
@@ -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`.
*/
diff --git a/core/math/bigint/logical.odin b/core/math/bigint/logical.odin
new file mode 100644
index 000000000..bd80226eb
--- /dev/null
+++ b/core/math/bigint/logical.odin
@@ -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);
+}