diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 4e923526a..7a3bbf751 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -8,7 +8,7 @@ package big 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 basic arithmetic operations like `add` and `sub`. + This file contains basic arithmetic operations like `add`, `sub`, `div`, ... */ import "core:mem" @@ -337,6 +337,132 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { return clamp(dest); } + +/* + dest = src / 2 + dest = src >> 1 +*/ +int_halve :: proc(dest, src: ^Int) -> (err: Error) { + dest := dest; src := src; + if err = clear_if_uninitialized(dest); err != .None { + return err; + } + /* + Grow destination as required. + */ + if dest != src { + if err = grow(dest, src.used); err != .None { + return err; + } + } + + old_used := dest.used; + dest.used = src.used; + + /* + Carry + */ + fwd_carry := DIGIT(0); + + for x := dest.used; x >= 0; x -= 1 { + /* + Get the carry for the next iteration. + */ + src_digit := src.digit[x]; + carry := src_digit & 1; + /* + Shift the current digit, add in carry and store. + */ + dest.digit[x] = (src_digit >> 1) | (fwd_carry << (_DIGIT_BITS - 1)); + /* + Forward carry to next iteration. + */ + fwd_carry = carry; + } + + zero_count := old_used - dest.used; + /* + Zero remainder. + */ + if zero_count > 0 { + mem.zero_slice(dest.digit[dest.used:][:zero_count]); + } + /* + Adjust dest.used based on leading zeroes. + */ + dest.sign = src.sign; + return clamp(dest); +} +halve :: proc { int_halve, }; +shr1 :: halve; + +/* + dest = src * 2 + dest = src << 1 +*/ +int_double :: proc(dest, src: ^Int) -> (err: Error) { + dest := dest; src := src; + if err = clear_if_uninitialized(dest); err != .None { + return err; + } + /* + Grow destination as required. + */ + if dest != src { + if err = grow(dest, src.used + 1); err != .None { + return err; + } + } + + old_used := dest.used; + dest.used = src.used + 1; + + /* + Forward carry + */ + carry := DIGIT(0); + for x := 0; x < src.used; x += 1 { + /* + Get what will be the *next* carry bit from the MSB of the current digit. + */ + src_digit := src.digit[x]; + fwd_carry := src_digit >> (_DIGIT_BITS - 1); + + /* + Now shift up this digit, add in the carry [from the previous] + */ + dest.digit[x] = (src_digit << 1 | carry) & _MASK; + + /* + Update carry + */ + carry = fwd_carry; + } + /* + New leading digit? + */ + if carry != 0 { + /* + Add a MSB which is always 1 at this point. + */ + dest.digit[dest.used] = 1; + } + zero_count := old_used - dest.used; + /* + Zero remainder. + */ + if zero_count > 0 { + mem.zero_slice(dest.digit[dest.used:][:zero_count]); + } + /* + Adjust dest.used based on leading zeroes. + */ + dest.sign = src.sign; + return clamp(dest); +} +double :: proc { int_double, }; +shl1 :: double; + /* ========================== Low-level routines diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 3c90719f1..380d1423b 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -58,12 +58,17 @@ demo :: proc() { defer destroy(a, b, c); err = set(a, 512); - err = set(b, a); + err = set(b, 1); + for x := 0; x < 11; x += 1 { + err = shl1(b, b); + print("b", b, 16); + } + fmt.println(); err = set(c, -4); - print("a", a, 2); - print("b", b, 2); - print("c", c, 2); + print("a", a, 10); + print("b", b, 10); + print("c", c, 10); fmt.println("=== a = a & b ==="); err = and(a, a, b); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index af3d27e0f..471c501b2 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -11,6 +11,7 @@ package big import "core:mem" import "core:intrinsics" + /* Deallocates the backing memory of one or more `Int`s. */ @@ -366,6 +367,49 @@ count_bits :: proc(a: ^Int) -> (count: int, err: Error) { return; } +/* + Counts the number of LSBs which are zero before the first zero bit +*/ +count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } + + lnz := []u8{4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; + + q: DIGIT; + + /* + Early out for zero. + */ + if z, _ := is_zero(a); z { + return 0, .None; + } + + /* + Scan lower digits until non-zero. + */ + for count = 0; (count < a.used && a.digit[count] == 0); count += 1 {} + q = a.digit[count]; + count *= _DIGIT_BITS; + + /* + Now scan this digit until a 1 is found. + */ + if q & 1 == 0 { + p: DIGIT; + for { + p = q & 15; + count += int(lnz[p]); + q >>= 4; + if p != 0 { + break; + } + } + } + return count, .None; +} + /* Internal helpers. */ @@ -402,7 +446,6 @@ clamp :: proc(a: ^Int) -> (err: Error) { if err = clear_if_uninitialized(a); err != .None { return err; } - for a.used > 0 && a.digit[a.used - 1] == 0 { a.used -= 1; } @@ -410,6 +453,5 @@ clamp :: proc(a: ^Int) -> (err: Error) { if z, _ := is_zero(a); z { a.sign = .Zero_or_Positive; } - return .None; } \ No newline at end of file diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 47537d092..8bc468203 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -212,4 +212,36 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) { dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; return clamp(dest); -} \ No newline at end of file +} + +/* + dest = ~src +*/ +int_complement :: proc(dest, src: ^Int) -> (err: Error) { + /* + Check that src and dest are usable. + */ + if err = clear_if_uninitialized(src); err != .None { + return err; + } + if err = clear_if_uninitialized(dest); err != .None { + return err; + } + + /* + Temporarily fix sign. + */ + old_sign := src.sign; + z, _ := is_zero(src); + + src.sign = .Negative if (src.sign == .Zero_or_Positive || z) else .Zero_or_Positive; + + err = sub(dest, src, 1); + /* + Restore sign. + */ + src.sign = old_sign; + + return err; +} +complement :: proc { int_complement, }; \ No newline at end of file