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); + 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() { 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 @@ -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`. */ 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 . + 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); +}