diff --git a/core/math/bigint/basic.odin b/core/math/big/basic.odin similarity index 94% rename from core/math/bigint/basic.odin rename to core/math/big/basic.odin index c3b6395bb..98f14687c 100644 --- a/core/math/bigint/basic.odin +++ b/core/math/big/basic.odin @@ -1,455 +1,454 @@ -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 basic arithmetic operations like `add` and `sub`. -*/ - -import "core:mem" -import "core:intrinsics" -import "core:fmt" - -/* - =========================== - User-level routines - =========================== -*/ - -/* - High-level addition. Handles sign. -*/ -add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { - dest := dest; x := a; y := b; - assert_initialized(dest); assert_initialized(a); assert_initialized(b); - - /* - Handle both negative or both positive. - */ - if x.sign == y.sign { - dest.sign = x.sign; - return _add(dest, x, y); - } - - /* - One positive, the other negative. - Subtract the one with the greater magnitude from the other. - The result gets the sign of the one with the greater magnitude. - */ - if cmp_mag(x, y) == .Less_Than { - x, y = y, x; - } - - dest.sign = x.sign; - return _sub(dest, x, y); -} - -/* - Adds the unsigned `DIGIT` immediate to an `Int`, - such that the `DIGIT` doesn't have to be turned into an `Int` first. - - dest = a + digit; -*/ -add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { - dest := dest; x := a; digit := digit; - assert_initialized(dest); assert_initialized(a); - - /* - Fast paths for destination and input Int being the same. - */ - if dest == a { - /* - Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow. - */ - if is_pos(dest) && (dest.digit[0] + digit < _DIGIT_MAX) { - dest.digit[0] += digit; - return .OK; - } - /* - Can be subtracted from dest.digit[0] without underflow. - */ - if is_neg(a) && (dest.digit[0] > digit) { - dest.digit[0] -= digit; - return .OK; - } - } - - /* - Grow destination as required. - */ - err = grow(dest, a.used + 1); - if err != .OK { - return err; - } - - /* - If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit` - */ - if is_neg(a) && (a.used > 1 || a.digit[0] >= digit) { - /* - Temporarily fix `a`'s sign. - */ - t := a; - t.sign = .Zero_or_Positive; - /* - dest = |a| - digit - */ - err = sub(dest, t, digit); - /* - Restore sign and set `dest` sign. - */ - dest.sign = .Negative; - clamp(dest); - - return err; - } - - /* - Remember the currently used number of digits in `dest`. - */ - old_used := dest.used; - - /* - If `a` is positive - */ - if is_pos(a) { - /* - Add digits, use `carry`. - */ - i: int; - carry := digit; - for i = 0; i < a.used; i += 1 { - dest.digit[i] = a.digit[i] + carry; - carry = dest.digit[i] >> _DIGIT_BITS; - dest.digit[i] &= _MASK; - } - /* - Set final carry. - */ - dest.digit[i] = carry; - /* - Set `dest` size. - */ - dest.used = a.used + 1; - } else { - /* - `a` was negative and |a| < digit. - */ - dest.used = 1; - /* - The result is a single DIGIT. - */ - dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit; - } - /* - Sign is always positive. - */ - dest.sign = .Zero_or_Positive; - - 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. - */ - clamp(dest); - - return .OK; -} - -add :: proc{add_two_ints, add_digit}; - -/* - High-level subtraction, dest = number - decrease. Handles signs. -*/ -sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { - dest := dest; x := number; y := decrease; - assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); - - if x.sign != y.sign { - /* - Subtract a negative from a positive, OR subtract a positive from a negative. - In either case, ADD their magnitudes and use the sign of the first number. - */ - dest.sign = x.sign; - return _add(dest, x, y); - } - - /* - Subtract a positive from a positive, OR negative from a negative. - First, take the difference between their magnitudes, then... - */ - if cmp_mag(x, y) == .Less_Than { - /* - The second has a larger magnitude. - The result has the *opposite* sign from the first number. - */ - dest.sign = .Negative if is_pos(x) else .Zero_or_Positive; - x, y = y, x; - } else { - /* - The first has a larger or equal magnitude. - Copy the sign from the first. - */ - dest.sign = x.sign; - } - return _sub(dest, x, y); -} - -/* - Adds the unsigned `DIGIT` immediate to an `Int`, - such that the `DIGIT` doesn't have to be turned into an `Int` first. - - dest = a - digit; -*/ -sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { - dest := dest; x := a; digit := digit; - assert_initialized(dest); assert_initialized(a); - - /* - Fast paths for destination and input Int being the same. - */ - if dest == a { - /* - Fast path for `dest` is negative and unsigned addition doesn't overflow the lowest digit. - */ - if is_neg(dest) && (dest.digit[0] + digit < _DIGIT_MAX) { - dest.digit[0] += digit; - return .OK; - } - /* - Can be subtracted from dest.digit[0] without underflow. - */ - if is_pos(a) && (dest.digit[0] > digit) { - dest.digit[0] -= digit; - return .OK; - } - } - - /* - Grow destination as required. - */ - err = grow(dest, a.used + 1); - if err != .OK { - return err; - } - - /* - If `a` is negative, just do an unsigned addition (with fudged signs). - */ - if is_neg(a) { - t := a; - t.sign = .Zero_or_Positive; - - err = add(dest, t, digit); - dest.sign = .Negative; - - clamp(dest); - return err; - } - - old_used := dest.used; - - /* - if `a`<= digit, simply fix the single digit. - */ - if a.used == 1 && (a.digit[0] <= digit || is_zero(a)) { - dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit; - dest.sign = .Negative; - dest.used = 1; - } else { - dest.sign = .Zero_or_Positive; - dest.used = a.used; - - /* - Subtract with carry. - */ - carry := digit; - - for i := 0; i < a.used; i += 1 { - dest.digit[i] = a.digit[i] - carry; - carry := dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); - dest.digit[i] &= _MASK; - } - } - - 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. - */ - clamp(dest); - - return .OK; -} - -sub :: proc{sub_two_ints, sub_digit}; - -/* - ========================== - Low-level routines - ========================== -*/ - -/* - Low-level addition, unsigned. - Handbook of Applied Cryptography, algorithm 14.7. -*/ -_add :: proc(dest, a, b: ^Int) -> (err: Error) { - dest := dest; x := a; y := b; - assert_initialized(a); assert_initialized(b); assert_initialized(dest); - - old_used, min_used, max_used, i: int; - - if x.used < y.used { - x, y = y, x; - } - - min_used = x.used; - max_used = y.used; - old_used = dest.used; - - err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); - if err != .OK { - return err; - } - dest.used = max_used + 1; - - /* Zero the carry */ - carry := DIGIT(0); - - for i = 0; i < min_used; i += 1 { - /* - Compute the sum one _DIGIT at a time. - dest[i] = a[i] + b[i] + carry; - */ - dest.digit[i] = x.digit[i] + y.digit[i] + carry; - - /* - Compute carry - */ - carry = dest.digit[i] >> _DIGIT_BITS; - /* - Mask away carry from result digit. - */ - dest.digit[i] &= _MASK; - } - - if min_used != max_used { - /* - Now copy higher words, if any, in A+B. - If A or B has more digits, add those in. - */ - for ; i < max_used; i += 1 { - dest.digit[i] = x.digit[i] + carry; - /* - Compute carry - */ - carry = dest.digit[i] >> _DIGIT_BITS; - /* - Mask away carry from result digit. - */ - dest.digit[i] &= _MASK; - } - } - /* - Add remaining carry. - */ - dest.digit[i] = 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. - */ - clamp(dest); - - return .OK; -} - -/* - Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|. - Handbook of Applied Cryptography, algorithm 14.9. -*/ -_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { - dest := dest; x := number; y := decrease; - assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); - - old_used := dest.used; - min_used := y.used; - max_used := x.used; - i: int; - - err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); - if err != .OK { - return err; - } - dest.used = max_used; - - borrow := DIGIT(0); - - for i = 0; i < min_used; i += 1 { - dest.digit[i] = (x.digit[i] - y.digit[i] - borrow); - /* - borrow = carry bit of dest[i] - Note this saves performing an AND operation since if a carry does occur, - it will propagate all the way to the MSB. - As a result a single shift is enough to get the carry. - */ - borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); - /* - Clear borrow from dest[i]. - */ - dest.digit[i] &= _MASK; - } - - /* - Now copy higher words if any, e.g. if A has more digits than B - */ - for ; i < max_used; i += 1 { - dest.digit[i] = x.digit[i] - borrow; - /* - borrow = carry bit of dest[i] - Note this saves performing an AND operation since if a carry does occur, - it will propagate all the way to the MSB. - As a result a single shift is enough to get the carry. - */ - borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); - /* - Clear borrow from dest[i]. - */ - dest.digit[i] &= _MASK; - } - - 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. - */ - clamp(dest); - return .OK; +package big + +/* + 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 basic arithmetic operations like `add` and `sub`. +*/ + +import "core:mem" +import "core:intrinsics" + +/* + =========================== + User-level routines + =========================== +*/ + +/* + High-level addition. Handles sign. +*/ +add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { + dest := dest; x := a; y := b; + assert_initialized(dest); assert_initialized(a); assert_initialized(b); + + /* + Handle both negative or both positive. + */ + if x.sign == y.sign { + dest.sign = x.sign; + return _add(dest, x, y); + } + + /* + One positive, the other negative. + Subtract the one with the greater magnitude from the other. + The result gets the sign of the one with the greater magnitude. + */ + if cmp_mag(x, y) == .Less_Than { + x, y = y, x; + } + + dest.sign = x.sign; + return _sub(dest, x, y); +} + +/* + Adds the unsigned `DIGIT` immediate to an `Int`, + such that the `DIGIT` doesn't have to be turned into an `Int` first. + + dest = a + digit; +*/ +add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { + dest := dest; digit := digit; + assert_initialized(dest); assert_initialized(a); + + /* + Fast paths for destination and input Int being the same. + */ + if dest == a { + /* + Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow. + */ + if is_pos(dest) && (dest.digit[0] + digit < _DIGIT_MAX) { + dest.digit[0] += digit; + return .OK; + } + /* + Can be subtracted from dest.digit[0] without underflow. + */ + if is_neg(a) && (dest.digit[0] > digit) { + dest.digit[0] -= digit; + return .OK; + } + } + + /* + Grow destination as required. + */ + err = grow(dest, a.used + 1); + if err != .OK { + return err; + } + + /* + If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit` + */ + if is_neg(a) && (a.used > 1 || a.digit[0] >= digit) { + /* + Temporarily fix `a`'s sign. + */ + t := a; + t.sign = .Zero_or_Positive; + /* + dest = |a| - digit + */ + err = sub(dest, t, digit); + /* + Restore sign and set `dest` sign. + */ + dest.sign = .Negative; + clamp(dest); + + return err; + } + + /* + Remember the currently used number of digits in `dest`. + */ + old_used := dest.used; + + /* + If `a` is positive + */ + if is_pos(a) { + /* + Add digits, use `carry`. + */ + i: int; + carry := digit; + for i = 0; i < a.used; i += 1 { + dest.digit[i] = a.digit[i] + carry; + carry = dest.digit[i] >> _DIGIT_BITS; + dest.digit[i] &= _MASK; + } + /* + Set final carry. + */ + dest.digit[i] = carry; + /* + Set `dest` size. + */ + dest.used = a.used + 1; + } else { + /* + `a` was negative and |a| < digit. + */ + dest.used = 1; + /* + The result is a single DIGIT. + */ + dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit; + } + /* + Sign is always positive. + */ + dest.sign = .Zero_or_Positive; + + 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. + */ + clamp(dest); + + return .OK; +} + +add :: proc{add_two_ints, add_digit}; + +/* + High-level subtraction, dest = number - decrease. Handles signs. +*/ +sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { + dest := dest; x := number; y := decrease; + assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); + + if x.sign != y.sign { + /* + Subtract a negative from a positive, OR subtract a positive from a negative. + In either case, ADD their magnitudes and use the sign of the first number. + */ + dest.sign = x.sign; + return _add(dest, x, y); + } + + /* + Subtract a positive from a positive, OR negative from a negative. + First, take the difference between their magnitudes, then... + */ + if cmp_mag(x, y) == .Less_Than { + /* + The second has a larger magnitude. + The result has the *opposite* sign from the first number. + */ + dest.sign = .Negative if is_pos(x) else .Zero_or_Positive; + x, y = y, x; + } else { + /* + The first has a larger or equal magnitude. + Copy the sign from the first. + */ + dest.sign = x.sign; + } + return _sub(dest, x, y); +} + +/* + Adds the unsigned `DIGIT` immediate to an `Int`, + such that the `DIGIT` doesn't have to be turned into an `Int` first. + + dest = a - digit; +*/ +sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { + dest := dest; digit := digit; + assert_initialized(dest); assert_initialized(a); + + /* + Fast paths for destination and input Int being the same. + */ + if dest == a { + /* + Fast path for `dest` is negative and unsigned addition doesn't overflow the lowest digit. + */ + if is_neg(dest) && (dest.digit[0] + digit < _DIGIT_MAX) { + dest.digit[0] += digit; + return .OK; + } + /* + Can be subtracted from dest.digit[0] without underflow. + */ + if is_pos(a) && (dest.digit[0] > digit) { + dest.digit[0] -= digit; + return .OK; + } + } + + /* + Grow destination as required. + */ + err = grow(dest, a.used + 1); + if err != .OK { + return err; + } + + /* + If `a` is negative, just do an unsigned addition (with fudged signs). + */ + if is_neg(a) { + t := a; + t.sign = .Zero_or_Positive; + + err = add(dest, t, digit); + dest.sign = .Negative; + + clamp(dest); + return err; + } + + old_used := dest.used; + + /* + if `a`<= digit, simply fix the single digit. + */ + if a.used == 1 && (a.digit[0] <= digit || is_zero(a)) { + dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit; + dest.sign = .Negative; + dest.used = 1; + } else { + dest.sign = .Zero_or_Positive; + dest.used = a.used; + + /* + Subtract with carry. + */ + carry := digit; + + for i := 0; i < a.used; i += 1 { + dest.digit[i] = a.digit[i] - carry; + carry := dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); + dest.digit[i] &= _MASK; + } + } + + 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. + */ + clamp(dest); + + return .OK; +} + +sub :: proc{sub_two_ints, sub_digit}; + +/* + ========================== + Low-level routines + ========================== +*/ + +/* + Low-level addition, unsigned. + Handbook of Applied Cryptography, algorithm 14.7. +*/ +_add :: proc(dest, a, b: ^Int) -> (err: Error) { + dest := dest; x := a; y := b; + assert_initialized(a); assert_initialized(b); assert_initialized(dest); + + old_used, min_used, max_used, i: int; + + if x.used < y.used { + x, y = y, x; + } + + min_used = x.used; + max_used = y.used; + old_used = dest.used; + + err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); + if err != .OK { + return err; + } + dest.used = max_used + 1; + + /* Zero the carry */ + carry := DIGIT(0); + + for i = 0; i < min_used; i += 1 { + /* + Compute the sum one _DIGIT at a time. + dest[i] = a[i] + b[i] + carry; + */ + dest.digit[i] = x.digit[i] + y.digit[i] + carry; + + /* + Compute carry + */ + carry = dest.digit[i] >> _DIGIT_BITS; + /* + Mask away carry from result digit. + */ + dest.digit[i] &= _MASK; + } + + if min_used != max_used { + /* + Now copy higher words, if any, in A+B. + If A or B has more digits, add those in. + */ + for ; i < max_used; i += 1 { + dest.digit[i] = x.digit[i] + carry; + /* + Compute carry + */ + carry = dest.digit[i] >> _DIGIT_BITS; + /* + Mask away carry from result digit. + */ + dest.digit[i] &= _MASK; + } + } + /* + Add remaining carry. + */ + dest.digit[i] = 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. + */ + clamp(dest); + + return .OK; +} + +/* + Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|. + Handbook of Applied Cryptography, algorithm 14.9. +*/ +_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { + dest := dest; x := number; y := decrease; + assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); + + old_used := dest.used; + min_used := y.used; + max_used := x.used; + i: int; + + err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); + if err != .OK { + return err; + } + dest.used = max_used; + + borrow := DIGIT(0); + + for i = 0; i < min_used; i += 1 { + dest.digit[i] = (x.digit[i] - y.digit[i] - borrow); + /* + borrow = carry bit of dest[i] + Note this saves performing an AND operation since if a carry does occur, + it will propagate all the way to the MSB. + As a result a single shift is enough to get the carry. + */ + borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); + /* + Clear borrow from dest[i]. + */ + dest.digit[i] &= _MASK; + } + + /* + Now copy higher words if any, e.g. if A has more digits than B + */ + for ; i < max_used; i += 1 { + dest.digit[i] = x.digit[i] - borrow; + /* + borrow = carry bit of dest[i] + Note this saves performing an AND operation since if a carry does occur, + it will propagate all the way to the MSB. + As a result a single shift is enough to get the carry. + */ + borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); + /* + Clear borrow from dest[i]. + */ + dest.digit[i] &= _MASK; + } + + 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. + */ + clamp(dest); + return .OK; } \ No newline at end of file diff --git a/core/math/bigint/bigint.odin b/core/math/big/bigint.odin similarity index 91% rename from core/math/bigint/bigint.odin rename to core/math/big/bigint.odin index 77a1fcb2f..82ddaeb79 100644 --- a/core/math/bigint/bigint.odin +++ b/core/math/big/bigint.odin @@ -1,4 +1,4 @@ -package bigint +package big /* Copyright 2021 Jeroen van Rijn . @@ -35,7 +35,13 @@ _DEFAULT_SQR_KARATSUBA_CUTOFF :: 120; _DEFAULT_MUL_TOOM_CUTOFF :: 350; _DEFAULT_SQR_TOOM_CUTOFF :: 400; +/* + TODO(Jeroen): Decide whether to turn `Sign` into `Flags :: bit_set{Flag; u8}`. + This would hold the sign and float class, as appropriate, and would allow us + to set an `Int` to +/- Inf, or NaN. + The operations would need to be updated to propagate these as expected. +*/ Sign :: enum u8 { Zero_or_Positive = 0, Negative = 1, @@ -44,8 +50,8 @@ Sign :: enum u8 { Int :: struct { used: int, allocated: int, - sign: Sign, digit: [dynamic]DIGIT, + sign: Sign, }; Comparison_Flag :: enum i8 { @@ -72,7 +78,7 @@ Error :: enum i8 { Primality_Flag :: enum u8 { Blum_Blum_Shub = 0, /* BBS style prime */ Safe = 1, /* Safe prime (p-1)/2 == prime */ - Second_MSB_On = 3, /* force 2nd MSB to 1 */ + Second_MSB_On = 3, /* force 2nd MSB to 1 */ }; Primality_Flags :: bit_set[Primality_Flag; u8]; diff --git a/core/math/big/build.bat b/core/math/big/build.bat new file mode 100644 index 000000000..fa1a0d382 --- /dev/null +++ b/core/math/big/build.bat @@ -0,0 +1,2 @@ +@echo off +odin run . -vet \ No newline at end of file diff --git a/core/math/bigint/compare.odin b/core/math/big/compare.odin similarity index 99% rename from core/math/bigint/compare.odin rename to core/math/big/compare.odin index 97cb64197..facfca2e8 100644 --- a/core/math/bigint/compare.odin +++ b/core/math/big/compare.odin @@ -1,4 +1,4 @@ -package bigint +package big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/bigint/example.odin b/core/math/big/example.odin similarity index 93% rename from core/math/bigint/example.odin rename to core/math/big/example.odin index aded4b3c9..44ed91f4f 100644 --- a/core/math/bigint/example.odin +++ b/core/math/big/example.odin @@ -1,5 +1,5 @@ //+ignore -package bigint +package big /* Copyright 2021 Jeroen van Rijn . @@ -60,9 +60,9 @@ demo :: proc() { defer destroy(b); defer destroy(c); - a, err = init(1+4+16+64); + a, err = init(512); - b, err = init(1+2+8+32+128); + b, err = init(a); c, err = init(-4); @@ -108,4 +108,10 @@ main :: proc() { fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location); } } + if len(ta.bad_free_array) > 0 { + fmt.println("Bad frees:"); + for v in ta.bad_free_array { + fmt.println(v); + } + } } \ No newline at end of file diff --git a/core/math/bigint/helpers.odin b/core/math/big/helpers.odin similarity index 92% rename from core/math/bigint/helpers.odin rename to core/math/big/helpers.odin index ecd9e4968..9d21295df 100644 --- a/core/math/bigint/helpers.odin +++ b/core/math/big/helpers.odin @@ -1,4 +1,4 @@ -package bigint +package big /* Copyright 2021 Jeroen van Rijn . @@ -11,7 +11,6 @@ package bigint import "core:mem" import "core:intrinsics" -import "core:fmt" /* Deallocates the backing memory of an Int. @@ -62,7 +61,7 @@ init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size Initialize from a signed or unsigned integer. Inits a new `Int` and then calls the appropriate `set` routine. */ -init_new_integer :: proc(u: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) where intrinsics.type_is_integer(T) { +init_from_integer :: proc(src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) where intrinsics.type_is_integer(T) { n := _DEFAULT_DIGIT_COUNT; if minimize { @@ -71,12 +70,27 @@ init_new_integer :: proc(u: $T, minimize := false, allocator_zeroes := true, all a, err = init_new(allocator_zeroes, allocator, n); if err == .OK { - set(a, u, minimize); + set(a, src, minimize); } return; } -init :: proc{init_new, init_new_integer}; +/* + Initialize an `Int` as a copy from another `Int`. +*/ +init_copy :: proc(src: ^Int, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) { + if !is_initialized(src) { + return nil, .Invalid_Input; + } + + a, err = init_new(allocator_zeroes, allocator, src.used); + if err == .OK { + copy(a, src); + } + return; +} + +init :: proc{init_new, init_from_integer, init_copy}; /* Helpers to set an `Int` to a specific value. @@ -123,7 +137,7 @@ copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Grow `dest` to fit `src`. */ - if err = grow(dest, min(src.used, _DEFAULT_DIGIT_COUNT)); err != .OK { + if err = grow(dest, src.used); err != .OK { return err; } @@ -226,6 +240,9 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { return 1 if ((a.digit[limb] & i) != 0) else 0, .OK; } +/* + TODO: Optimize. +*/ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { if count > _WORD_BITS || count < 1 { return 0, .Invalid_Input; diff --git a/core/math/bigint/log.odin b/core/math/big/log.odin similarity index 94% rename from core/math/bigint/log.odin rename to core/math/big/log.odin index 980b5a7cb..d4b794344 100644 --- a/core/math/bigint/log.odin +++ b/core/math/big/log.odin @@ -1,125 +1,123 @@ -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. -*/ - -import "core:fmt" - -log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { - assert_initialized(a); - if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX { - return -1, .Invalid_Input; - } - - /* - Fast path for bases that are a power of two. - */ - if is_power_of_two(int(base)) { - return _log_power_of_two(a, base), .OK; - } - - /* - Fast path for `Int`s that fit within a single `DIGIT`. - */ - if a.used == 1 { - return log_n_digit(a.digit[0], DIGIT(base)), .OK; - } - - // if (MP_HAS(S_MP_LOG)) { - // return s_mp_log(a, (mp_digit)base, c); - // } - - return -1, .Unimplemented; -} - -log_n :: proc{log_n_int, log_n_digit}; - -/* - Returns the log2 of an `Int`, provided `base` is a power of two. - Don't call it if it isn't. -*/ -_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int) { - base := base; - y: int; - for y = 0; base & 1 == 0; { - y += 1; - base >>= 1; - } - return (count_bits(a) - 1) / y; -} - -/* - -*/ -small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { - exponent := exponent; base := base; - result = _WORD(1); - - for exponent != 0 { - if exponent & 1 == 1 { - result *= base; - } - exponent >>= 1; - base *= base; - } - return result; -} - -log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int) { - /* - If the number is smaller than the base, it fits within a fraction. - Therefore, we return 0. - */ - if a < base { - return 0; - } - - /* - If a number equals the base, the log is 1. - */ - if a == base { - return 1; - } - - N := _WORD(a); - bracket_low := _WORD(1); - bracket_high := _WORD(base); - high := 1; - low := 0; - - for bracket_high < N { - low = high; - bracket_low = bracket_high; - high <<= 1; - bracket_high *= bracket_high; - } - - for high - low > 1 { - mid := (low + high) >> 1; - bracket_mid := bracket_low * small_pow(_WORD(base), _WORD(mid - low)); - - if N < bracket_mid { - high = mid; - bracket_high = bracket_mid; - } - if N > bracket_mid { - low = mid; - bracket_low = bracket_mid; - } - if N == bracket_mid { - return mid; - } - } - - if bracket_high == N { - return high; - } else { - return low; - } +package big + +/* + 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. +*/ + +log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { + assert_initialized(a); + if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX { + return -1, .Invalid_Input; + } + + /* + Fast path for bases that are a power of two. + */ + if is_power_of_two(int(base)) { + return _log_power_of_two(a, base), .OK; + } + + /* + Fast path for `Int`s that fit within a single `DIGIT`. + */ + if a.used == 1 { + return log_n_digit(a.digit[0], DIGIT(base)), .OK; + } + + // if (MP_HAS(S_MP_LOG)) { + // return s_mp_log(a, (mp_digit)base, c); + // } + + return -1, .Unimplemented; +} + +log_n :: proc{log_n_int, log_n_digit}; + +/* + Returns the log2 of an `Int`, provided `base` is a power of two. + Don't call it if it isn't. +*/ +_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int) { + base := base; + y: int; + for y = 0; base & 1 == 0; { + y += 1; + base >>= 1; + } + return (count_bits(a) - 1) / y; +} + +/* + +*/ +small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { + exponent := exponent; base := base; + result = _WORD(1); + + for exponent != 0 { + if exponent & 1 == 1 { + result *= base; + } + exponent >>= 1; + base *= base; + } + return result; +} + +log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int) { + /* + If the number is smaller than the base, it fits within a fraction. + Therefore, we return 0. + */ + if a < base { + return 0; + } + + /* + If a number equals the base, the log is 1. + */ + if a == base { + return 1; + } + + N := _WORD(a); + bracket_low := _WORD(1); + bracket_high := _WORD(base); + high := 1; + low := 0; + + for bracket_high < N { + low = high; + bracket_low = bracket_high; + high <<= 1; + bracket_high *= bracket_high; + } + + for high - low > 1 { + mid := (low + high) >> 1; + bracket_mid := bracket_low * small_pow(_WORD(base), _WORD(mid - low)); + + if N < bracket_mid { + high = mid; + bracket_high = bracket_mid; + } + if N > bracket_mid { + low = mid; + bracket_low = bracket_mid; + } + if N == bracket_mid { + return mid; + } + } + + if bracket_high == N { + return high; + } else { + return low; + } } \ No newline at end of file diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin new file mode 100644 index 000000000..9ecb4fba3 --- /dev/null +++ b/core/math/big/logical.odin @@ -0,0 +1,206 @@ +package big + +/* + 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`. +*/ + +/* + The `and`, `or` and `xor` binops differ in two lines only. + We could handle those with a switch, but that adds overhead. +*/ + +/* + 2's complement `and`, returns `dest = a & b;` +*/ +and :: proc(dest, a, b: ^Int) -> (err: Error) { + assert_initialized(dest); assert_initialized(a); assert_initialized(b); + + used := max(a.used, b.used) + 1; + neg: bool; + + neg = is_neg(a) && is_neg(b); + + 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]; + } + + 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; +} + +/* + 2's complement `or`, returns `dest = a | b;` +*/ +or :: proc(dest, a, b: ^Int) -> (err: Error) { + assert_initialized(dest); assert_initialized(a); assert_initialized(b); + + used := max(a.used, b.used) + 1; + neg: bool; + + neg = is_neg(a) || is_neg(b); + + 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]; + } + + 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; +} + +/* + 2's complement `xor`, returns `dest = a ~ b;` +*/ +xor :: proc(dest, a, b: ^Int) -> (err: Error) { + assert_initialized(dest); assert_initialized(a); assert_initialized(b); + + used := max(a.used, b.used) + 1; + neg: bool; + + neg = is_neg(a) != is_neg(b); + + 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]; + } + + 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; +} \ No newline at end of file diff --git a/core/math/bigint/radix.odin b/core/math/big/radix.odin similarity index 97% rename from core/math/bigint/radix.odin rename to core/math/big/radix.odin index 7cacdbc56..be1be7d10 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/big/radix.odin @@ -1,4 +1,4 @@ -package bigint +package big /* Copyright 2021 Jeroen van Rijn . @@ -11,21 +11,20 @@ package bigint This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa). */ -import "core:mem" import "core:intrinsics" import "core:fmt" import "core:strings" -import "core:slice" /* This version of `itoa` allocates one behalf of the caller. The caller must free the string. */ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { + radix := radix; assert_initialized(a); /* Radix defaults to 10. */ - radix := radix if radix > 0 else 10; + radix = radix if radix > 0 else 10; /* TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer. @@ -87,11 +86,12 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator This version of `itoa` allocates one behalf of the caller. The caller must free the string. */ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) { + radix := radix; assert_initialized(a); /* Radix defaults to 10. */ - radix := radix if radix > 0 else 10; + radix = radix if radix > 0 else 10; s: string; s, err = itoa_string(a, radix, true, allocator); @@ -119,11 +119,12 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) - and having to perform a buffer overflow check each character. */ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) { + radix := radix; assert_initialized(a); size := size; /* Radix defaults to 10. */ - radix := radix if radix > 0 else 10; + radix = radix if radix > 0 else 10; if radix < 2 || radix > 64 { return 0, .Invalid_Input; } @@ -197,10 +198,10 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina buffer[available] = 0; } - mask := _WORD(radix - 1); + // mask := _WORD(radix - 1); shift := int(log_n(DIGIT(radix), 2)); count := int(count_bits(a)); - digit: _WORD; + // digit: _WORD; for offset := 0; offset < count; offset += 4 { bits_to_get := int(min(count - offset, shift)); diff --git a/core/math/bigint/build.bat b/core/math/bigint/build.bat deleted file mode 100644 index ccc547948..000000000 --- a/core/math/bigint/build.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -odin run . -rem -vet \ No newline at end of file diff --git a/core/math/bigint/logical.odin b/core/math/bigint/logical.odin deleted file mode 100644 index bd80226eb..000000000 --- a/core/math/bigint/logical.odin +++ /dev/null @@ -1,113 +0,0 @@ -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); -}