From 18dda6ff9d8f67f259bafbcdfe5fd5286f5a45fa Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 15 Jul 2021 23:34:02 +0200 Subject: [PATCH 001/105] Start of core:math/bigint We have: - `init` to create a new `Int` - `init(from_integer)` to create a new `Int` and set it to `from_integer`. - `set(Int, from_integer)` to set an `Int` to `from_integer` - `add(dest, a, b)` to add `a` and `b` into `dest`. - `sub(dest, a, b)` to subtract `b` from `a` and put the result in `dest`. And a few helper functions, like: - `is_zero`, `is_negative`, ... - `grow`, `shrink`, `clear`, `zero` --- core/math/bigint/basic.odin | 196 ++++++++++++++++++++++++++++ core/math/bigint/bigint.odin | 115 ++++++++++++++++ core/math/bigint/build.bat | 2 + core/math/bigint/compares.odin | 98 ++++++++++++++ core/math/bigint/example.odin | 50 +++++++ core/math/bigint/helpers.odin | 232 +++++++++++++++++++++++++++++++++ 6 files changed, 693 insertions(+) create mode 100644 core/math/bigint/basic.odin create mode 100644 core/math/bigint/bigint.odin create mode 100644 core/math/bigint/build.bat create mode 100644 core/math/bigint/compares.odin create mode 100644 core/math/bigint/example.odin create mode 100644 core/math/bigint/helpers.odin diff --git a/core/math/bigint/basic.odin b/core/math/bigint/basic.odin new file mode 100644 index 000000000..56ac692ff --- /dev/null +++ b/core/math/bigint/basic.odin @@ -0,0 +1,196 @@ +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:mem" +import "core:intrinsics" + +/* + High-level addition. Handles sign. +*/ +add :: proc(dest, a, b: ^Int) -> (err: Error) { + dest := dest; x := a; y := b; + _panic_if_uninitialized(a); _panic_if_uninitialized(b); _panic_if_uninitialized(dest); + + /* + 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); +} + +/* + 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; + _panic_if_uninitialized(a); _panic_if_uninitialized(b); _panic_if_uninitialized(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. Assumes |a| > |b|. + Handbook of Applied Cryptography, algorithm 14.9. +*/ +_sub :: proc(dest, a, b: ^Int) -> (err: Error) { + dest := dest; x := a; y := b; + _panic_if_uninitialized(a); _panic_if_uninitialized(b); _panic_if_uninitialized(dest); + + for n in 0..=12 { + dest.digit[n] = DIGIT(n); + dest.used = n+1; + } + + old_used := dest.used; + min_used := y.used; + max_used := a.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] >> (_DIGIT_BITS - 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] >> (_DIGIT_BITS - 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/bigint/bigint.odin new file mode 100644 index 000000000..1445bc69d --- /dev/null +++ b/core/math/bigint/bigint.odin @@ -0,0 +1,115 @@ +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. +*/ + +/* + Tunables +*/ +_LOW_MEMORY :: #config(BIGINT_SMALL_MEMORY, false); +when _LOW_MEMORY { + _DEFAULT_DIGIT_COUNT :: 8; +} else { + _DEFAULT_DIGIT_COUNT :: 32; +} + +// /* tunable cutoffs */ +// #ifndef MP_FIXED_CUTOFFS +// extern int +// MP_MUL_KARATSUBA_CUTOFF, +// MP_SQR_KARATSUBA_CUTOFF, +// MP_MUL_TOOM_CUTOFF, +// MP_SQR_TOOM_CUTOFF; +// #endif + +Sign :: enum u8 { + Zero_or_Positive = 0, + Negative = 1, +}; + +Int :: struct { + used: int, + allocated: int, + sign: Sign, + digit: [dynamic]DIGIT, +}; + +Comparison_Flag :: enum i8 { + Less_Than = -1, + Equal = 0, + Greater_Than = 1, + + /* One of the numbers was uninitialized */ + Uninitialized = -127, +}; + +Error :: enum i8 { + OK = 0, + Unknown_Error = -1, + Out_of_Memory = -2, + Invalid_Input = -3, + Max_Iterations_Reached = -4, + Buffer_Overflow = -5, + Integer_Overflow = -6, + + Unimplemented = -127, +}; + +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 */ +}; +Primality_Flags :: bit_set[Primality_Flag; u8]; + +/* + How do we store the Ints? + + Minimum number of available digits in `Int`, `_DEFAULT_DIGIT_COUNT` >= `_MIN_DIGIT_COUNT` + - Must be at least 3 for `_div_school`. + - Must be large enough such that `init_integer` can store `u128` in the `Int` without growing. + */ + +_MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS); +#assert(_DEFAULT_DIGIT_COUNT >= _MIN_DIGIT_COUNT); + +/* + Maximum number of digits. + - Must be small enough such that `_bit_count` does not overflow. + - 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; + +when size_of(rawptr) == 8 { + /* + We can use u128 as an intermediary. + */ + DIGIT :: distinct(u64); + _DIGIT_BITS :: 60; + _WORD :: u128; +} else { + DIGIT :: distinct(u32); + _DIGIT_BITS :: 28; + _WORD :: u64; +} +#assert(size_of(_WORD) == 2 * size_of(DIGIT)); +_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1); +_DIGIT_MAX :: _MASK; + +Order :: enum i8 { + LSB_First = -1, + MSB_First = 1, +}; + +Endianness :: enum i8 { + Little = -1, + Platform = 0, + Big = 1, +}; \ No newline at end of file diff --git a/core/math/bigint/build.bat b/core/math/bigint/build.bat new file mode 100644 index 000000000..df9cd1e85 --- /dev/null +++ b/core/math/bigint/build.bat @@ -0,0 +1,2 @@ +@echo off +odin run . -vet \ No newline at end of file diff --git a/core/math/bigint/compares.odin b/core/math/bigint/compares.odin new file mode 100644 index 000000000..1838d901e --- /dev/null +++ b/core/math/bigint/compares.odin @@ -0,0 +1,98 @@ +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:intrinsics" + +is_initialized :: proc(a: ^Int) -> bool { + return a != rawptr(uintptr(0)); +} + +is_zero :: proc(a: ^Int) -> bool { + return is_initialized(a) && a.used == 0; +} + +is_positive :: proc(a: ^Int) -> bool { + return is_initialized(a) && a.sign == .Zero_or_Positive; +} +is_pos :: is_positive;; + +is_negative :: proc(a: ^Int) -> bool { + return is_initialized(a) && a.sign == .Negative; +} +is_neg :: is_negative; + +/* + Compare two `Int`s, signed. +*/ +compare :: proc(a, b: ^Int) -> Comparison_Flag { + if !is_initialized(a) { return .Uninitialized; } + if !is_initialized(b) { return .Uninitialized; } + + /* Compare based on sign */ + if a.sign != b.sign { + return .Less_Than if is_negative(a) else .Greater_Than; + } + + x, y := a, b; + /* If negative, compare in the opposite direction */ + if is_neg(a) { + x, y = b, a; + } + return cmp_mag(x, y); +} +cmp :: compare; + +/* + Compare the magnitude of two `Int`s, unsigned. +*/ +compare_magnitude :: proc(a, b: ^Int) -> Comparison_Flag { + if !is_initialized(a) { return .Uninitialized; } + if !is_initialized(b) { return .Uninitialized; } + + /* Compare based on used digits */ + if a.used != b.used { + return .Greater_Than if a.used > b.used else .Less_Than; + } + + /* Same number of used digits, compare based on their value */ + for n := a.used - 1; n >= 0; n -= 1 { + if a.digit[n] != b.digit[n] { + return .Greater_Than if a.digit[n] > b.digit[n] else .Less_Than; + } + } + + return .Equal; +} +cmp_mag :: compare_magnitude; + +/* + Compare an `Int` to an unsigned number upto the size of the backing type. +*/ +compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag { + if !is_initialized(a) { return .Uninitialized; } + + /* Compare based on sign */ + if is_neg(a) { + return .Less_Than; + } + + /* Compare based on magnitude */ + if a.used > 1 { + return .Greater_Than; + } + + /* Compare the only digit in `a` to `u`. */ + if a.digit[0] != u { + return .Greater_Than if a.digit[0] > u else .Less_Than; + } + + return .Equal; +} \ No newline at end of file diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin new file mode 100644 index 000000000..4b89b193d --- /dev/null +++ b/core/math/bigint/example.odin @@ -0,0 +1,50 @@ +//+ignore +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" +import "core:mem" + +demo :: proc() { + a, b, c: ^Int; + err: Error; + + a, err = init(-21); + defer destroy(a); + fmt.printf("a: %v, err: %v\n\n", a, err); + + b, err = init(42); + defer destroy(b); + + fmt.printf("b: %v, err: %v\n\n", b, err); + + c, err = init(); + defer destroy(c); + + err = add(c, a, b); + fmt.printf("c: %v, err: %v\n\n", c, err); +} + +main :: proc() { + ta := mem.Tracking_Allocator{}; + mem.tracking_allocator_init(&ta, context.allocator); + context.allocator = mem.tracking_allocator(&ta); + + fmt.printf("_DIGIT_BITS: %v\n_MIN_DIGIT_COUNT: %v\n_MAX_DIGIT_COUNT: %v\n_DEFAULT_DIGIT_COUNT: %v\n\n", _DIGIT_BITS, _MIN_DIGIT_COUNT, _MAX_DIGIT_COUNT, _DEFAULT_DIGIT_COUNT); + + demo(); + + if len(ta.allocation_map) > 0 { + for _, v in ta.allocation_map { + fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location); + } + } +} \ No newline at end of file diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin new file mode 100644 index 000000000..f96942954 --- /dev/null +++ b/core/math/bigint/helpers.odin @@ -0,0 +1,232 @@ +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:mem" +import "core:intrinsics" + +/* + Deallocates the backing memory of an Int. +*/ + +destroy :: proc(a: ^Int, allocator_zeroes := false, free_int := true, loc := #caller_location) { + if !is_initialized(a) { + // Nothing to do. + return; + } + + if !allocator_zeroes { + mem.zero_slice(a.digit[:]); + } + free(&a.digit[0]); + a.used = 0; + a.allocated = 0; + if free_int { + free(a); + } +} + +/* + Creates and returns a new `Int`. +*/ + +init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (a: ^Int, err: Error) { + /* + Allocating a new variable. + */ + a = new(Int, allocator); + + a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, size, size, allocator); + a.allocated = 0; + a.used = 0; + a.sign = .Zero_or_Positive; + + if len(a.digit) != size { + return a, .Out_of_Memory; + } + a.allocated = size; + + if !allocator_zeroes { + _zero_unused(a); + } + return a, .OK; +} + +/* + 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) { + + n := _DEFAULT_DIGIT_COUNT; + if minimize { + n = _MIN_DIGIT_COUNT; + } + + a, err = init_new(allocator_zeroes, allocator, n); + if err == .OK { + set(a, u, minimize); + } + return; +} + +init :: proc{init_new, init_new_integer}; + +/* + Helpers to set an `Int` to a specific value. +*/ + +set_integer :: proc(a: ^Int, n: $T, minimize := false) where intrinsics.type_is_integer(T) { + n := n; + _panic_if_uninitialized(a); + + a.used = 0; + a.sign = .Zero_or_Positive if n >= 0 else .Negative; + n = abs(n); + for n != 0 { + a.digit[a.used] = DIGIT(n) & _MASK; + a.used += 1; + n >>= _DIGIT_BITS; + } + if minimize { + shrink(a); + } + + _zero_unused(a); +} + +set :: proc{set_integer}; + +/* + Resize backing store. +*/ + +shrink :: proc(a: ^Int) -> (err: Error) { + needed := max(_MIN_DIGIT_COUNT, a.used); + + if a.used != needed { + return grow(a, needed); + } + return .OK; +} + +grow :: proc(a: ^Int, n: int) -> (err: Error) { + _panic_if_uninitialized(a); + + resize(&a.digit, n); + if len(a.digit) != n { + return .Out_of_Memory; + } + + a.used = min(n, a.used); + a.allocated = n; + return .OK; +} + +/* + Clear `Int` and resize it to the default size. +*/ +clear :: proc(a: ^Int) -> (err: Error) { + _panic_if_uninitialized(a); + + mem.zero_slice(a.digit[:]); + a.sign = .Zero_or_Positive; + a.used = 0; + grow(a, _DEFAULT_DIGIT_COUNT); + + return .OK; +} + +/* + Set the `Int` to 0 and optionally shrink it to the minimum backing size. +*/ +zero :: proc(a: ^Int, minimize := false) -> (err: Error) { + _panic_if_uninitialized(a); + + mem.zero_slice(a.digit[:]); + a.sign = .Zero_or_Positive; + a.used = 0; + if minimize { + return shrink(a); + } + + return .OK; +} + +/* + Set the `Int` to 1 and optionally shrink it to the minimum backing size. +*/ +one :: proc(a: ^Int, minimize := false) -> (err: Error) { + _panic_if_uninitialized(a); + + mem.zero_slice(a.digit[:]); + a.sign = .Zero_or_Positive; + a.used = 1; + a.digit[0] = 1; + if minimize { + return shrink(a); + } + + return .OK; +} + +/* + Set the `Int` to -1 and optionally shrink it to the minimum backing size. +*/ +minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) { + _panic_if_uninitialized(a); + + mem.zero_slice(a.digit[:]); + a.sign = .Negative; + a.used = 1; + a.digit[0] = 1; + if minimize { + return shrink(a); + } + + return .OK; +} + +/* + Internal helpers. +*/ + +_panic_if_uninitialized :: proc(a: ^Int, loc := #caller_location) { + if !is_initialized(a) { + panic("Int was not properly initialized.", loc); + } +} + +_zero_unused :: proc(a: ^Int) { + _panic_if_uninitialized(a); + if a.used < a.allocated { + mem.zero_slice(a.digit[a.used:]); + } +} + +clamp :: proc(a: ^Int) { + _panic_if_uninitialized(a); + /* + Trim unused digits + This is used to ensure that leading zero digits are + trimmed and the leading "used" digit will be non-zero. + Typically very fast. Also fixes the sign if there + are no more leading digits. + */ + + for a.used > 0 && a.digit[a.used - 1] == 0 { + a.used -= 1; + } + + if is_zero(a) { + a.sign = .Zero_or_Positive; + } +} \ No newline at end of file From d57e1be89f05ef4d898043d13edeabb4c90da458 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 16 Jul 2021 01:01:48 +0200 Subject: [PATCH 002/105] bigint: Improve `add`. --- core/math/bigint/basic.odin | 65 +++++++++++++++++++++++++++++------ core/math/bigint/example.odin | 6 ++-- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/core/math/bigint/basic.odin b/core/math/bigint/basic.odin index 56ac692ff..c6a36f60f 100644 --- a/core/math/bigint/basic.odin +++ b/core/math/bigint/basic.odin @@ -12,6 +12,12 @@ package bigint import "core:mem" import "core:intrinsics" +/* + =========================== + User-level routines + =========================== +*/ + /* High-level addition. Handles sign. */ @@ -40,6 +46,49 @@ add :: proc(dest, a, b: ^Int) -> (err: Error) { return _sub(dest, x, y); } +/* + High-level subtraction, dest = number - decrease. Handles signs. +*/ +sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { + dest := dest; x := number; y := decrease; + _panic_if_uninitialized(number); _panic_if_uninitialized(decrease); _panic_if_uninitialized(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); +} + +/* + ========================== + Low-level routines + ========================== +*/ + /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -121,23 +170,17 @@ _add :: proc(dest, a, b: ^Int) -> (err: Error) { return .OK; } - /* - Low-level subtraction. Assumes |a| > |b|. + Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|. Handbook of Applied Cryptography, algorithm 14.9. */ -_sub :: proc(dest, a, b: ^Int) -> (err: Error) { - dest := dest; x := a; y := b; - _panic_if_uninitialized(a); _panic_if_uninitialized(b); _panic_if_uninitialized(dest); - - for n in 0..=12 { - dest.digit[n] = DIGIT(n); - dest.used = n+1; - } +_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { + dest := dest; x := number; y := decrease; + _panic_if_uninitialized(number); _panic_if_uninitialized(decrease); _panic_if_uninitialized(dest); old_used := dest.used; min_used := y.used; - max_used := a.used; + max_used := x.used; i: int; err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 4b89b193d..0b57d7a8d 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -17,11 +17,11 @@ demo :: proc() { a, b, c: ^Int; err: Error; - a, err = init(-21); + a, err = init(21); defer destroy(a); fmt.printf("a: %v, err: %v\n\n", a, err); - b, err = init(42); + b, err = init(-21); defer destroy(b); fmt.printf("b: %v, err: %v\n\n", b, err); @@ -29,7 +29,7 @@ demo :: proc() { c, err = init(); defer destroy(c); - err = add(c, a, b); + err = sub(c, a, b); fmt.printf("c: %v, err: %v\n\n", c, err); } From c5cbd3260ab4f6e971c22cae1a33191604ee69eb Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 16 Jul 2021 01:51:57 +0200 Subject: [PATCH 003/105] bigint: Add prototypes for immediate add+sub. --- core/math/bigint/basic.odin | 54 ++++++++++++++----- .../bigint/{compares.odin => compare.odin} | 0 2 files changed, 40 insertions(+), 14 deletions(-) rename core/math/bigint/{compares.odin => compare.odin} (100%) diff --git a/core/math/bigint/basic.odin b/core/math/bigint/basic.odin index c6a36f60f..bf002e7d5 100644 --- a/core/math/bigint/basic.odin +++ b/core/math/bigint/basic.odin @@ -14,14 +14,14 @@ import "core:intrinsics" /* =========================== - User-level routines + User-level routines =========================== */ /* High-level addition. Handles sign. */ -add :: proc(dest, a, b: ^Int) -> (err: Error) { +add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; _panic_if_uninitialized(a); _panic_if_uninitialized(b); _panic_if_uninitialized(dest); @@ -34,22 +34,35 @@ add :: proc(dest, a, b: ^Int) -> (err: Error) { } /* - 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; - } + 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); + 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) { + + return .Unimplemented; +} + +add :: proc{add_two_ints, add_digit}; + /* High-level subtraction, dest = number - decrease. Handles signs. */ -sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { +sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; _panic_if_uninitialized(number); _panic_if_uninitialized(decrease); _panic_if_uninitialized(dest); @@ -83,9 +96,22 @@ sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { 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 = number - decrease; +*/ +sub_digit :: proc(dest, number: ^int, decrease: DIGIT) -> (err: Error) { + + return .Unimplemented; +} + +sub :: proc{sub_two_ints, sub_digit}; + /* ========================== - Low-level routines + Low-level routines ========================== */ @@ -235,5 +261,5 @@ _sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { Adjust dest.used based on leading zeroes. */ clamp(dest); - return .OK; + return .OK; } \ No newline at end of file diff --git a/core/math/bigint/compares.odin b/core/math/bigint/compare.odin similarity index 100% rename from core/math/bigint/compares.odin rename to core/math/bigint/compare.odin From c2c07f07db5be2fa0c4d7ebb8832a192681d87d9 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 16 Jul 2021 19:31:25 +0200 Subject: [PATCH 004/105] Add single DIGIT addition. --- core/math/bigint/basic.odin | 122 ++++++++++++++++++++++++++++++++-- core/math/bigint/example.odin | 29 ++++++-- core/math/bigint/helpers.odin | 74 +++++++++++++-------- 3 files changed, 186 insertions(+), 39 deletions(-) diff --git a/core/math/bigint/basic.odin b/core/math/bigint/basic.odin index bf002e7d5..866124327 100644 --- a/core/math/bigint/basic.odin +++ b/core/math/bigint/basic.odin @@ -11,6 +11,7 @@ package bigint import "core:mem" import "core:intrinsics" +import "core:fmt" /* =========================== @@ -23,7 +24,7 @@ import "core:intrinsics" */ add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - _panic_if_uninitialized(a); _panic_if_uninitialized(b); _panic_if_uninitialized(dest); + assert_initialized(dest); assert_initialized(a); assert_initialized(b); /* Handle both negative or both positive. @@ -52,9 +53,116 @@ add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { dest = a + digit; */ -add_digit :: proc(dest, a: ^int, digit: DIGIT) -> (err: Error) { +add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { + dest := dest; x := a; digit := digit; + assert_initialized(dest); assert_initialized(a); - return .Unimplemented; + /* + 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. + */ + a.sign = .Zero_or_Positive; + /* + dest = |a| - digit + */ + err = sub(dest, a, digit); + /* + Restore sign and set `dest` sign. + */ + dest.sign = .Negative; + a.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}; @@ -64,7 +172,7 @@ add :: proc{add_two_ints, add_digit}; */ sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - _panic_if_uninitialized(number); _panic_if_uninitialized(decrease); _panic_if_uninitialized(dest); + assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); if x.sign != y.sign { /* @@ -102,7 +210,7 @@ sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest = number - decrease; */ -sub_digit :: proc(dest, number: ^int, decrease: DIGIT) -> (err: Error) { +sub_digit :: proc(dest, number: ^Int, decrease: DIGIT) -> (err: Error) { return .Unimplemented; } @@ -121,7 +229,7 @@ sub :: proc{sub_two_ints, sub_digit}; */ _add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - _panic_if_uninitialized(a); _panic_if_uninitialized(b); _panic_if_uninitialized(dest); + assert_initialized(a); assert_initialized(b); assert_initialized(dest); old_used, min_used, max_used, i: int; @@ -202,7 +310,7 @@ _add :: proc(dest, a, b: ^Int) -> (err: Error) { */ _sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - _panic_if_uninitialized(number); _panic_if_uninitialized(decrease); _panic_if_uninitialized(dest); + assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); old_used := dest.used; min_used := y.used; diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 0b57d7a8d..0c88a6e1a 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -13,24 +13,43 @@ package bigint import "core:fmt" import "core:mem" +print_int :: proc(a: ^Int, print_raw := false) -> string { + if print_raw { + return fmt.tprintf("%v", a); + } + sign := "-" if a.sign == .Negative else ""; + if a.used <= 2 { + v := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); + return fmt.tprintf("%v%v", sign, v); + } else { + return fmt.tprintf("[%2d/%2d] %v%v", a.used, a.allocated, sign, a.digit[:a.used]); + } +} + demo :: proc() { a, b, c: ^Int; err: Error; a, err = init(21); defer destroy(a); - fmt.printf("a: %v, err: %v\n\n", a, err); + fmt.printf("a: %v, err: %v\n\n", print_int(a), err); - b, err = init(-21); + b, err = init(21); defer destroy(b); - fmt.printf("b: %v, err: %v\n\n", b, err); + fmt.printf("b: %v, err: %v\n\n", print_int(b), err); c, err = init(); defer destroy(c); + fmt.printf("c: %v\n", print_int(c, true)); - err = sub(c, a, b); - fmt.printf("c: %v, err: %v\n\n", c, err); + fmt.println("=== Add ==="); + err = add(c, a, DIGIT(42)); + // err = add(c, a, b); + fmt.printf("Error: %v\n", err); + fmt.printf("a: %v\n", print_int(a)); + fmt.printf("b: %v\n", print_int(b)); + fmt.printf("c: %v\n", print_int(c)); } main :: proc() { diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin index f96942954..170beb1bc 100644 --- a/core/math/bigint/helpers.odin +++ b/core/math/bigint/helpers.odin @@ -11,6 +11,7 @@ package bigint import "core:mem" import "core:intrinsics" +import "core:fmt" /* Deallocates the backing memory of an Int. @@ -84,9 +85,9 @@ init :: proc{init_new, init_new_integer}; Helpers to set an `Int` to a specific value. */ -set_integer :: proc(a: ^Int, n: $T, minimize := false) where intrinsics.type_is_integer(T) { +set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) where intrinsics.type_is_integer(T) { n := n; - _panic_if_uninitialized(a); + assert_initialized(a, loc); a.used = 0; a.sign = .Zero_or_Positive if n >= 0 else .Negative; @@ -118,24 +119,45 @@ shrink :: proc(a: ^Int) -> (err: Error) { return .OK; } -grow :: proc(a: ^Int, n: int) -> (err: Error) { - _panic_if_uninitialized(a); +grow :: proc(a: ^Int, n: int, allow_shrink := false) -> (err: Error) { + assert_initialized(a); + /* + By default, calling `grow` with `n` <= a.allocated won't resize. + With `allow_shrink` set to `true`, will call resize and shrink the `Int` as a result. + */ - resize(&a.digit, n); - if len(a.digit) != n { - return .Out_of_Memory; - } + /* + We need at least _MIN_DIGIT_COUNT or a.used digits, whichever is bigger. + */ + needed := max(_MIN_DIGIT_COUNT, a.used); + /* + The caller is asking for `n`. Let's be accomodating. + */ + needed = max(needed, n); + /* + If `allow_shrink` == `false`, we need to needed >= `a.allocated`. + */ + if !allow_shrink { + needed = max(needed, a.allocated); + } - a.used = min(n, a.used); - a.allocated = n; - return .OK; + if a.allocated != needed { + resize(&a.digit, needed); + if len(a.digit) != needed { + return .Out_of_Memory; + } + } + + // a.used = min(size, a.used); + a.allocated = needed; + return .OK; } /* Clear `Int` and resize it to the default size. */ clear :: proc(a: ^Int) -> (err: Error) { - _panic_if_uninitialized(a); + assert_initialized(a); mem.zero_slice(a.digit[:]); a.sign = .Zero_or_Positive; @@ -149,11 +171,11 @@ clear :: proc(a: ^Int) -> (err: Error) { Set the `Int` to 0 and optionally shrink it to the minimum backing size. */ zero :: proc(a: ^Int, minimize := false) -> (err: Error) { - _panic_if_uninitialized(a); + assert_initialized(a); - mem.zero_slice(a.digit[:]); a.sign = .Zero_or_Positive; a.used = 0; + mem.zero_slice(a.digit[a.used:]); if minimize { return shrink(a); } @@ -165,12 +187,12 @@ zero :: proc(a: ^Int, minimize := false) -> (err: Error) { Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ one :: proc(a: ^Int, minimize := false) -> (err: Error) { - _panic_if_uninitialized(a); + assert_initialized(a); - mem.zero_slice(a.digit[:]); a.sign = .Zero_or_Positive; a.used = 1; a.digit[0] = 1; + mem.zero_slice(a.digit[a.used:]); if minimize { return shrink(a); } @@ -182,12 +204,12 @@ one :: proc(a: ^Int, minimize := false) -> (err: Error) { Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) { - _panic_if_uninitialized(a); + assert_initialized(a); - mem.zero_slice(a.digit[:]); a.sign = .Negative; a.used = 1; a.digit[0] = 1; + mem.zero_slice(a.digit[a.used:]); if minimize { return shrink(a); } @@ -199,27 +221,25 @@ minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) { Internal helpers. */ -_panic_if_uninitialized :: proc(a: ^Int, loc := #caller_location) { - if !is_initialized(a) { - panic("Int was not properly initialized.", loc); - } +assert_initialized :: proc(a: ^Int, loc := #caller_location) { + assert(is_initialized(a), "`Int` was not properly initialized.", loc); } _zero_unused :: proc(a: ^Int) { - _panic_if_uninitialized(a); + assert_initialized(a); if a.used < a.allocated { mem.zero_slice(a.digit[a.used:]); } } clamp :: proc(a: ^Int) { - _panic_if_uninitialized(a); + assert_initialized(a); /* Trim unused digits - This is used to ensure that leading zero digits are - trimmed and the leading "used" digit will be non-zero. + This is used to ensure that leading zero digits are + trimmed and the leading "used" digit will be non-zero. Typically very fast. Also fixes the sign if there - are no more leading digits. + are no more leading digits. */ for a.used > 0 && a.digit[a.used - 1] == 0 { From daceaa65f553556d18aa908e5a8b3512f4b73ddf Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 16 Jul 2021 21:29:10 +0200 Subject: [PATCH 005/105] bigint: Add substractin with immediate. --- core/math/bigint/basic.odin | 99 +++++++++++++++++++++++++++++++---- core/math/bigint/build.bat | 3 +- core/math/bigint/example.odin | 6 +-- 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/core/math/bigint/basic.odin b/core/math/bigint/basic.odin index 866124327..8bab5b50d 100644 --- a/core/math/bigint/basic.odin +++ b/core/math/bigint/basic.odin @@ -89,20 +89,20 @@ add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit` */ if is_neg(a) && (a.used > 1 || a.digit[0] >= digit) { + fmt.print("a = neg, %v\n", print_int(a)); /* Temporarily fix `a`'s sign. */ - a.sign = .Zero_or_Positive; + t := a; + t.sign = .Zero_or_Positive; /* dest = |a| - digit */ - err = sub(dest, a, digit); + err = sub(dest, t, digit); /* Restore sign and set `dest` sign. */ dest.sign = .Negative; - a.sign = .Negative; - clamp(dest); return err; @@ -208,11 +208,92 @@ sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { Adds the unsigned `DIGIT` immediate to an `Int`, such that the `DIGIT` doesn't have to be turned into an `Int` first. - dest = number - decrease; + dest = a - digit; */ -sub_digit :: proc(dest, number: ^Int, decrease: DIGIT) -> (err: Error) { +sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { + dest := dest; x := a; digit := digit; + assert_initialized(dest); assert_initialized(a); - return .Unimplemented; + /* + 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}; @@ -333,7 +414,7 @@ _sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { 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] >> (_DIGIT_BITS - 1); + borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); /* Clear borrow from dest[i]. */ @@ -351,7 +432,7 @@ _sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { 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] >> (_DIGIT_BITS - 1); + borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); /* Clear borrow from dest[i]. */ diff --git a/core/math/bigint/build.bat b/core/math/bigint/build.bat index df9cd1e85..ccc547948 100644 --- a/core/math/bigint/build.bat +++ b/core/math/bigint/build.bat @@ -1,2 +1,3 @@ @echo off -odin run . -vet \ No newline at end of file +odin run . +rem -vet \ No newline at end of file diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 0c88a6e1a..69dab47dd 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -30,11 +30,11 @@ demo :: proc() { a, b, c: ^Int; err: Error; - a, err = init(21); + a, err = init(512); defer destroy(a); fmt.printf("a: %v, err: %v\n\n", print_int(a), err); - b, err = init(21); + b, err = init(42); defer destroy(b); fmt.printf("b: %v, err: %v\n\n", print_int(b), err); @@ -44,7 +44,7 @@ demo :: proc() { fmt.printf("c: %v\n", print_int(c, true)); fmt.println("=== Add ==="); - err = add(c, a, DIGIT(42)); + err = sub(a, a, DIGIT(42)); // err = add(c, a, b); fmt.printf("Error: %v\n", err); fmt.printf("a: %v\n", print_int(a)); From dfd5a993a28c404d2b6f5b06ab6c3663b40cf3de Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 17 Jul 2021 00:03:40 +0200 Subject: [PATCH 006/105] bigint: Prepare for multiplication. --- core/math/bigint/bigint.odin | 31 +++++++++++++++++++++++-------- core/math/bigint/example.odin | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/core/math/bigint/bigint.odin b/core/math/bigint/bigint.odin index 1445bc69d..6e146b0cb 100644 --- a/core/math/bigint/bigint.odin +++ b/core/math/bigint/bigint.odin @@ -19,14 +19,20 @@ when _LOW_MEMORY { _DEFAULT_DIGIT_COUNT :: 32; } -// /* tunable cutoffs */ -// #ifndef MP_FIXED_CUTOFFS -// extern int -// MP_MUL_KARATSUBA_CUTOFF, -// MP_SQR_KARATSUBA_CUTOFF, -// MP_MUL_TOOM_CUTOFF, -// MP_SQR_TOOM_CUTOFF; -// #endif +_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); +_SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); +_MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); +_SQR_TOOM_CUTOFF :: #config(SQR_TOOM_CUTOFF, _DEFAULT_SQR_TOOM_CUTOFF); + +/* + These defaults were tuned on an AMD A8-6600K (64-bit) using libTomMath's `make tune`. + TODO(Jeroen): Port this tuning algorithm and tune them for more modern processors. +*/ +_DEFAULT_MUL_KARATSUBA_CUTOFF :: 80; +_DEFAULT_SQR_KARATSUBA_CUTOFF :: 120; +_DEFAULT_MUL_TOOM_CUTOFF :: 350; +_DEFAULT_SQR_TOOM_CUTOFF :: 400; + Sign :: enum u8 { Zero_or_Positive = 0, @@ -94,15 +100,24 @@ when size_of(rawptr) == 8 { DIGIT :: distinct(u64); _DIGIT_BITS :: 60; _WORD :: u128; + _MAX_COMBA :: 1 << (128 - (2 * _DIGIT_BITS)) ; + _WARRAY :: 1 << ((128 - (2 * _DIGIT_BITS)) + 1); } else { DIGIT :: distinct(u32); _DIGIT_BITS :: 28; _WORD :: u64; + _MAX_COMBA :: 1 << ( 64 - (2 * _DIGIT_BITS)) ; + _WARRAY :: 1 << (( 64 - (2 * _DIGIT_BITS)) + 1); } #assert(size_of(_WORD) == 2 * size_of(DIGIT)); _MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1); _DIGIT_MAX :: _MASK; +_BITS_IN_BYTE :: 8; +_BITS_IN_TYPE :: #force_inline proc($T) -> int where intrinsics.type_is_integer(T) { + return _BITS_IN_BYTE * size_of(T); +} + Order :: enum i8 { LSB_First = -1, MSB_First = 1, diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 69dab47dd..082e695c3 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -13,6 +13,34 @@ package bigint import "core:fmt" import "core:mem" +print_configation :: proc() { + fmt.printf( +`Configuration: + DIGIT_BITS %v + MIN_DIGIT_COUNT %v + MAX_DIGIT_COUNT %v + EFAULT_DIGIT_COUNT %v + MAX_COMBA %v + WARRAY %v + MUL_KARATSUBA_CUTOFF %v + SQR_KARATSUBA_CUTOFF %v + MUL_TOOM_CUTOFF %v + SQR_TOOM_CUTOFF %v +`, _DIGIT_BITS, +_MIN_DIGIT_COUNT, +_MAX_DIGIT_COUNT, +_DEFAULT_DIGIT_COUNT, +_MAX_COMBA, +_WARRAY, +_MUL_KARATSUBA_CUTOFF, +_SQR_KARATSUBA_CUTOFF, +_MUL_TOOM_CUTOFF, +_SQR_TOOM_CUTOFF, +); + + fmt.println(); +} + print_int :: proc(a: ^Int, print_raw := false) -> string { if print_raw { return fmt.tprintf("%v", a); @@ -44,7 +72,7 @@ demo :: proc() { fmt.printf("c: %v\n", print_int(c, true)); fmt.println("=== Add ==="); - err = sub(a, a, DIGIT(42)); + err = sub(c, a, b); // err = add(c, a, b); fmt.printf("Error: %v\n", err); fmt.printf("a: %v\n", print_int(a)); @@ -57,8 +85,7 @@ main :: proc() { mem.tracking_allocator_init(&ta, context.allocator); context.allocator = mem.tracking_allocator(&ta); - fmt.printf("_DIGIT_BITS: %v\n_MIN_DIGIT_COUNT: %v\n_MAX_DIGIT_COUNT: %v\n_DEFAULT_DIGIT_COUNT: %v\n\n", _DIGIT_BITS, _MIN_DIGIT_COUNT, _MAX_DIGIT_COUNT, _DEFAULT_DIGIT_COUNT); - + // print_configation(); demo(); if len(ta.allocation_map) > 0 { From 905d5459a94da7f6901220d4acfd9d9a916869df Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 17 Jul 2021 01:04:27 +0200 Subject: [PATCH 007/105] bigint: Add `count_bits` and more prep. --- core/math/bigint/basic.odin | 1 - core/math/bigint/bigint.odin | 31 +++++++++++++++---------------- core/math/bigint/example.odin | 6 +++--- core/math/bigint/helpers.odin | 23 +++++++++++++++++++++++ 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/core/math/bigint/basic.odin b/core/math/bigint/basic.odin index 8bab5b50d..a174799ba 100644 --- a/core/math/bigint/basic.odin +++ b/core/math/bigint/basic.odin @@ -89,7 +89,6 @@ add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit` */ if is_neg(a) && (a.used > 1 || a.digit[0] >= digit) { - fmt.print("a = neg, %v\n", print_int(a)); /* Temporarily fix `a`'s sign. */ diff --git a/core/math/bigint/bigint.odin b/core/math/bigint/bigint.odin index 6e146b0cb..8f6cb1cf7 100644 --- a/core/math/bigint/bigint.odin +++ b/core/math/bigint/bigint.odin @@ -9,6 +9,8 @@ package bigint The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. */ +import "core:intrinsics" + /* Tunables */ @@ -97,26 +99,23 @@ when size_of(rawptr) == 8 { /* We can use u128 as an intermediary. */ - DIGIT :: distinct(u64); - _DIGIT_BITS :: 60; - _WORD :: u128; - _MAX_COMBA :: 1 << (128 - (2 * _DIGIT_BITS)) ; - _WARRAY :: 1 << ((128 - (2 * _DIGIT_BITS)) + 1); + DIGIT :: distinct(u64); + _WORD :: distinct(u128); } else { - DIGIT :: distinct(u32); - _DIGIT_BITS :: 28; - _WORD :: u64; - _MAX_COMBA :: 1 << ( 64 - (2 * _DIGIT_BITS)) ; - _WARRAY :: 1 << (( 64 - (2 * _DIGIT_BITS)) + 1); + DIGIT :: distinct(u32); + _WORD :: distinct(u64); } #assert(size_of(_WORD) == 2 * size_of(DIGIT)); -_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1); -_DIGIT_MAX :: _MASK; -_BITS_IN_BYTE :: 8; -_BITS_IN_TYPE :: #force_inline proc($T) -> int where intrinsics.type_is_integer(T) { - return _BITS_IN_BYTE * size_of(T); -} +_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT); +_WORD_TYPE_BITS :: 8 * size_of(_WORD); + +_DIGIT_BITS :: _DIGIT_TYPE_BITS - 4; + +_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1); +_DIGIT_MAX :: _MASK; +_MAX_COMBA :: 1 << (_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) ; +_WARRAY :: 1 << ((_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) + 1); Order :: enum i8 { LSB_First = -1, diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 082e695c3..145a8fc75 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -75,9 +75,9 @@ demo :: proc() { err = sub(c, a, b); // err = add(c, a, b); fmt.printf("Error: %v\n", err); - fmt.printf("a: %v\n", print_int(a)); - fmt.printf("b: %v\n", print_int(b)); - fmt.printf("c: %v\n", print_int(c)); + fmt.printf("a: %v, bits: %v\n", print_int(a), count_bits(a)); + fmt.printf("b: %v, bits: %v\n", print_int(b), count_bits(b)); + fmt.printf("c: %v, bits: %v\n", print_int(c), count_bits(c)); } main :: proc() { diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin index 170beb1bc..3d25230f2 100644 --- a/core/math/bigint/helpers.odin +++ b/core/math/bigint/helpers.odin @@ -217,6 +217,29 @@ minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) { return .OK; } +/* + Count bits in an `Int`. +*/ +count_bits :: proc(a: ^Int) -> (count: int) { + assert_initialized(a); + /* + Fast path for zero. + */ + if is_zero(a) { + return 0; + } + /* + Get the number of DIGITs and use it. + */ + count = (a.used - 1) * _DIGIT_BITS; + /* + Take the last DIGIT and count the bits in it. + */ + clz := int(intrinsics.count_leading_zeros(a.digit[a.used - 1])); + count += (_DIGIT_TYPE_BITS - clz); + return; +} + /* Internal helpers. */ From dbcd8da733c24be71e80b69db77de1e5215a1439 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 17 Jul 2021 15:40:57 +0200 Subject: [PATCH 008/105] bigint: Working on `itoa` and `logn`. --- core/math/bigint/basic.odin | 2 + core/math/bigint/compare.odin | 23 ++++++++++ core/math/bigint/example.odin | 43 +++++++++---------- core/math/bigint/helpers.odin | 6 --- core/math/bigint/log.odin | 46 ++++++++++++++++++++ core/math/bigint/radix.odin | 80 +++++++++++++++++++++++++++++++++++ 6 files changed, 172 insertions(+), 28 deletions(-) create mode 100644 core/math/bigint/log.odin create mode 100644 core/math/bigint/radix.odin diff --git a/core/math/bigint/basic.odin b/core/math/bigint/basic.odin index a174799ba..c3b6395bb 100644 --- a/core/math/bigint/basic.odin +++ b/core/math/bigint/basic.odin @@ -7,6 +7,8 @@ package bigint 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" diff --git a/core/math/bigint/compare.odin b/core/math/bigint/compare.odin index 1838d901e..9d48c4706 100644 --- a/core/math/bigint/compare.odin +++ b/core/math/bigint/compare.odin @@ -29,6 +29,29 @@ is_negative :: proc(a: ^Int) -> bool { } is_neg :: is_negative; +is_even :: proc(a: ^Int) -> bool { + if is_initialized(a) { + if is_zero(a) { + return true; + } + if a.used > 0 && a.digit[0] & 1 == 0 { + return true; + } + } + return false; +} + +is_odd :: proc(a: ^Int) -> bool { + if is_initialized(a) { + return !is_even(a); + } + return false; +} + +is_power_of_two :: proc(x: int) -> bool { + return ((x) != 0) && (((x) & ((x) - 1)) == 0); +} + /* Compare two `Int`s, signed. */ diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 145a8fc75..2d3f5e12d 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -41,43 +41,42 @@ _SQR_TOOM_CUTOFF, fmt.println(); } -print_int :: proc(a: ^Int, print_raw := false) -> string { - if print_raw { - return fmt.tprintf("%v", a); - } - sign := "-" if a.sign == .Negative else ""; - if a.used <= 2 { - v := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); - return fmt.tprintf("%v%v", sign, v); - } else { - return fmt.tprintf("[%2d/%2d] %v%v", a.used, a.allocated, sign, a.digit[:a.used]); - } -} - demo :: proc() { - a, b, c: ^Int; + a, b, c: ^Int; + as, bs, cs: string; err: Error; a, err = init(512); defer destroy(a); - fmt.printf("a: %v, err: %v\n\n", print_int(a), err); + as, err = itoa(a, 10); + fmt.printf("a: %v, err: %v\n\n", as, err); + delete(as); b, err = init(42); defer destroy(b); - - fmt.printf("b: %v, err: %v\n\n", print_int(b), err); + bs, err = itoa(b, 10); + fmt.printf("b: %v, err: %v\n\n", bs, err); + delete(bs); c, err = init(); defer destroy(c); - fmt.printf("c: %v\n", print_int(c, true)); + cs, err = itoa(c, 10); + fmt.printf("c: %v\n", cs); + delete(cs); fmt.println("=== Add ==="); err = sub(c, a, b); - // err = add(c, a, b); + fmt.printf("Error: %v\n", err); - fmt.printf("a: %v, bits: %v\n", print_int(a), count_bits(a)); - fmt.printf("b: %v, bits: %v\n", print_int(b), count_bits(b)); - fmt.printf("c: %v, bits: %v\n", print_int(c), count_bits(c)); + as, err = itoa(a, 10); + bs, err = itoa(b, 10); + cs, err = itoa(c, 10); + 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("log2:", log_n(a, 8)); } main :: proc() { diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin index 3d25230f2..869bd3e26 100644 --- a/core/math/bigint/helpers.odin +++ b/core/math/bigint/helpers.odin @@ -16,7 +16,6 @@ import "core:fmt" /* Deallocates the backing memory of an Int. */ - destroy :: proc(a: ^Int, allocator_zeroes := false, free_int := true, loc := #caller_location) { if !is_initialized(a) { // Nothing to do. @@ -37,7 +36,6 @@ destroy :: proc(a: ^Int, allocator_zeroes := false, free_int := true, loc := #ca /* Creates and returns a new `Int`. */ - init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (a: ^Int, err: Error) { /* Allocating a new variable. @@ -64,7 +62,6 @@ 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) { n := _DEFAULT_DIGIT_COUNT; @@ -84,7 +81,6 @@ init :: proc{init_new, init_new_integer}; /* Helpers to set an `Int` to a specific value. */ - set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) where intrinsics.type_is_integer(T) { n := n; assert_initialized(a, loc); @@ -109,7 +105,6 @@ set :: proc{set_integer}; /* Resize backing store. */ - shrink :: proc(a: ^Int) -> (err: Error) { needed := max(_MIN_DIGIT_COUNT, a.used); @@ -243,7 +238,6 @@ count_bits :: proc(a: ^Int) -> (count: int) { /* Internal helpers. */ - assert_initialized :: proc(a: ^Int, loc := #caller_location) { assert(is_initialized(a), "`Int` was not properly initialized.", loc); } diff --git a/core/math/bigint/log.odin b/core/math/bigint/log.odin new file mode 100644 index 000000000..fd8116b78 --- /dev/null +++ b/core/math/bigint/log.odin @@ -0,0 +1,46 @@ +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. +*/ + +log_n :: proc(a: ^Int, base: int) -> (log: int, err: Error) { + assert_initialized(a); + if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX { + return -1, .Invalid_Input; + } + + if is_power_of_two(base) { + return _log_power_of_two(a, base), .OK; + } + + // if (MP_HAS(S_MP_LOG_D) && (a->used == 1)) { + // *c = s_mp_log_d((mp_digit)base, a->dp[0]); + // return MP_OKAY; + // } + + // if (MP_HAS(S_MP_LOG)) { + // return s_mp_log(a, (mp_digit)base, c); + // } + + return -1, .Unimplemented; +} + +/* + 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: int) -> (log: int) { + base := base; + y: int; + for y = 0; base & 1 == 0; { + y += 1; + base >>= 1; + } + return (count_bits(a) - 1) / y; +} diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin new file mode 100644 index 000000000..5bfb6b9a0 --- /dev/null +++ b/core/math/bigint/radix.odin @@ -0,0 +1,80 @@ +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 radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa). +*/ + +import "core:mem" +import "core:intrinsics" +import "core:fmt" +import "core:strings" + +itoa :: proc(a: ^Int, radix: int, allocator := context.allocator) -> (res: string, err: Error) { + assert_initialized(a); + + if radix < 2 || radix > 64 { + return strings.clone("", allocator), .Invalid_Input; + } + + /* + Fast path for radixes that are a power of two. + */ + if radix & 1 == 0 { + + + } + + + + + fallback :: proc(a: ^Int, print_raw := false) -> string { + if print_raw { + return fmt.tprintf("%v", a); + } + sign := "-" if a.sign == .Negative else ""; + if a.used <= 2 { + v := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); + return fmt.tprintf("%v%v", sign, v); + } else { + return fmt.tprintf("[%2d/%2d] %v%v", a.used, a.allocated, sign, a.digit[:a.used]); + } + } + return strings.clone(fallback(a), allocator), .Unimplemented; +} + +int_to_string :: itoa; + + +radix_size :: proc(a: ^Int, base: int) -> (size: int, err: Error) { + // mp_err err; + // mp_int a_; + // int b; + + // /* make sure the radix is in range */ + // if ((radix < 2) || (radix > 64)) { + // return MP_VAL; + // } + + // if (mp_iszero(a)) { + // *size = 2; + // return MP_OKAY; + // } + + // a_ = *a; + // a_.sign = MP_ZPOS; + // if ((err = mp_log_n(&a_, radix, &b)) != MP_OKAY) { + // return err; + // } + + // /* mp_ilogb truncates to zero, hence we need one extra put on top and one for `\0`. */ + // *size = (size_t)b + 2U + (mp_isneg(a) ? 1U : 0U); + + return size, .OK; +} \ No newline at end of file From 767948ab46411135730a8f868343e05d1f89f54c Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 17 Jul 2021 21:19:41 +0200 Subject: [PATCH 009/105] bigint: log_n for bases that fit within one DIGIT or are a power of two. --- core/math/bigint/example.odin | 2 +- core/math/bigint/log.odin | 95 ++++++++++++++++++++++++++++++++--- core/math/bigint/radix.odin | 49 +++++++++--------- 3 files changed, 114 insertions(+), 32 deletions(-) diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 2d3f5e12d..e21925f09 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -76,7 +76,7 @@ demo :: proc() { fmt.printf("c: %v, bits: %v\n", cs, count_bits(c)); delete(as); delete(bs); delete(cs); - fmt.println("log2:", log_n(a, 8)); + fmt.println("radix_size:", radix_size(a, 10)); } main :: proc() { diff --git a/core/math/bigint/log.odin b/core/math/bigint/log.odin index fd8116b78..347e60d99 100644 --- a/core/math/bigint/log.odin +++ b/core/math/bigint/log.odin @@ -9,28 +9,37 @@ package bigint The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. */ -log_n :: proc(a: ^Int, base: int) -> (log: int, err: Error) { +import "core:fmt" + +log_n_int :: proc(a: ^Int, base: int) -> (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(base) { return _log_power_of_two(a, base), .OK; } - // if (MP_HAS(S_MP_LOG_D) && (a->used == 1)) { - // *c = s_mp_log_d((mp_digit)base, a->dp[0]); - // return MP_OKAY; - // } + /* + 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); - // } + // 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. @@ -44,3 +53,73 @@ _log_power_of_two :: proc(a: ^Int, base: int) -> (log: int) { } 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/bigint/radix.odin b/core/math/bigint/radix.odin index 5bfb6b9a0..64118f70b 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -26,8 +26,7 @@ itoa :: proc(a: ^Int, radix: int, allocator := context.allocator) -> (res: strin /* Fast path for radixes that are a power of two. */ - if radix & 1 == 0 { - + if is_power_of_two(radix) { } @@ -51,30 +50,34 @@ itoa :: proc(a: ^Int, radix: int, allocator := context.allocator) -> (res: strin int_to_string :: itoa; +/* + We size for `string`, not `cstring`. +*/ +radix_size :: proc(a: ^Int, radix: int) -> (size: int, err: Error) { + t := a; -radix_size :: proc(a: ^Int, base: int) -> (size: int, err: Error) { - // mp_err err; - // mp_int a_; - // int b; + if radix < 2 || radix > 64 { + return -1, .Invalid_Input; + } - // /* make sure the radix is in range */ - // if ((radix < 2) || (radix > 64)) { - // return MP_VAL; - // } + if is_zero(a) { + return 1, .OK; + } - // if (mp_iszero(a)) { - // *size = 2; - // return MP_OKAY; - // } + t.sign = .Zero_or_Positive; + log: int; - // a_ = *a; - // a_.sign = MP_ZPOS; - // if ((err = mp_log_n(&a_, radix, &b)) != MP_OKAY) { - // return err; - // } + log, err = log_n(t, radix); + if err != .OK { + return log, err; + } - // /* mp_ilogb truncates to zero, hence we need one extra put on top and one for `\0`. */ - // *size = (size_t)b + 2U + (mp_isneg(a) ? 1U : 0U); - - return size, .OK; + /* + log truncates to zero, so we need to add one more, and one for `-` if negative. + */ + if is_neg(a) { + return log + 2, .OK; + } else { + return log + 1, .OK; + } } \ No newline at end of file From e3d8ac559de102a7600f54eacb8aba4b54b0e428 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 18 Jul 2021 00:58:19 +0200 Subject: [PATCH 010/105] bigint: Fast paths for radix code. --- core/math/bigint/example.odin | 22 +++-- core/math/bigint/log.odin | 6 +- core/math/bigint/radix.odin | 151 +++++++++++++++++++++++++++++++--- 3 files changed, 154 insertions(+), 25 deletions(-) diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index e21925f09..166088b4e 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -46,37 +46,35 @@ demo :: proc() { as, bs, cs: string; err: Error; - a, err = init(512); + a, err = init(4); defer destroy(a); - as, err = itoa(a, 10); + as, err = itoa(a); fmt.printf("a: %v, err: %v\n\n", as, err); delete(as); - b, err = init(42); + b, err = init(4); defer destroy(b); - bs, err = itoa(b, 10); - fmt.printf("b: %v, err: %v\n\n", bs, err); + bs, err = itoa(b); + fmt.printf("b: %s, err: %v\n\n", bs, err); delete(bs); c, err = init(); defer destroy(c); - cs, err = itoa(c, 10); - fmt.printf("c: %v\n", cs); + cs, err = itoa(c); + fmt.printf("b: %s, err: %v\n\n", cs, err); delete(cs); fmt.println("=== Add ==="); err = sub(c, a, b); fmt.printf("Error: %v\n", err); - as, err = itoa(a, 10); - bs, err = itoa(b, 10); - cs, err = itoa(c, 10); + as, err = itoa(a); + bs, err = itoa(b); + cs, err = itoa(c); 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("radix_size:", radix_size(a, 10)); } main :: proc() { diff --git a/core/math/bigint/log.odin b/core/math/bigint/log.odin index 347e60d99..980b5a7cb 100644 --- a/core/math/bigint/log.odin +++ b/core/math/bigint/log.odin @@ -11,7 +11,7 @@ package bigint import "core:fmt" -log_n_int :: proc(a: ^Int, base: int) -> (log: int, err: Error) { +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; @@ -20,7 +20,7 @@ log_n_int :: proc(a: ^Int, base: int) -> (log: int, err: Error) { /* Fast path for bases that are a power of two. */ - if is_power_of_two(base) { + if is_power_of_two(int(base)) { return _log_power_of_two(a, base), .OK; } @@ -44,7 +44,7 @@ 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: int) -> (log: int) { +_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int) { base := base; y: int; for y = 0; base & 1 == 0; { diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin index 64118f70b..5c2a8aef2 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -15,23 +15,60 @@ import "core:mem" import "core:intrinsics" import "core:fmt" import "core:strings" +import "core:slice" -itoa :: proc(a: ^Int, radix: int, allocator := context.allocator) -> (res: string, err: Error) { +/* + 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) { assert_initialized(a); + /* + Radix defaults to 10. + */ + radix := radix if radix > 0 else 10; - if radix < 2 || radix > 64 { - return strings.clone("", allocator), .Invalid_Input; + /* + TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer. + Then after the digits are written and the string is reversed + */ + + /* + Calculate the size of the buffer we need. + */ + size: int; + size, err = radix_size(a, radix); + if zero_terminate { + size += 1; } /* - Fast path for radixes that are a power of two. + Exit if calculating the size returned an error. */ - if is_power_of_two(radix) { - + if err != .OK { + if zero_terminate { + return string(cstring("")), err; + } + return "", err; } + /* + Allocate the buffer we need. + */ + buffer := make([]u8, size); + /* + Write the digits out into the buffer. + */ + written: int; + written, err = itoa_raw(a, radix, buffer, zero_terminate); + /* + For now, delete the buffer and fall back to the below on failure. + */ + if err == .OK { + return string(buffer[:written]), .OK; + } + delete(buffer); fallback :: proc(a: ^Int, print_raw := false) -> string { if print_raw { @@ -48,12 +85,91 @@ itoa :: proc(a: ^Int, radix: int, allocator := context.allocator) -> (res: strin return strings.clone(fallback(a), allocator), .Unimplemented; } -int_to_string :: itoa; +/* + 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) { + assert_initialized(a); + /* + Radix defaults to 10. + */ + radix := radix if radix > 0 else 10; + + s: string; + s, err = itoa_string(a, radix, true, allocator); + return cstring(raw_data(s)), err; +} + +/* + A low-level `itoa` using a caller-provided buffer. `itoa_string` and `itoa_cstring` use this. + You can use also use it if you want to pre-allocate a buffer and optionally reuse it. + + Use `radix_size` or `radix_size_estimate` to determine a buffer size big enough. + + `written` includes the sign if negative, and the zero terminator if asked for. +*/ +itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (written: int, err: Error) { + assert_initialized(a); + /* + Radix defaults to 10. + */ + radix := radix if radix > 0 else 10; + + /* + Early exit if we were given an empty buffer. + */ + available := len(buffer); + if available == 0 { + return 0, .Buffer_Overflow; + } + /* + Early exit if `Int` == 0 or the entire `Int` fits in a single radix digit. + */ + if is_zero(a) || (a.used == 1 && a.digit[0] < DIGIT(radix)) { + needed := 2 if is_neg(a) else 1; + needed += 1 if zero_terminate else 0; + if available < needed { + return 0, .Buffer_Overflow; + } + + if is_neg(a) { + buffer[written] = '-'; + written += 1; + } + + buffer[written] = RADIX_TABLE[a.digit[0]]; + written += 1; + + if zero_terminate { + buffer[written] = 0; + written += 1; + } + + return written, .OK; + } + + + /* + Fast path for radixes that are a power of two. + */ + if is_power_of_two(int(radix)) { + + + + return len(buffer), .OK; + } + + return -1, .Unimplemented; +} + +itoa :: proc{itoa_string, itoa_raw}; +int_to_string :: itoa; +int_to_cstring :: itoa_cstring; /* We size for `string`, not `cstring`. */ -radix_size :: proc(a: ^Int, radix: int) -> (size: int, err: Error) { +radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) { t := a; if radix < 2 || radix > 64 { @@ -67,7 +183,7 @@ radix_size :: proc(a: ^Int, radix: int) -> (size: int, err: Error) { t.sign = .Zero_or_Positive; log: int; - log, err = log_n(t, radix); + log, err = log_n(t, DIGIT(radix)); if err != .OK { return log, err; } @@ -80,4 +196,19 @@ radix_size :: proc(a: ^Int, radix: int) -> (size: int, err: Error) { } else { return log + 1, .OK; } -} \ No newline at end of file +} + +/* + Characters used in radix conversions. +*/ +RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +RADIX_TABLE_REVERSE := [80]u8{ + 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */ + 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */ + 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */ + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* IJKLMNOPQR */ + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, /* STUVWXYZ[\ */ + 0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */ + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */ + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */ +}; \ No newline at end of file From 341e8a3c99b0fb402ad53cb4937c72efbf68e54c Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 18 Jul 2021 02:17:51 +0200 Subject: [PATCH 011/105] bigint: itoa works for numbers <= 120 bits. --- core/math/bigint/example.odin | 6 +-- core/math/bigint/helpers.odin | 2 +- core/math/bigint/radix.odin | 71 +++++++++++++++++++++++++++-------- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 166088b4e..008b1eb08 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -46,13 +46,13 @@ demo :: proc() { as, bs, cs: string; err: Error; - a, err = init(4); + a, err = init(-512); defer destroy(a); as, err = itoa(a); fmt.printf("a: %v, err: %v\n\n", as, err); delete(as); - b, err = init(4); + b, err = init(42); defer destroy(b); bs, err = itoa(b); fmt.printf("b: %s, err: %v\n\n", bs, err); @@ -68,7 +68,7 @@ demo :: proc() { err = sub(c, a, b); fmt.printf("Error: %v\n", err); - as, err = itoa(a); + as, err = itoa(a, 8); bs, err = itoa(b); cs, err = itoa(c); fmt.printf("a: %v, bits: %v\n", as, count_bits(a)); diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin index 869bd3e26..3b6e108d7 100644 --- a/core/math/bigint/helpers.odin +++ b/core/math/bigint/helpers.odin @@ -88,6 +88,7 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) a.used = 0; a.sign = .Zero_or_Positive if n >= 0 else .Negative; n = abs(n); + for n != 0 { a.digit[a.used] = DIGIT(n) & _MASK; a.used += 1; @@ -96,7 +97,6 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) if minimize { shrink(a); } - _zero_unused(a); } diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin index 5c2a8aef2..8e6780904 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -114,6 +114,9 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> ( Radix defaults to 10. */ radix := radix if radix > 0 else 10; + if radix < 2 || radix > 64 { + return 0, .Invalid_Input; + } /* Early exit if we were given an empty buffer. @@ -123,7 +126,7 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> ( return 0, .Buffer_Overflow; } /* - Early exit if `Int` == 0 or the entire `Int` fits in a single radix digit. + Fast path for when `Int` == 0 or the entire `Int` fits in a single radix digit. */ if is_zero(a) || (a.used == 1 && a.digit[0] < DIGIT(radix)) { needed := 2 if is_neg(a) else 1; @@ -148,15 +151,51 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> ( return written, .OK; } + /* + Fast path for when `Int` fits within a `_WORD`. + */ + if a.used == 1 || a.used == 2 { + val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); + for val > 0 { + if available == 0 { + return written, .Buffer_Overflow; + } + + q := val / _WORD(radix); + buffer[written] = RADIX_TABLE[val - (q * _WORD(radix))]; + available -= 1; + written += 1; + + val = q; + } + if is_neg(a) { + if available == 0 { + return written, .Buffer_Overflow; + } + buffer[written] = '-'; + available -= 1; + written += 1; + } + /* + Reverse output. + */ + slice.reverse(buffer[:written]); + if zero_terminate { + if available == 0 { + return written, .Buffer_Overflow; + } + buffer[written] = 0; + written += 1; + } + return written, .OK; + } /* Fast path for radixes that are a power of two. */ if is_power_of_two(int(radix)) { - - - return len(buffer), .OK; + // return len(buffer), .OK; } return -1, .Unimplemented; @@ -170,8 +209,6 @@ int_to_cstring :: itoa_cstring; We size for `string`, not `cstring`. */ radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) { - t := a; - if radix < 2 || radix > 64 { return -1, .Invalid_Input; } @@ -180,22 +217,26 @@ radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) { return 1, .OK; } - t.sign = .Zero_or_Positive; - log: int; + /* + Calculate `log` on a temporary "copy" with its sign set to positive. + */ + t := &Int{ + used = a.used, + allocated = a.allocated, + sign = .Zero_or_Positive, + digit = a.digit, + }; - log, err = log_n(t, DIGIT(radix)); + size, err = log_n(t, DIGIT(radix)); if err != .OK { - return log, err; + return; } /* log truncates to zero, so we need to add one more, and one for `-` if negative. */ - if is_neg(a) { - return log + 2, .OK; - } else { - return log + 1, .OK; - } + size += 2 if is_neg(a) else 1; + return size, .OK; } /* From 04a83eb9f7b4f08995e37318f4f32b9ed2b62957 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 18 Jul 2021 13:47:10 +0200 Subject: [PATCH 012/105] bigint: pass `size` to `itoa_raw`. --- core/math/bigint/radix.odin | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin index 8e6780904..e35021b4b 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -36,10 +36,7 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator Calculate the size of the buffer we need. */ size: int; - size, err = radix_size(a, radix); - if zero_terminate { - size += 1; - } + size, err = radix_size(a, radix, zero_terminate); /* Exit if calculating the size returned an error. @@ -60,7 +57,7 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator Write the digits out into the buffer. */ written: int; - written, err = itoa_raw(a, radix, buffer, zero_terminate); + written, err = itoa_raw(a, radix, buffer, size, zero_terminate); /* For now, delete the buffer and fall back to the below on failure. @@ -107,9 +104,15 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) - Use `radix_size` or `radix_size_estimate` to determine a buffer size big enough. `written` includes the sign if negative, and the zero terminator if asked for. + If you want a 2s complement negative number, adjust it before calling. + + You can pass the output of `radix_size` to `size` if you've previously called it to size + the output buffer. If you haven't, this routine will call it. This way it knows if the buffer + is the appropriate size, and it can avoid writing backwards and then reversing. + */ -itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (written: int, err: Error) { - assert_initialized(a); +itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) { + assert_initialized(a); size := size; /* Radix defaults to 10. */ @@ -119,10 +122,17 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> ( } /* - Early exit if we were given an empty buffer. + We weren't given a size. Let's compute it. + */ + if size == -1 { + size, err = radix_size(a, radix, zero_terminate); + } + + /* + Early exit if the buffer we were given is too small. */ available := len(buffer); - if available == 0 { + if available < size { return 0, .Buffer_Overflow; } /* @@ -208,7 +218,7 @@ int_to_cstring :: itoa_cstring; /* We size for `string`, not `cstring`. */ -radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) { +radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, err: Error) { if radix < 2 || radix > 64 { return -1, .Invalid_Input; } @@ -236,6 +246,7 @@ radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) { log truncates to zero, so we need to add one more, and one for `-` if negative. */ size += 2 if is_neg(a) else 1; + size += 1 if zero_terminate else 0; return size, .OK; } From d7ae611f762c41447b417371c91c18a757079787 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 18 Jul 2021 15:42:19 +0200 Subject: [PATCH 013/105] bigint: `itoa` now writes backwards directly, no need to reverse after. --- core/math/bigint/example.odin | 10 ++++-- core/math/bigint/radix.odin | 57 ++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 008b1eb08..930c11b7f 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -58,12 +58,18 @@ demo :: proc() { fmt.printf("b: %s, err: %v\n\n", bs, err); delete(bs); - c, err = init(); + c, err = init(-4); defer destroy(c); cs, err = itoa(c); - fmt.printf("b: %s, err: %v\n\n", cs, err); + 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); diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin index e35021b4b..3d1068a37 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -103,13 +103,19 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) - Use `radix_size` or `radix_size_estimate` to determine a buffer size big enough. - `written` includes the sign if negative, and the zero terminator if asked for. - If you want a 2s complement negative number, adjust it before calling. - You can pass the output of `radix_size` to `size` if you've previously called it to size the output buffer. If you haven't, this routine will call it. This way it knows if the buffer - is the appropriate size, and it can avoid writing backwards and then reversing. + is the appropriate size, and we can write directly in place without a reverse step at the end. + === === === IMPORTANT === === === + + If you determined the buffer size using `radix_size_estimate`, or have a buffer + that you reuse that you know is large enough, don't pass this size unless you know what you are doing, + because we will always write backwards starting at last byte of the buffer. + + Keep in mind that if you set `size` yourself and it's smaller than the buffer, + it'll result in buffer overflows, as we use it to avoid reversing at the end + 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) { assert_initialized(a); size := size; @@ -145,17 +151,20 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina return 0, .Buffer_Overflow; } - if is_neg(a) { - buffer[written] = '-'; - written += 1; + if zero_terminate { + available -= 1; + buffer[available] = 0; + written += 1; } - buffer[written] = RADIX_TABLE[a.digit[0]]; - written += 1; + available -= 1; + buffer[available] = RADIX_TABLE[a.digit[0]]; + written += 1; - if zero_terminate { - buffer[written] = 0; - written += 1; + if is_neg(a) { + available -= 1; + buffer[available] = '-'; + written += 1; } return written, .OK; @@ -165,6 +174,15 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina Fast path for when `Int` fits within a `_WORD`. */ if a.used == 1 || a.used == 2 { + if zero_terminate { + if available == 0 { + return written, .Buffer_Overflow; + } + available -= 1; + buffer[available] = 0; + written += 1; + } + val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); for val > 0 { if available == 0 { @@ -172,8 +190,8 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina } q := val / _WORD(radix); - buffer[written] = RADIX_TABLE[val - (q * _WORD(radix))]; available -= 1; + buffer[available] = RADIX_TABLE[val - (q * _WORD(radix))]; written += 1; val = q; @@ -182,19 +200,8 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina if available == 0 { return written, .Buffer_Overflow; } - buffer[written] = '-'; available -= 1; - written += 1; - } - /* - Reverse output. - */ - slice.reverse(buffer[:written]); - if zero_terminate { - if available == 0 { - return written, .Buffer_Overflow; - } - buffer[written] = 0; + buffer[available] = '-'; written += 1; } return written, .OK; From e600e5947b6a59b1d813892de7273ba6a6cafcb9 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 18 Jul 2021 15:56:20 +0200 Subject: [PATCH 014/105] bigint: remove unnecessary boundary checks. --- core/math/bigint/radix.odin | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin index 3d1068a37..f939aa7da 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -145,29 +145,20 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina Fast path for when `Int` == 0 or the entire `Int` fits in a single radix digit. */ if is_zero(a) || (a.used == 1 && a.digit[0] < DIGIT(radix)) { - needed := 2 if is_neg(a) else 1; - needed += 1 if zero_terminate else 0; - if available < needed { - return 0, .Buffer_Overflow; - } - if zero_terminate { available -= 1; buffer[available] = 0; - written += 1; } available -= 1; buffer[available] = RADIX_TABLE[a.digit[0]]; - written += 1; if is_neg(a) { available -= 1; buffer[available] = '-'; - written += 1; } - return written, .OK; + return len(buffer) - available, .OK; } /* @@ -175,36 +166,23 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina */ if a.used == 1 || a.used == 2 { if zero_terminate { - if available == 0 { - return written, .Buffer_Overflow; - } available -= 1; buffer[available] = 0; - written += 1; } val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); for val > 0 { - if available == 0 { - return written, .Buffer_Overflow; - } - q := val / _WORD(radix); available -= 1; buffer[available] = RADIX_TABLE[val - (q * _WORD(radix))]; - written += 1; val = q; } if is_neg(a) { - if available == 0 { - return written, .Buffer_Overflow; - } available -= 1; buffer[available] = '-'; - written += 1; } - return written, .OK; + return len(buffer) - available, .OK; } /* From 5af85aed3dcbcead9e8a436e0795b77fcda93a7f Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 18 Jul 2021 20:54:40 +0200 Subject: [PATCH 015/105] bigint: `itoa` support for arbitrary precision if `is_power_of_two(radix)` --- core/math/bigint/bigint.odin | 1 + core/math/bigint/example.odin | 24 +++++++++++---------- core/math/bigint/helpers.odin | 33 +++++++++++++++++++++++++++++ core/math/bigint/radix.odin | 40 ++++++++++++++++++++++++++++++----- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/core/math/bigint/bigint.odin b/core/math/bigint/bigint.odin index 8f6cb1cf7..50934e429 100644 --- a/core/math/bigint/bigint.odin +++ b/core/math/bigint/bigint.odin @@ -111,6 +111,7 @@ _DIGIT_TYPE_BITS :: 8 * size_of(DIGIT); _WORD_TYPE_BITS :: 8 * size_of(_WORD); _DIGIT_BITS :: _DIGIT_TYPE_BITS - 4; +_WORD_BITS :: 2 * _DIGIT_BITS; _MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1); _DIGIT_MAX :: _MASK; diff --git a/core/math/bigint/example.odin b/core/math/bigint/example.odin index 930c11b7f..38290369d 100644 --- a/core/math/bigint/example.odin +++ b/core/math/bigint/example.odin @@ -46,37 +46,39 @@ demo :: proc() { as, bs, cs: string; err: Error; - a, err = init(-512); + a, err = init(); + a.digit[2] = 512; + a.used = 3; defer destroy(a); - as, err = itoa(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); + bs, err = itoa(b, 16); fmt.printf("b: %s, err: %v\n\n", bs, err); delete(bs); c, err = init(-4); defer destroy(c); - cs, err = itoa(c); + cs, err = itoa(c, 16); fmt.printf("c: %s, err: %v\n\n", cs, err); delete(cs); - cstr: cstring; - defer delete(cstr); + // cstr: cstring; + // defer delete(cstr); - cstr, err = itoa_cstring(a); - fmt.printf("cstring: %v, err: %v\n\n", cstr, err); + // 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, 8); - bs, err = itoa(b); - cs, err = itoa(c); + 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)); diff --git a/core/math/bigint/helpers.odin b/core/math/bigint/helpers.odin index 3b6e108d7..a96eabb2c 100644 --- a/core/math/bigint/helpers.odin +++ b/core/math/bigint/helpers.odin @@ -102,6 +102,39 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) set :: proc{set_integer}; +/* + Helpers to extract values from the `Int`. +*/ +extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { + limb := bit_offset / _DIGIT_BITS; + if limb < 0 || limb >= a.used { + return 0, .Invalid_Input; + } + + i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); + + return 1 if ((a.digit[limb] & i) != 0) else 0, .OK; +} + +extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { + if count > _WORD_BITS || count < 1 { + return 0, .Invalid_Input; + } + + v: DIGIT; + e: Error; + for shift := 0; shift < count; shift += 1 { + o := offset + shift; + v, e = extract_bit(a, o); + if e != .OK { + break; + } + res = res + _WORD(v) << uint(shift); + } + + return res, e; +} + /* Resize backing store. */ diff --git a/core/math/bigint/radix.odin b/core/math/bigint/radix.odin index f939aa7da..7cacdbc56 100644 --- a/core/math/bigint/radix.odin +++ b/core/math/bigint/radix.odin @@ -37,15 +37,16 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator */ size: int; size, err = radix_size(a, radix, zero_terminate); - /* Exit if calculating the size returned an error. */ if err != .OK { + f := strings.clone(fallback(a), allocator); if zero_terminate { - return string(cstring("")), err; + c := strings.clone_to_cstring(f); + return string(c), err; } - return "", err; + return f, err; } /* @@ -149,7 +150,6 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina available -= 1; buffer[available] = 0; } - available -= 1; buffer[available] = RADIX_TABLE[a.digit[0]]; @@ -184,13 +184,40 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina } return len(buffer) - available, .OK; } + /* + At least 3 DIGITs are in use if we made it this far. + */ /* Fast path for radixes that are a power of two. */ if is_power_of_two(int(radix)) { + if zero_terminate { + available -= 1; + buffer[available] = 0; + } - // return len(buffer), .OK; + mask := _WORD(radix - 1); + shift := int(log_n(DIGIT(radix), 2)); + count := int(count_bits(a)); + digit: _WORD; + + for offset := 0; offset < count; offset += 4 { + bits_to_get := int(min(count - offset, shift)); + digit, err := extract_bits(a, offset, bits_to_get); + if err != .OK { + return len(buffer) - available, .Invalid_Input; + } + available -= 1; + buffer[available] = RADIX_TABLE[digit]; + } + + if is_neg(a) { + available -= 1; + buffer[available] = '-'; + } + + return len(buffer) - available, .OK; } return -1, .Unimplemented; @@ -209,6 +236,9 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e } if is_zero(a) { + if zero_terminate { + return 2, .OK; + } return 1, .OK; } From cccd29083440ae7f93b58d085e70f368293d92bc Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 19 Jul 2021 14:16:44 +0200 Subject: [PATCH 016/105] bigint: Add `is_power_of_two` helper. --- core/math/bigint/compare.odin | 38 +++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/core/math/bigint/compare.odin b/core/math/bigint/compare.odin index 9d48c4706..97cb64197 100644 --- a/core/math/bigint/compare.odin +++ b/core/math/bigint/compare.odin @@ -48,10 +48,44 @@ is_odd :: proc(a: ^Int) -> bool { return false; } -is_power_of_two :: proc(x: int) -> bool { - return ((x) != 0) && (((x) & ((x) - 1)) == 0); +is_power_of_two_small :: proc(a: int) -> bool { + return ((a) != 0) && (((a) & ((a) - 1)) == 0); } +is_power_of_two_large :: proc(a: ^Int) -> (res: bool) { + /* + Early out for Int == 0. + */ + if a.used == 0 { + return false; + } + + /* + For an `Int` to be a power of two, its top limb has to be a power of two. + */ + if !is_power_of_two_small(int(a.digit[a.used - 1])) { + return false; + } + + /* + That was the only limb, so it's a power of two. + */ + if a.used == 1 { + return true; + } + + /* + For an Int to be a power of two, all limbs except the top one have to be zero. + */ + for i := 1; i < a.used; i += 1 { + if a.digit[i - 1] != 0 { + return false; + } + } + return true; +} +is_power_of_two :: proc{is_power_of_two_small, is_power_of_two_large}; + /* Compare two `Int`s, signed. */ From baef0c291d85fb9d87fa6d1db7959b34c366e361 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 19 Jul 2021 23:00:08 +0200 Subject: [PATCH 017/105] bigint: Added some more helpers. --- core/math/bigint/bigint.odin | 3 +- core/math/bigint/example.odin | 74 ++++++++++-------- core/math/bigint/helpers.odin | 139 ++++++++++++++++++++++++++++++++++ core/math/bigint/logical.odin | 113 +++++++++++++++++++++++++++ 4 files changed, 296 insertions(+), 33 deletions(-) create mode 100644 core/math/bigint/logical.odin 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); +} From 9dba17cf87a208f41f2d53bc9a2be14ed70a396a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 20 Jul 2021 17:15:06 +0200 Subject: [PATCH 018/105] bigint: refactor to `big.Int` instead of `bigint.Int`. --- core/math/{bigint => big}/basic.odin | 907 ++++++++++++------------- core/math/{bigint => big}/bigint.odin | 12 +- core/math/big/build.bat | 2 + core/math/{bigint => big}/compare.odin | 2 +- core/math/{bigint => big}/example.odin | 12 +- core/math/{bigint => big}/helpers.odin | 29 +- core/math/{bigint => big}/log.odin | 246 ++++--- core/math/big/logical.odin | 206 ++++++ core/math/{bigint => big}/radix.odin | 17 +- core/math/bigint/build.bat | 3 - core/math/bigint/logical.odin | 113 --- 11 files changed, 834 insertions(+), 715 deletions(-) rename core/math/{bigint => big}/basic.odin (94%) rename core/math/{bigint => big}/bigint.odin (91%) create mode 100644 core/math/big/build.bat rename core/math/{bigint => big}/compare.odin (99%) rename core/math/{bigint => big}/example.odin (93%) rename core/math/{bigint => big}/helpers.odin (92%) rename core/math/{bigint => big}/log.odin (94%) create mode 100644 core/math/big/logical.odin rename core/math/{bigint => big}/radix.odin (97%) delete mode 100644 core/math/bigint/build.bat delete mode 100644 core/math/bigint/logical.odin 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); -} From 4eadd0867d6529340ad1da3ab2af56d62b40f3d2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 20 Jul 2021 19:51:39 +0200 Subject: [PATCH 019/105] big: Continuing to refactor. --- core/math/big/api.odin | 155 +++++++++++++++++++++++++++++++++++++ core/math/big/basic.odin | 26 +++---- core/math/big/bigint.odin | 2 +- core/math/big/compare.odin | 75 +++++++++--------- core/math/big/helpers.odin | 64 +++++++-------- core/math/big/log.odin | 2 +- core/math/big/logical.odin | 2 +- core/math/big/radix.odin | 2 +- 8 files changed, 234 insertions(+), 94 deletions(-) create mode 100644 core/math/big/api.odin diff --git a/core/math/big/api.odin b/core/math/big/api.odin new file mode 100644 index 000000000..d2cbd8565 --- /dev/null +++ b/core/math/big/api.odin @@ -0,0 +1,155 @@ +package big + +/* + Copyright 2021 Jeroen van Rijn . + Made available under Odin's BSD-2 license. + + An arbitrary precision mathematics 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 collects public procs into proc maps, as well as any of their aliases. +/* + +*/ + === === === === === === === === === === === === === === === === === === === === === === === === + Basic arithmetic. + See `basic.odin`. + === === === === === === === === === === === === === === === === === === === === === === === === +*/ + +/* + err = add(dest, a, b); +*/ +add :: proc { + /* + int_add :: proc(dest, a, b: ^Int) -> (err: Error) + */ + int_add, + /* + int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) + */ + int_add_digit, +}; + +/* + err = sub(dest, a, b); +*/ +sub :: proc { + /* + int_sub :: proc(dest, a, b: ^Int) -> (err: Error) + */ + int_sub, + /* + int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) + */ + int_sub_digit, +}; + +/* + === === === === === === === === === === === === === === === === === === === === === === === === + Comparisons. + See `compare.odin`. + === === === === === === === === === === === === === === === === === === === === === === === === +*/ + +is_initialized :: proc { + /* + int_is_initialized :: proc(a: ^Int) -> bool + */ + int_is_initialized, +}; + +is_zero :: proc { + /* + int_is_zero :: proc(a: ^Int) -> bool + */ + int_is_zero, +}; + +is_positive :: proc { + /* + int_is_positive :: proc(a: ^Int) -> bool + */ + int_is_positive, +}; +is_pos :: is_positive; + +is_negative :: proc { + /* + int_is_negative :: proc(a: ^Int) -> bool + */ + int_is_negative, +}; +is_neg :: is_negative; + +is_even :: proc { + /* + int_is_even :: proc(a: ^Int) -> bool + */ + int_is_even, +}; + +is_odd :: proc { + /* + int_is_odd :: proc(a: ^Int) -> bool + */ + int_is_odd, +}; + +is_power_of_two :: proc { + /* + platform_int_is_power_of_two :: proc(a: int) -> bool + */ + platform_int_is_power_of_two, + /* + int_is_power_of_two :: proc(a: ^Int) -> (res: bool) + */ + int_is_power_of_two, +}; + +compare :: proc { + /* + Compare two `Int`s, signed. + + int_compare :: proc(a, b: ^Int) -> Comparison_Flag + */ + int_compare, + /* + Compare an `Int` to an unsigned number upto the size of the backing type. + + int_compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag + */ + int_compare_digit, +}; +cmp :: compare; + +compare_magnitude :: proc { + /* + Compare the magnitude of two `Int`s, unsigned. + */ + int_compare_magnitude, +}; +cmp_mag :: compare_magnitude; + +/* + === === === === === === === === === === === === === === === === === === === === === === === === + Initialization and other helpers. + See `helpers.odin`. + === === === === === === === === === === === === === === === === === === === === === === === === +*/ + +destroy :: proc { + /* + Deallocates the backing memory of an `Int` and resets it. + + int_destroy :: proc(a: ^Int, allocator_zeroes := false, allocator := context.allocator) + */ + int_destroy, +}; + +init :: proc{ + int_init_sized, + int_init_from_integer, + int_init_copy, +}; diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 98f14687c..43f5a3715 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -4,7 +4,7 @@ package big Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-2 license. - A BigInt implementation in Odin. + An arbitrary precision mathematics 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. @@ -23,7 +23,7 @@ import "core:intrinsics" /* High-level addition. Handles sign. */ -add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { +int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; assert_initialized(dest); assert_initialized(a); assert_initialized(b); @@ -32,7 +32,7 @@ add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { */ if x.sign == y.sign { dest.sign = x.sign; - return _add(dest, x, y); + return _int_add(dest, x, y); } /* @@ -45,7 +45,7 @@ add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { } dest.sign = x.sign; - return _sub(dest, x, y); + return _int_sub(dest, x, y); } /* @@ -54,7 +54,7 @@ add_two_ints :: proc(dest, a, b: ^Int) -> (err: Error) { dest = a + digit; */ -add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { +int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; assert_initialized(dest); assert_initialized(a); @@ -165,12 +165,10 @@ add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { 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) { +int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); @@ -180,7 +178,7 @@ sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { In either case, ADD their magnitudes and use the sign of the first number. */ dest.sign = x.sign; - return _add(dest, x, y); + return _int_add(dest, x, y); } /* @@ -201,7 +199,7 @@ sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { */ dest.sign = x.sign; } - return _sub(dest, x, y); + return _int_sub(dest, x, y); } /* @@ -210,7 +208,7 @@ sub_two_ints :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest = a - digit; */ -sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { +int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; assert_initialized(dest); assert_initialized(a); @@ -296,8 +294,6 @@ sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { return .OK; } -sub :: proc{sub_two_ints, sub_digit}; - /* ========================== Low-level routines @@ -308,7 +304,7 @@ sub :: proc{sub_two_ints, sub_digit}; Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. */ -_add :: proc(dest, a, b: ^Int) -> (err: Error) { +_int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; assert_initialized(a); assert_initialized(b); assert_initialized(dest); @@ -389,7 +385,7 @@ _add :: proc(dest, a, b: ^Int) -> (err: Error) { Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|. Handbook of Applied Cryptography, algorithm 14.9. */ -_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { +_int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); diff --git a/core/math/big/bigint.odin b/core/math/big/bigint.odin index 82ddaeb79..0e5b11381 100644 --- a/core/math/big/bigint.odin +++ b/core/math/big/bigint.odin @@ -4,7 +4,7 @@ package big Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-2 license. - A BigInt implementation in Odin. + An arbitrary precision mathematics 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. */ diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index facfca2e8..b9c6dace2 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -4,32 +4,32 @@ package big Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-2 license. - A BigInt implementation in Odin. + An arbitrary precision mathematics 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 various comparison routines. */ import "core:intrinsics" -is_initialized :: proc(a: ^Int) -> bool { +int_is_initialized :: proc(a: ^Int) -> bool { return a != rawptr(uintptr(0)); } -is_zero :: proc(a: ^Int) -> bool { +int_is_zero :: proc(a: ^Int) -> bool { return is_initialized(a) && a.used == 0; } -is_positive :: proc(a: ^Int) -> bool { +int_is_positive :: proc(a: ^Int) -> bool { return is_initialized(a) && a.sign == .Zero_or_Positive; } -is_pos :: is_positive;; -is_negative :: proc(a: ^Int) -> bool { +int_is_negative :: proc(a: ^Int) -> bool { return is_initialized(a) && a.sign == .Negative; } -is_neg :: is_negative; -is_even :: proc(a: ^Int) -> bool { +int_is_even :: proc(a: ^Int) -> bool { if is_initialized(a) { if is_zero(a) { return true; @@ -41,18 +41,18 @@ is_even :: proc(a: ^Int) -> bool { return false; } -is_odd :: proc(a: ^Int) -> bool { +int_is_odd :: proc(a: ^Int) -> bool { if is_initialized(a) { return !is_even(a); } return false; } -is_power_of_two_small :: proc(a: int) -> bool { +platform_int_is_power_of_two :: proc(a: int) -> bool { return ((a) != 0) && (((a) & ((a) - 1)) == 0); } -is_power_of_two_large :: proc(a: ^Int) -> (res: bool) { +int_is_power_of_two :: proc(a: ^Int) -> (res: bool) { /* Early out for Int == 0. */ @@ -63,7 +63,7 @@ is_power_of_two_large :: proc(a: ^Int) -> (res: bool) { /* For an `Int` to be a power of two, its top limb has to be a power of two. */ - if !is_power_of_two_small(int(a.digit[a.used - 1])) { + if !platform_int_is_power_of_two(int(a.digit[a.used - 1])) { return false; } @@ -84,12 +84,11 @@ is_power_of_two_large :: proc(a: ^Int) -> (res: bool) { } return true; } -is_power_of_two :: proc{is_power_of_two_small, is_power_of_two_large}; /* Compare two `Int`s, signed. */ -compare :: proc(a, b: ^Int) -> Comparison_Flag { +int_compare :: proc(a, b: ^Int) -> Comparison_Flag { if !is_initialized(a) { return .Uninitialized; } if !is_initialized(b) { return .Uninitialized; } @@ -105,35 +104,11 @@ compare :: proc(a, b: ^Int) -> Comparison_Flag { } return cmp_mag(x, y); } -cmp :: compare; - -/* - Compare the magnitude of two `Int`s, unsigned. -*/ -compare_magnitude :: proc(a, b: ^Int) -> Comparison_Flag { - if !is_initialized(a) { return .Uninitialized; } - if !is_initialized(b) { return .Uninitialized; } - - /* Compare based on used digits */ - if a.used != b.used { - return .Greater_Than if a.used > b.used else .Less_Than; - } - - /* Same number of used digits, compare based on their value */ - for n := a.used - 1; n >= 0; n -= 1 { - if a.digit[n] != b.digit[n] { - return .Greater_Than if a.digit[n] > b.digit[n] else .Less_Than; - } - } - - return .Equal; -} -cmp_mag :: compare_magnitude; /* Compare an `Int` to an unsigned number upto the size of the backing type. */ -compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag { +int_compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag { if !is_initialized(a) { return .Uninitialized; } /* Compare based on sign */ @@ -152,4 +127,26 @@ compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag { } return .Equal; +} + +/* + Compare the magnitude of two `Int`s, unsigned. +*/ +int_compare_magnitude :: proc(a, b: ^Int) -> Comparison_Flag { + if !is_initialized(a) { return .Uninitialized; } + if !is_initialized(b) { return .Uninitialized; } + + /* Compare based on used digits */ + if a.used != b.used { + return .Greater_Than if a.used > b.used else .Less_Than; + } + + /* Same number of used digits, compare based on their value */ + for n := a.used - 1; n >= 0; n -= 1 { + if a.digit[n] != b.digit[n] { + return .Greater_Than if a.digit[n] > b.digit[n] else .Less_Than; + } + } + + return .Equal; } \ No newline at end of file diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 9d21295df..933b330a5 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -4,7 +4,7 @@ package big Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-2 license. - A BigInt implementation in Odin. + An arbitrary precision mathematics 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. */ @@ -13,13 +13,10 @@ import "core:mem" import "core:intrinsics" /* - Deallocates the backing memory of an Int. + Deallocates the backing memory of an `Int`. */ -destroy :: proc(a: ^Int, allocator_zeroes := false, free_int := true, loc := #caller_location) { - if !is_initialized(a) { - // Nothing to do. - return; - } +int_destroy :: proc(a: ^Int, allocator_zeroes := false, allocator := context.allocator) { + if !is_initialized(a) { return; } if !allocator_zeroes { mem.zero_slice(a.digit[:]); @@ -27,15 +24,15 @@ destroy :: proc(a: ^Int, allocator_zeroes := false, free_int := true, loc := #ca free(&a.digit[0]); a.used = 0; a.allocated = 0; - if free_int { - free(a); - } + free(a); } /* Creates and returns a new `Int`. + Size is the last parameter so it doesn't conflict with `int_init_from_integer`, + and we can say `init(512)` to init & set to 512. */ -init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (a: ^Int, err: Error) { +int_init_sized :: proc(allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (a: ^Int, err: Error) { /* Allocating a new variable. */ @@ -46,10 +43,10 @@ init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size a.used = 0; a.sign = .Zero_or_Positive; - if len(a.digit) != size { + if len(a.digit) != int(size) { return a, .Out_of_Memory; } - a.allocated = size; + a.allocated = int(size); if !allocator_zeroes { _zero_unused(a); @@ -58,17 +55,17 @@ init_new :: proc(allocator_zeroes := true, allocator := context.allocator, size } /* - Initialize from a signed or unsigned integer. + Initialize from a signed or unsigned platform integer. Inits a new `Int` and then calls the appropriate `set` routine. */ -init_from_integer :: proc(src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) where intrinsics.type_is_integer(T) { +int_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 { n = _MIN_DIGIT_COUNT; } - a, err = init_new(allocator_zeroes, allocator, n); + a, err = init(allocator_zeroes, allocator, n); if err == .OK { set(a, src, minimize); } @@ -78,43 +75,41 @@ init_from_integer :: proc(src: $T, minimize := false, allocator_zeroes := true, /* 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) { +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); + a, err = init(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. */ -set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location) where intrinsics.type_is_integer(T) { - n := n; - assert_initialized(a, loc); +int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, loc := #caller_location) where intrinsics.type_is_integer(T) { + src := src; + assert_initialized(dest, loc); - a.used = 0; - a.sign = .Zero_or_Positive if n >= 0 else .Negative; - n = abs(n); + dest.used = 0; + dest.sign = .Zero_or_Positive if src >= 0 else .Negative; + src = abs(src); - for n != 0 { - a.digit[a.used] = DIGIT(n) & _MASK; - a.used += 1; - n >>= _DIGIT_BITS; + for src != 0 { + dest.digit[dest.used] = DIGIT(src) & _MASK; + dest.used += 1; + src >>= _DIGIT_BITS; } if minimize { - shrink(a); + shrink(dest); } - _zero_unused(a); + _zero_unused(dest); } -set :: proc{set_integer}; +set :: proc{int_set_from_integer}; /* Copy one `Int` to another. @@ -375,9 +370,6 @@ minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) { power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { assert_initialized(a); - /* - - */ if power < 0 || power > _MAX_BIT_COUNT { return .Invalid_Input; } diff --git a/core/math/big/log.odin b/core/math/big/log.odin index d4b794344..1ce2437bf 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -4,7 +4,7 @@ package big Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-2 license. - A BigInt implementation in Odin. + An arbitrary precision mathematics 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. */ diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 9ecb4fba3..6c857a2f2 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -4,7 +4,7 @@ package big Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-2 license. - A BigInt implementation in Odin. + An arbitrary precision mathematics 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. diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index be1be7d10..86080d041 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -4,7 +4,7 @@ package big Copyright 2021 Jeroen van Rijn . Made available under Odin's BSD-2 license. - A BigInt implementation in Odin. + An arbitrary precision mathematics 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. From 687c211a58bd915b06b0d5d159a4fa9fa0a83617 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 20 Jul 2021 20:18:36 +0200 Subject: [PATCH 020/105] big: ZII. --- core/math/big/compare.odin | 2 +- core/math/big/example.odin | 10 ++++----- core/math/big/helpers.odin | 43 ++++++++++++++++++-------------------- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index b9c6dace2..3355f5752 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -14,7 +14,7 @@ package big import "core:intrinsics" int_is_initialized :: proc(a: ^Int) -> bool { - return a != rawptr(uintptr(0)); + return a != rawptr(uintptr(0)) && a.allocated >= _MIN_DIGIT_COUNT; } int_is_zero :: proc(a: ^Int) -> bool { diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 44ed91f4f..4ca72baf1 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -53,18 +53,16 @@ print :: proc(name: string, a: ^Int, base := i8(16)) { } demo :: proc() { - a, b, c: ^Int; + a, b, c := &Int{}, &Int{}, &Int{}; err: Error; defer destroy(a); defer destroy(b); defer destroy(c); - a, err = init(512); - - b, err = init(a); - - c, err = init(-4); + err = set(a, 512); + err = init(b, a); + err = init(c, -4); print("a", a, 2); print("b", b, 2); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 933b330a5..037343413 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -11,7 +11,7 @@ package big import "core:mem" import "core:intrinsics" - +//import "core:fmt" /* Deallocates the backing memory of an `Int`. */ @@ -24,7 +24,6 @@ int_destroy :: proc(a: ^Int, allocator_zeroes := false, allocator := context.all free(&a.digit[0]); a.used = 0; a.allocated = 0; - free(a); } /* @@ -32,57 +31,52 @@ int_destroy :: proc(a: ^Int, allocator_zeroes := false, allocator := context.all Size is the last parameter so it doesn't conflict with `int_init_from_integer`, and we can say `init(512)` to init & set to 512. */ -int_init_sized :: proc(allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (a: ^Int, err: Error) { +int_init_sized :: proc(a: ^Int, allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (err: Error) { /* Allocating a new variable. */ - a = new(Int, allocator); a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, size, size, allocator); a.allocated = 0; a.used = 0; a.sign = .Zero_or_Positive; - if len(a.digit) != int(size) { - return a, .Out_of_Memory; + if len(a.digit) != size { + return .Out_of_Memory; } - a.allocated = int(size); + a.allocated = size; if !allocator_zeroes { _zero_unused(a); } - return a, .OK; + return .OK; } /* Initialize from a signed or unsigned platform integer. Inits a new `Int` and then calls the appropriate `set` routine. */ -int_init_from_integer :: proc(src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) where intrinsics.type_is_integer(T) { +int_init_from_integer :: proc(a: ^Int, src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (err: Error) + where intrinsics.type_is_integer(T) { n := _DEFAULT_DIGIT_COUNT; if minimize { n = _MIN_DIGIT_COUNT; } - a, err = init(allocator_zeroes, allocator, n); - if err == .OK { - set(a, src, minimize); - } - return; + return set(a, src, minimize); } /* Initialize an `Int` as a copy from another `Int`. */ -int_init_copy :: proc(src: ^Int, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (a: ^Int, err: Error) { +int_init_copy :: proc(dest, src: ^Int, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (err: Error) { if !is_initialized(src) { - return nil, .Invalid_Input; + return .Invalid_Input; } - a, err = init(allocator_zeroes, allocator, src.used); if err == .OK { - copy(a, src); + err = copy(dest, src); } return; } @@ -90,9 +84,12 @@ int_init_copy :: proc(src: ^Int, minimize := false, allocator_zeroes := true, al /* Helpers to set an `Int` to a specific value. */ -int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, loc := #caller_location) where intrinsics.type_is_integer(T) { +int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (err: Error) + where intrinsics.type_is_integer(T) { src := src; - assert_initialized(dest, loc); + if !is_initialized(dest) { + grow(dest, _DEFAULT_DIGIT_COUNT); + } dest.used = 0; dest.sign = .Zero_or_Positive if src >= 0 else .Negative; @@ -107,6 +104,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, loc := #cal shrink(dest); } _zero_unused(dest); + return .OK; } set :: proc{int_set_from_integer}; @@ -123,9 +121,9 @@ copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { } /* - Check they're both initialized. + Check `src` is initialized. */ - if !(is_initialized(dest) && is_initialized(src)) { + if !is_initialized(src) { return .Invalid_Input; } @@ -270,7 +268,6 @@ shrink :: proc(a: ^Int) -> (err: Error) { } grow :: proc(a: ^Int, n: int, allow_shrink := false) -> (err: Error) { - assert_initialized(a); /* By default, calling `grow` with `n` <= a.allocated won't resize. With `allow_shrink` set to `true`, will call resize and shrink the `Int` as a result. From 2e372b33a34ec4ad516112a0d174257b392bdc60 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 20 Jul 2021 22:59:26 +0200 Subject: [PATCH 021/105] big: More ZII refactoring. --- core/math/big/api.odin | 10 +- core/math/big/bigint.odin | 3 +- core/math/big/compare.odin | 7 +- core/math/big/example.odin | 11 +- core/math/big/helpers.odin | 299 +++++++++++++++++-------------------- core/math/big/radix.odin | 3 +- 6 files changed, 151 insertions(+), 182 deletions(-) diff --git a/core/math/big/api.odin b/core/math/big/api.odin index d2cbd8565..d2eb21fc5 100644 --- a/core/math/big/api.odin +++ b/core/math/big/api.odin @@ -141,15 +141,11 @@ cmp_mag :: compare_magnitude; destroy :: proc { /* - Deallocates the backing memory of an `Int` and resets it. + Clears one or more `Int`s and dellocates their backing memory. - int_destroy :: proc(a: ^Int, allocator_zeroes := false, allocator := context.allocator) + int_destroy :: proc(integers: ..^Int) */ int_destroy, }; -init :: proc{ - int_init_sized, - int_init_from_integer, - int_init_copy, -}; + diff --git a/core/math/big/bigint.odin b/core/math/big/bigint.odin index 0e5b11381..f2c9366ad 100644 --- a/core/math/big/bigint.odin +++ b/core/math/big/bigint.odin @@ -49,7 +49,6 @@ Sign :: enum u8 { Int :: struct { used: int, - allocated: int, digit: [dynamic]DIGIT, sign: Sign, }; @@ -71,6 +70,8 @@ Error :: enum i8 { Max_Iterations_Reached = -4, Buffer_Overflow = -5, Integer_Overflow = -6, + Nil_Pointer_Passed = -7, + Int_Not_Initialized = -8, Unimplemented = -127, }; diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index 3355f5752..142a3bfe3 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -12,9 +12,14 @@ package big */ import "core:intrinsics" +import "core:mem" int_is_initialized :: proc(a: ^Int) -> bool { - return a != rawptr(uintptr(0)) && a.allocated >= _MIN_DIGIT_COUNT; + if a == nil { + return false; + } + raw := transmute(mem.Raw_Dynamic_Array)a.digit; + return raw.cap >= _MIN_DIGIT_COUNT; } int_is_zero :: proc(a: ^Int) -> bool { diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 4ca72baf1..9cb99b3e7 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -53,16 +53,13 @@ print :: proc(name: string, a: ^Int, base := i8(16)) { } demo :: proc() { - a, b, c := &Int{}, &Int{}, &Int{}; err: Error; - - defer destroy(a); - defer destroy(b); - defer destroy(c); + a, b, c := &Int{}, &Int{}, &Int{}; + defer destroy(a, b, c); err = set(a, 512); - err = init(b, a); - err = init(c, -4); + err = set(b, a); + err = set(c, -4); print("a", a, 2); print("b", b, 2); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 037343413..a590967d8 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -11,86 +11,34 @@ package big import "core:mem" import "core:intrinsics" -//import "core:fmt" /* - Deallocates the backing memory of an `Int`. + Deallocates the backing memory of one or more `Int`s. */ -int_destroy :: proc(a: ^Int, allocator_zeroes := false, allocator := context.allocator) { - if !is_initialized(a) { return; } +int_destroy :: proc(integers: ..^Int) { + integers := integers; - if !allocator_zeroes { + for a in &integers { mem.zero_slice(a.digit[:]); + free(&a.digit[0]); + a = &Int{}; } - free(&a.digit[0]); - a.used = 0; - a.allocated = 0; -} - -/* - Creates and returns a new `Int`. - Size is the last parameter so it doesn't conflict with `int_init_from_integer`, - and we can say `init(512)` to init & set to 512. -*/ -int_init_sized :: proc(a: ^Int, allocator_zeroes := true, allocator := context.allocator, size := _DEFAULT_DIGIT_COUNT) -> (err: Error) { - /* - Allocating a new variable. - */ - - a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, size, size, allocator); - a.allocated = 0; - a.used = 0; - a.sign = .Zero_or_Positive; - - if len(a.digit) != size { - return .Out_of_Memory; - } - a.allocated = size; - - if !allocator_zeroes { - _zero_unused(a); - } - return .OK; -} - -/* - Initialize from a signed or unsigned platform integer. - Inits a new `Int` and then calls the appropriate `set` routine. -*/ -int_init_from_integer :: proc(a: ^Int, src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (err: Error) - where intrinsics.type_is_integer(T) { - - n := _DEFAULT_DIGIT_COUNT; - if minimize { - n = _MIN_DIGIT_COUNT; - } - - return set(a, src, minimize); -} - -/* - Initialize an `Int` as a copy from another `Int`. -*/ -int_init_copy :: proc(dest, src: ^Int, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (err: Error) { - if !is_initialized(src) { - return .Invalid_Input; - } - - if err == .OK { - err = copy(dest, src); - } - return; } /* Helpers to set an `Int` to a specific value. */ -int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator_zeroes := true, allocator := context.allocator) -> (err: Error) +int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) where intrinsics.type_is_integer(T) { src := src; - if !is_initialized(dest) { - grow(dest, _DEFAULT_DIGIT_COUNT); + /* + Check that dest is usable. + */ + if dest == nil { + return .Nil_Pointer_Passed; } + if err = _grow_if_uninitialized(dest, minimize); err != .OK { return err; } + dest.used = 0; dest.sign = .Zero_or_Positive if src >= 0 else .Negative; src = abs(src); @@ -100,19 +48,25 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator_z dest.used += 1; src >>= _DIGIT_BITS; } - if minimize { - shrink(dest); - } _zero_unused(dest); return .OK; } -set :: proc{int_set_from_integer}; +set :: proc { int_set_from_integer, int_copy }; /* Copy one `Int` to another. */ -copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { +int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + /* + Check that src and dest are usable. + */ + if dest == nil || src == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(src) { + return .Int_Not_Initialized; + } + /* If dest == src, do nothing */ @@ -120,24 +74,17 @@ copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { return .OK; } - /* - Check `src` is initialized. - */ - if !is_initialized(src) { - return .Invalid_Input; - } - /* Grow `dest` to fit `src`. + If `dest` is not yet initialized, it will be using `allocator`. */ - if err = grow(dest, src.used); err != .OK { + if err = grow(dest, src.used, false, allocator); 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; } @@ -146,11 +93,21 @@ copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { _zero_unused(dest); return .OK; } +copy :: proc { int_copy, }; /* Set `dest` to |`src`|. */ -abs_bigint :: proc(dest, src: ^Int) -> (err: Error) { +int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + /* + Check that src and dest are usable. + */ + if dest == nil || src == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(src) { + return .Int_Not_Initialized; + } + /* If `dest == src`, just fix `dest`'s sign. */ @@ -159,17 +116,10 @@ abs_bigint :: proc(dest, src: ^Int) -> (err: Error) { 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 { + if err = copy(dest, src, allocator); err != .OK { return err; } @@ -180,15 +130,24 @@ abs_bigint :: proc(dest, src: ^Int) -> (err: Error) { return .OK; } -abs_integer :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { +platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { return n if n >= 0 else -n; } -abs :: proc{abs_bigint, abs_integer}; +abs :: proc{int_abs, platform_abs}; /* Set `dest` to `-src`. */ -neg :: proc(dest, src: ^Int) -> (err: Error) { +neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + /* + Check that src and dest are usable. + */ + if dest == nil || src == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(src) { + return .Int_Not_Initialized; + } + /* If `dest == src`, just fix `dest`'s sign. */ @@ -198,17 +157,10 @@ neg :: proc(dest, src: ^Int) -> (err: Error) { 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 { + if err = copy(dest, src, allocator); err != .OK { return err; } @@ -223,6 +175,15 @@ neg :: proc(dest, src: ^Int) -> (err: Error) { Helpers to extract values from the `Int`. */ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { + /* + Check that `a` is usable. + */ + if a == nil { + return 0, .Nil_Pointer_Passed; + } else if !is_initialized(a) { + return 0, .Int_Not_Initialized; + } + limb := bit_offset / _DIGIT_BITS; if limb < 0 || limb >= a.used { return 0, .Invalid_Input; @@ -237,6 +198,15 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { TODO: Optimize. */ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { + /* + Check that `a` is usable. + */ + if a == nil { + return 0, .Nil_Pointer_Passed; + } else if !is_initialized(a) { + return 0, .Int_Not_Initialized; + } + if count > _WORD_BITS || count < 1 { return 0, .Invalid_Input; } @@ -259,6 +229,10 @@ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { Resize backing store. */ shrink :: proc(a: ^Int) -> (err: Error) { + if a == nil { + return .Nil_Pointer_Passed; + } + needed := max(_MIN_DIGIT_COUNT, a.used); if a.used != needed { @@ -267,102 +241,91 @@ shrink :: proc(a: ^Int) -> (err: Error) { return .OK; } -grow :: proc(a: ^Int, n: int, allow_shrink := false) -> (err: Error) { - /* - By default, calling `grow` with `n` <= a.allocated won't resize. - With `allow_shrink` set to `true`, will call resize and shrink the `Int` as a result. - */ +int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { + if a == nil { + return .Nil_Pointer_Passed; + } + raw := transmute(mem.Raw_Dynamic_Array)a.digit; /* We need at least _MIN_DIGIT_COUNT or a.used digits, whichever is bigger. + The caller is asking for `digits`. Let's be accomodating. */ - needed := max(_MIN_DIGIT_COUNT, a.used); - /* - The caller is asking for `n`. Let's be accomodating. - */ - needed = max(needed, n); - /* - If `allow_shrink` == `false`, we need to needed >= `a.allocated`. - */ + needed := max(_MIN_DIGIT_COUNT, a.used, digits); if !allow_shrink { - needed = max(needed, a.allocated); + needed = max(needed, raw.cap); } - if a.allocated != needed { + /* + If not yet iniialized, initialize the `digit` backing with the allocator we were passed. + Otherwise, `[dynamic]DIGIT` already knows what allocator was used for it, so reuse will do the right thing. + */ + if raw.cap == 0 { + a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, needed, needed, allocator); + } else if raw.cap != needed { resize(&a.digit, needed); - if len(a.digit) != needed { - return .Out_of_Memory; - } } - - // a.used = min(size, a.used); - a.allocated = needed; + /* + Let's see if the allocation/resize worked as expected. + */ + if len(a.digit) != needed { + return .Out_of_Memory; + } return .OK; } +grow :: proc { int_grow, }; /* Clear `Int` and resize it to the default size. */ -clear :: proc(a: ^Int) -> (err: Error) { - assert_initialized(a); - - mem.zero_slice(a.digit[:]); - a.sign = .Zero_or_Positive; - a.used = 0; - grow(a, _DEFAULT_DIGIT_COUNT); - - return .OK; -} - -/* - Set the `Int` to 0 and optionally shrink it to the minimum backing size. -*/ -zero :: proc(a: ^Int, minimize := false) -> (err: Error) { - assert_initialized(a); - - a.sign = .Zero_or_Positive; - a.used = 0; - mem.zero_slice(a.digit[a.used:]); - if minimize { - return shrink(a); +int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + if a == nil { + return .Nil_Pointer_Passed; } - return .OK; + raw := transmute(mem.Raw_Dynamic_Array)a.digit; + if raw.cap != 0 { + mem.zero_slice(a.digit[:]); + } + a.sign = .Zero_or_Positive; + a.used = 0; + + return grow(a, a.used, minimize, allocator); } +clear :: proc { int_clear, }; +zero :: clear; /* Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ -one :: proc(a: ^Int, minimize := false) -> (err: Error) { - assert_initialized(a); - - a.sign = .Zero_or_Positive; - a.used = 1; - a.digit[0] = 1; - mem.zero_slice(a.digit[a.used:]); - if minimize { - return shrink(a); +int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + if err = clear(a, minimize, allocator); err != .OK { + return err; } + a.used = 1; + a.digit[0] = 1; + a.sign = .Zero_or_Positive; return .OK; } +one :: proc { int_one, }; /* Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ -minus_one :: proc(a: ^Int, minimize := false) -> (err: Error) { - assert_initialized(a); - - a.sign = .Negative; - a.used = 1; - a.digit[0] = 1; - mem.zero_slice(a.digit[a.used:]); - if minimize { - return shrink(a); +int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + if err = clear(a, minimize, allocator); err != .OK { + return err; } + a.used = 1; + a.digit[0] = 1; + a.sign = .Negative; return .OK; } +minus_one :: proc { int_minus_one, }; + + power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { assert_initialized(a); @@ -422,11 +385,18 @@ assert_initialized :: proc(a: ^Int, loc := #caller_location) { _zero_unused :: proc(a: ^Int) { assert_initialized(a); - if a.used < a.allocated { + if a.used < len(a.digit) { mem.zero_slice(a.digit[a.used:]); } } +_grow_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { + if !is_initialized(dest) { + return grow(dest, _MIN_DIGIT_COUNT if minimize else _DEFAULT_DIGIT_COUNT); + } + return .OK; +} + clamp :: proc(a: ^Int) { assert_initialized(a); /* @@ -444,4 +414,5 @@ clamp :: proc(a: ^Int) { if is_zero(a) { a.sign = .Zero_or_Positive; } -} \ No newline at end of file +} + diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 86080d041..228104ff7 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -76,7 +76,7 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator v := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); return fmt.tprintf("%v%v", sign, v); } else { - return fmt.tprintf("[%2d/%2d] %v%v", a.used, a.allocated, sign, a.digit[:a.used]); + return fmt.tprintf("[%2d] %v%v", a.used, sign, a.digit[:a.used]); } } return strings.clone(fallback(a), allocator), .Unimplemented; @@ -248,7 +248,6 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e */ t := &Int{ used = a.used, - allocated = a.allocated, sign = .Zero_or_Positive, digit = a.digit, }; From d9efa6c8b5cbaaf04c6468f465a6b402b4dc8e82 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 Jul 2021 09:06:28 +0200 Subject: [PATCH 022/105] big: More ZII refactoring. --- core/math/big/basic.odin | 46 +++++++++++++++++++++++++----------- core/math/big/example.odin | 4 ++-- core/math/big/helpers.odin | 48 ++++++++++++++++++++++++++------------ core/math/big/log.odin | 27 ++++++++++++--------- core/math/big/radix.odin | 5 ++-- 5 files changed, 87 insertions(+), 43 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 43f5a3715..8a50543ca 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -25,7 +25,11 @@ import "core:intrinsics" */ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - assert_initialized(dest); assert_initialized(a); assert_initialized(b); + if dest == nil || a == nil || b == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(a) || !is_initialized(b) { + return .Int_Not_Initialized; + } /* Handle both negative or both positive. @@ -56,8 +60,11 @@ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - assert_initialized(dest); assert_initialized(a); - + if dest == nil || a == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(a) { + return .Int_Not_Initialized; + } /* Fast paths for destination and input Int being the same. */ @@ -81,8 +88,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Grow destination as required. */ - err = grow(dest, a.used + 1); - if err != .OK { + if err = grow(dest, a.used + 1); err != .OK { return err; } @@ -170,7 +176,11 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); + if dest == nil || number == nil || decrease == nil { + return .Nil_Pointer_Passed; + } else if !(is_initialized(number) && is_initialized(decrease)) { + return .Int_Not_Initialized; + } if x.sign != y.sign { /* @@ -210,7 +220,11 @@ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - assert_initialized(dest); assert_initialized(a); + if dest == nil || a == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(a) { + return .Int_Not_Initialized; + } /* Fast paths for destination and input Int being the same. @@ -306,7 +320,11 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - assert_initialized(a); assert_initialized(b); assert_initialized(dest); + if dest == nil || a == nil || b == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(a) || !is_initialized(b) { + return .Int_Not_Initialized; + } old_used, min_used, max_used, i: int; @@ -318,8 +336,7 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { max_used = y.used; old_used = dest.used; - err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); - if err != .OK { + if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != .OK { return err; } dest.used = max_used + 1; @@ -387,15 +404,18 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - assert_initialized(number); assert_initialized(decrease); assert_initialized(dest); + if dest == nil || number == nil || decrease == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(number) || !is_initialized(decrease) { + return .Int_Not_Initialized; + } 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 { + if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != .OK { return err; } dest.used = max_used; diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 9cb99b3e7..616cbc553 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -44,9 +44,9 @@ _SQR_TOOM_CUTOFF, 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); + cb, _ := count_bits(a); + fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); } else { fmt.printf("%v (error: %v): %v\n", name, err, a); } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index a590967d8..5a2ea8962 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -328,7 +328,12 @@ minus_one :: proc { int_minus_one, }; power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { - assert_initialized(a); + /* + Check that `a` is usable. + */ + if a == nil { + return .Nil_Pointer_Passed; + } if power < 0 || power > _MAX_BIT_COUNT { return .Invalid_Input; @@ -356,13 +361,17 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { /* Count bits in an `Int`. */ -count_bits :: proc(a: ^Int) -> (count: int) { - assert_initialized(a); +count_bits :: proc(a: ^Int) -> (count: int, err: Error) { + if a == nil { + return 0, .Nil_Pointer_Passed; + } else if !is_initialized(a) { + return 0, .Int_Not_Initialized; + } /* Fast path for zero. */ if is_zero(a) { - return 0; + return 0, .OK; } /* Get the number of DIGITs and use it. @@ -384,7 +393,12 @@ assert_initialized :: proc(a: ^Int, loc := #caller_location) { } _zero_unused :: proc(a: ^Int) { - assert_initialized(a); + if a == nil { + return; + } else if !is_initialized(a) { + return; + } + if a.used < len(a.digit) { mem.zero_slice(a.digit[a.used:]); } @@ -397,15 +411,18 @@ _grow_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { return .OK; } -clamp :: proc(a: ^Int) { - assert_initialized(a); - /* - Trim unused digits - This is used to ensure that leading zero digits are - trimmed and the leading "used" digit will be non-zero. - Typically very fast. Also fixes the sign if there - are no more leading digits. - */ +/* + Trim unused digits. + + This is used to ensure that leading zero digits are trimmed and the leading "used" digit will be non-zero. + Typically very fast. Also fixes the sign if there are no more leading digits. +*/ +clamp :: proc(a: ^Int) -> (err: Error) { + if a == nil { + return .Nil_Pointer_Passed; + } else if !is_initialized(a) { + return .Int_Not_Initialized; + } for a.used > 0 && a.digit[a.used - 1] == 0 { a.used -= 1; @@ -414,5 +431,6 @@ clamp :: proc(a: ^Int) { if is_zero(a) { a.sign = .Zero_or_Positive; } -} + return .OK; +} \ No newline at end of file diff --git a/core/math/big/log.odin b/core/math/big/log.odin index 1ce2437bf..9c4fc3a2a 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -10,7 +10,11 @@ package big */ log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { - assert_initialized(a); + if a == nil { + return 0, .Nil_Pointer_Passed; + } else if !is_initialized(a) { + return 0, .Int_Not_Initialized; + } if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX { return -1, .Invalid_Input; } @@ -19,14 +23,14 @@ log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { 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; + return _log_power_of_two(a, base); } /* 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; + return log_n_digit(a.digit[0], DIGIT(base)); } // if (MP_HAS(S_MP_LOG)) { @@ -42,14 +46,15 @@ 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) { +_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { base := base; y: int; for y = 0; base & 1 == 0; { y += 1; base >>= 1; } - return (count_bits(a) - 1) / y; + log, err = count_bits(a); + return (log - 1) / y, err; } /* @@ -69,20 +74,20 @@ small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { return result; } -log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int) { +log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { /* If the number is smaller than the base, it fits within a fraction. Therefore, we return 0. */ if a < base { - return 0; + return 0, .OK; } /* If a number equals the base, the log is 1. */ if a == base { - return 1; + return 1, .OK; } N := _WORD(a); @@ -111,13 +116,13 @@ log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int) { bracket_low = bracket_mid; } if N == bracket_mid { - return mid; + return mid, .OK; } } if bracket_high == N { - return high; + return high, .OK; } else { - return low; + return low, .OK; } } \ No newline at end of file diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 228104ff7..0dd07672c 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -198,9 +198,10 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina buffer[available] = 0; } + shift, count: int; // mask := _WORD(radix - 1); - shift := int(log_n(DIGIT(radix), 2)); - count := int(count_bits(a)); + shift, err = log_n(DIGIT(radix), 2); + count, err = count_bits(a); // digit: _WORD; for offset := 0; offset < count; offset += 4 { From 7648f2e655b0e280bfb049eb02634d0f5cd136ac Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 Jul 2021 13:46:37 +0200 Subject: [PATCH 023/105] big: Finish big ZII refactor. --- core/math/big/basic.odin | 178 ++++++++++++--------- core/math/big/{bigint.odin => common.odin} | 32 ++-- core/math/big/compare.odin | 154 ++++++++++++------ core/math/big/example.odin | 6 +- core/math/big/helpers.odin | 147 ++++++++--------- core/math/big/log.odin | 27 ++-- core/math/big/logical.odin | 87 +++++----- core/math/big/radix.odin | 98 +++++++----- 8 files changed, 405 insertions(+), 324 deletions(-) rename core/math/big/{bigint.odin => common.odin} (87%) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 8a50543ca..4e923526a 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -25,11 +25,19 @@ import "core:intrinsics" */ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if dest == nil || a == nil || b == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) || !is_initialized(b) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return err; } + if err = clear_if_uninitialized(b); err != .None { + return err; + } + if err = clear_if_uninitialized(dest); err != .None { + return err; + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ /* Handle both negative or both positive. @@ -44,7 +52,7 @@ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { 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 { + if c, _ := cmp_mag(a, b); c == -1 { x, y = y, x; } @@ -60,11 +68,22 @@ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - if dest == nil || a == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return err; } + /* + Grow destination as required. + */ + if dest != a { + if err = grow(dest, a.used + 1); err != .None { + return err; + } + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ + /* Fast paths for destination and input Int being the same. */ @@ -72,46 +91,44 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow. */ - if is_pos(dest) && (dest.digit[0] + digit < _DIGIT_MAX) { + if p, _ := is_pos(dest); p && (dest.digit[0] + digit < _DIGIT_MAX) { dest.digit[0] += digit; - return .OK; + return .None; } /* Can be subtracted from dest.digit[0] without underflow. */ - if is_neg(a) && (dest.digit[0] > digit) { + if n, _ := is_neg(a); n && (dest.digit[0] > digit) { dest.digit[0] -= digit; - return .OK; + return .None; } } - /* - Grow destination as required. - */ - if err = grow(dest, a.used + 1); 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) { + if n, _ := is_neg(a); n && (a.used > 1 || a.digit[0] >= digit) { /* Temporarily fix `a`'s sign. */ - t := a; - t.sign = .Zero_or_Positive; + a.sign = .Zero_or_Positive; /* dest = |a| - digit */ - err = sub(dest, t, digit); + if err = sub(dest, a, digit); err != .None { + /* + Restore a's sign. + */ + a.sign = .Negative; + return err; + } /* Restore sign and set `dest` sign. */ + a.sign = .Negative; dest.sign = .Negative; - clamp(dest); - return err; + return clamp(dest); } /* @@ -122,7 +139,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* If `a` is positive */ - if is_pos(a) { + if p, _ := is_pos(a); p { /* Add digits, use `carry`. */ @@ -166,9 +183,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - - return .OK; + return clamp(dest); } /* @@ -176,11 +191,19 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - if dest == nil || number == nil || decrease == nil { - return .Nil_Pointer_Passed; - } else if !(is_initialized(number) && is_initialized(decrease)) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(dest); err != .None { + return err; } + if err = clear_if_uninitialized(x); err != .None { + return err; + } + if err = clear_if_uninitialized(y); err != .None { + return err; + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ if x.sign != y.sign { /* @@ -195,12 +218,16 @@ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { 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 { + if c, _ := cmp_mag(x, y); c == -1 { /* 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; + if p, _ := is_pos(x); p { + dest.sign = .Negative; + } else { + dest.sign = .Zero_or_Positive; + } x, y = y, x; } else { /* @@ -220,11 +247,21 @@ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - if dest == nil || a == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(dest); err != .None { + return err; } + /* + Grow destination as required. + */ + if dest != a { + if err = grow(dest, a.used + 1); err != .None { + return err; + } + } + /* + All parameters have been initialized. + We can now safely ignore errors from comparison routines. + */ /* Fast paths for destination and input Int being the same. @@ -233,31 +270,23 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* 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) { + if n, _ := is_neg(dest); n && (dest.digit[0] + digit < _DIGIT_MAX) { dest.digit[0] += digit; - return .OK; + return .None; } /* Can be subtracted from dest.digit[0] without underflow. */ - if is_pos(a) && (dest.digit[0] > digit) { + if p, _ := is_pos(a); p && (dest.digit[0] > digit) { dest.digit[0] -= digit; - return .OK; + return .None; } } - /* - 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) { + if n, _ := is_neg(a); n { t := a; t.sign = .Zero_or_Positive; @@ -273,7 +302,9 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* if `a`<= digit, simply fix the single digit. */ - if a.used == 1 && (a.digit[0] <= digit || is_zero(a)) { + z, _ := is_zero(a); + + if a.used == 1 && (a.digit[0] <= digit) || z { dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit; dest.sign = .Negative; dest.used = 1; @@ -303,9 +334,7 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - - return .OK; + return clamp(dest); } /* @@ -320,10 +349,11 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if dest == nil || a == nil || b == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) || !is_initialized(b) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(x); err != .None { + return err; + } + if err = clear_if_uninitialized(y); err != .None { + return err; } old_used, min_used, max_used, i: int; @@ -336,10 +366,13 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { max_used = y.used; old_used = dest.used; - if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != .OK { + if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } dest.used = max_used + 1; + /* + All parameters have been initialized. + */ /* Zero the carry */ carry := DIGIT(0); @@ -393,9 +426,7 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - - return .OK; + return clamp(dest); } /* @@ -404,10 +435,11 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - if dest == nil || number == nil || decrease == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(number) || !is_initialized(decrease) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(x); err != .None { + return err; + } + if err = clear_if_uninitialized(y); err != .None { + return err; } old_used := dest.used; @@ -415,10 +447,13 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { max_used := x.used; i: int; - if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != .OK { + if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } dest.used = max_used; + /* + All parameters have been initialized. + */ borrow := DIGIT(0); @@ -465,6 +500,5 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { /* Adjust dest.used based on leading zeroes. */ - clamp(dest); - return .OK; + return clamp(dest); } \ No newline at end of file diff --git a/core/math/big/bigint.odin b/core/math/big/common.odin similarity index 87% rename from core/math/big/bigint.odin rename to core/math/big/common.odin index f2c9366ad..bcbfafaef 100644 --- a/core/math/big/bigint.odin +++ b/core/math/big/common.odin @@ -53,27 +53,21 @@ Int :: struct { sign: Sign, }; -Comparison_Flag :: enum i8 { - Less_Than = -1, - Equal = 0, - Greater_Than = 1, +/* + Errors are a strict superset of runtime.Allocation_Error. +*/ +Error :: enum byte { + None = 0, + Out_Of_Memory = 1, + Invalid_Pointer = 2, + Invalid_Argument = 3, - /* One of the numbers was uninitialized */ - Uninitialized = -127, -}; + Unknown_Error = 4, + Max_Iterations_Reached = 5, + Buffer_Overflow = 6, + Integer_Overflow = 7, -Error :: enum i8 { - OK = 0, - Unknown_Error = -1, - Out_of_Memory = -2, - Invalid_Input = -3, - Max_Iterations_Reached = -4, - Buffer_Overflow = -5, - Integer_Overflow = -6, - Nil_Pointer_Passed = -7, - Int_Not_Initialized = -8, - - Unimplemented = -127, + Unimplemented = 127, }; Primality_Flag :: enum u8 { diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index 142a3bfe3..43e872ce1 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -22,61 +22,83 @@ int_is_initialized :: proc(a: ^Int) -> bool { return raw.cap >= _MIN_DIGIT_COUNT; } -int_is_zero :: proc(a: ^Int) -> bool { - return is_initialized(a) && a.used == 0; -} - -int_is_positive :: proc(a: ^Int) -> bool { - return is_initialized(a) && a.sign == .Zero_or_Positive; -} - -int_is_negative :: proc(a: ^Int) -> bool { - return is_initialized(a) && a.sign == .Negative; -} - -int_is_even :: proc(a: ^Int) -> bool { - if is_initialized(a) { - if is_zero(a) { - return true; - } - if a.used > 0 && a.digit[0] & 1 == 0 { - return true; - } +int_is_zero :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; } - return false; + return a.used == 0, .None; } -int_is_odd :: proc(a: ^Int) -> bool { - if is_initialized(a) { - return !is_even(a); +int_is_positive :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; } - return false; + return a.sign == .Zero_or_Positive, .None; +} + +int_is_negative :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + return a.sign == .Negative, .None; +} + +int_is_even :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + + res, err = is_zero(a); + if err != .None { + return false, err; + } else if res == true { + return true, .None; + } + + res = false; + if a.used > 0 && a.digit[0] & 1 == 0 { + res = true; + } + return res, .None; +} + +int_is_odd :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + + res, err = is_even(a); + return !res, err; } platform_int_is_power_of_two :: proc(a: int) -> bool { return ((a) != 0) && (((a) & ((a) - 1)) == 0); } -int_is_power_of_two :: proc(a: ^Int) -> (res: bool) { +int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return false, err; + } + /* Early out for Int == 0. */ if a.used == 0 { - return false; + return false, .None; } /* For an `Int` to be a power of two, its top limb has to be a power of two. */ if !platform_int_is_power_of_two(int(a.digit[a.used - 1])) { - return false; + return false, .None; } /* That was the only limb, so it's a power of two. */ if a.used == 1 { - return true; + return true, .None; } /* @@ -84,74 +106,102 @@ int_is_power_of_two :: proc(a: ^Int) -> (res: bool) { */ for i := 1; i < a.used; i += 1 { if a.digit[i - 1] != 0 { - return false; + return false, .None; } } - return true; + return true, .None; } /* Compare two `Int`s, signed. */ -int_compare :: proc(a, b: ^Int) -> Comparison_Flag { - if !is_initialized(a) { return .Uninitialized; } - if !is_initialized(b) { return .Uninitialized; } +int_compare :: proc(a, b: ^Int) -> (res: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } + if err = clear_if_uninitialized(b); err != .None { + return 0, err; + } + + neg: bool; + if neg, err = is_negative(a); err != .None { + return 0, err; + } /* Compare based on sign */ if a.sign != b.sign { - return .Less_Than if is_negative(a) else .Greater_Than; + res = -1 if neg else +1; + return res, .None; } - x, y := a, b; /* If negative, compare in the opposite direction */ - if is_neg(a) { - x, y = b, a; + if neg { + return cmp_mag(b, a); } - return cmp_mag(x, y); + return cmp_mag(a, b); } /* Compare an `Int` to an unsigned number upto the size of the backing type. */ -int_compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag { - if !is_initialized(a) { return .Uninitialized; } +int_compare_digit :: proc(a: ^Int, u: DIGIT) -> (res: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } /* Compare based on sign */ - if is_neg(a) { - return .Less_Than; + neg: bool; + if neg, err = is_neg(a); err != .None { + return 0, err; + } + if neg { + return -1, .None; } /* Compare based on magnitude */ if a.used > 1 { - return .Greater_Than; + return +1, .None; } /* Compare the only digit in `a` to `u`. */ if a.digit[0] != u { - return .Greater_Than if a.digit[0] > u else .Less_Than; + if a.digit[0] > u { + return +1, .None; + } + return -1, .None; } - return .Equal; + return 0, .None; } /* Compare the magnitude of two `Int`s, unsigned. */ -int_compare_magnitude :: proc(a, b: ^Int) -> Comparison_Flag { - if !is_initialized(a) { return .Uninitialized; } - if !is_initialized(b) { return .Uninitialized; } +int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } + if err = clear_if_uninitialized(b); err != .None { + return 0, err; + } /* Compare based on used digits */ if a.used != b.used { - return .Greater_Than if a.used > b.used else .Less_Than; + if a.used > b.used { + return +1, .None; + } + return -1, .None; } /* Same number of used digits, compare based on their value */ for n := a.used - 1; n >= 0; n -= 1 { if a.digit[n] != b.digit[n] { - return .Greater_Than if a.digit[n] > b.digit[n] else .Less_Than; + if a.digit[n] > b.digit[n] { + return +1, .None; + } + return -1, .None; } } - return .Equal; + return 0, .None; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 616cbc553..3c90719f1 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -44,7 +44,7 @@ _SQR_TOOM_CUTOFF, print :: proc(name: string, a: ^Int, base := i8(16)) { as, err := itoa(a, base); defer delete(as); - if err == .OK { + if err == .None { cb, _ := count_bits(a); fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); } else { @@ -80,10 +80,10 @@ demo :: proc() { print("c", c); fmt.println("\n\n=== Set a to (1 << 120) - 1 ==="); - if err = power_of_two(a, 120); err != .OK { + if err = power_of_two(a, 120); err != .None { fmt.printf("Error %v while setting a to 1 << 120.\n", err); } - if err = sub(a, a, 1); err != .OK { + if err = sub(a, a, 1); err != .None { fmt.printf("Error %v while subtracting 1 from a\n", err); } print("a", a, 16); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 5a2ea8962..af3d27e0f 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -30,15 +30,10 @@ int_destroy :: proc(integers: ..^Int) { int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) where intrinsics.type_is_integer(T) { src := src; - /* - Check that dest is usable. - */ - if dest == nil { - return .Nil_Pointer_Passed; + if err = clear_if_uninitialized(dest); err != .None { + return err; } - if err = _grow_if_uninitialized(dest, minimize); err != .OK { return err; } - dest.used = 0; dest.sign = .Zero_or_Positive if src >= 0 else .Negative; src = abs(src); @@ -49,7 +44,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : src >>= _DIGIT_BITS; } _zero_unused(dest); - return .OK; + return .None; } set :: proc { int_set_from_integer, int_copy }; @@ -58,27 +53,20 @@ set :: proc { int_set_from_integer, int_copy }; Copy one `Int` to another. */ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { - /* - Check that src and dest are usable. - */ - if dest == nil || src == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(src) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(src); err != .None { + return err; } - /* If dest == src, do nothing */ if (dest == src) { - return .OK; + return .None; } - /* Grow `dest` to fit `src`. If `dest` is not yet initialized, it will be using `allocator`. */ - if err = grow(dest, src.used, false, allocator); err != .OK { + if err = grow(dest, src.used, false, allocator); err != .None { return err; } @@ -91,7 +79,7 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error dest.used = src.used; dest.sign = src.sign; _zero_unused(dest); - return .OK; + return .None; } copy :: proc { int_copy, }; @@ -100,26 +88,23 @@ copy :: proc { int_copy, }; */ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* - Check that src and dest are usable. + Check that src is usable. */ - if dest == nil || src == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(src) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(src); err != .None { + return err; } - /* If `dest == src`, just fix `dest`'s sign. */ if (dest == src) { dest.sign = .Zero_or_Positive; - return .OK; + return .None; } /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != .OK { + if err = copy(dest, src, allocator); err != .None { return err; } @@ -127,7 +112,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) Fix sign. */ dest.sign = .Zero_or_Positive; - return .OK; + return .None; } platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { @@ -140,27 +125,29 @@ abs :: proc{int_abs, platform_abs}; */ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* - Check that src and dest are usable. + Check that src is usable. */ - if dest == nil || src == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(src) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(src); err != .None { + return err; } - /* 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; + sign := Sign.Zero_or_Positive; + if z, _ := is_zero(src); z { + sign = .Negative; + } + if n, _ := is_neg(src); n { + sign = .Negative; + } + if (dest == src) { + dest.sign = sign; + return .None; } - /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != .OK { + if err = copy(dest, src, allocator); err != .None { return err; } @@ -168,7 +155,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { Fix sign. */ dest.sign = sign; - return .OK; + return .None; } /* @@ -176,22 +163,20 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { */ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { /* - Check that `a` is usable. + Check that `a`is usable. */ - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } limb := bit_offset / _DIGIT_BITS; if limb < 0 || limb >= a.used { - return 0, .Invalid_Input; + return 0, .Invalid_Argument; } i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); - return 1 if ((a.digit[limb] & i) != 0) else 0, .OK; + return 1 if ((a.digit[limb] & i) != 0) else 0, .None; } /* @@ -199,16 +184,14 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { */ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* - Check that `a` is usable. + Check that `a`is usable. */ - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } if count > _WORD_BITS || count < 1 { - return 0, .Invalid_Input; + return 0, .Invalid_Argument; } v: DIGIT; @@ -216,7 +199,7 @@ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { for shift := 0; shift < count; shift += 1 { o := offset + shift; v, e = extract_bit(a, o); - if e != .OK { + if e != .None { break; } res = res + _WORD(v) << uint(shift); @@ -230,7 +213,7 @@ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { */ shrink :: proc(a: ^Int) -> (err: Error) { if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } needed := max(_MIN_DIGIT_COUNT, a.used); @@ -238,12 +221,12 @@ shrink :: proc(a: ^Int) -> (err: Error) { if a.used != needed { return grow(a, needed); } - return .OK; + return .None; } int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } raw := transmute(mem.Raw_Dynamic_Array)a.digit; @@ -269,9 +252,9 @@ int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := conte Let's see if the allocation/resize worked as expected. */ if len(a.digit) != needed { - return .Out_of_Memory; + return .Out_Of_Memory; } - return .OK; + return .None; } grow :: proc { int_grow, }; @@ -280,7 +263,7 @@ grow :: proc { int_grow, }; */ int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } raw := transmute(mem.Raw_Dynamic_Array)a.digit; @@ -299,14 +282,14 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != .OK { + if err = clear(a, minimize, allocator); err != .None { return err; } a.used = 1; a.digit[0] = 1; a.sign = .Zero_or_Positive; - return .OK; + return .None; } one :: proc { int_one, }; @@ -314,14 +297,14 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != .OK { + if err = clear(a, minimize, allocator); err != .None { return err; } a.used = 1; a.digit[0] = 1; a.sign = .Negative; - return .OK; + return .None; } minus_one :: proc { int_minus_one, }; @@ -332,18 +315,18 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Check that `a` is usable. */ if a == nil { - return .Nil_Pointer_Passed; + return .Invalid_Pointer; } if power < 0 || power > _MAX_BIT_COUNT { - return .Invalid_Input; + return .Invalid_Argument; } /* Grow to accomodate the single bit. */ a.used = (power / _DIGIT_BITS) + 1; - if err = grow(a, a.used); err != .OK { + if err = grow(a, a.used); err != .None { return err; } /* @@ -355,23 +338,21 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Set the bit. */ a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS)); - return .OK; + return .None; } /* Count bits in an `Int`. */ count_bits :: proc(a: ^Int) -> (count: int, err: Error) { - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } /* Fast path for zero. */ - if is_zero(a) { - return 0, .OK; + if z, _ := is_zero(a); z { + return 0, .None; } /* Get the number of DIGITs and use it. @@ -404,11 +385,11 @@ _zero_unused :: proc(a: ^Int) { } } -_grow_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { +clear_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { if !is_initialized(dest) { return grow(dest, _MIN_DIGIT_COUNT if minimize else _DEFAULT_DIGIT_COUNT); } - return .OK; + return .None; } /* @@ -418,19 +399,17 @@ _grow_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { Typically very fast. Also fixes the sign if there are no more leading digits. */ clamp :: proc(a: ^Int) -> (err: Error) { - if a == nil { - return .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return .Int_Not_Initialized; + if err = clear_if_uninitialized(a); err != .None { + return err; } for a.used > 0 && a.digit[a.used - 1] == 0 { a.used -= 1; } - if is_zero(a) { + if z, _ := is_zero(a); z { a.sign = .Zero_or_Positive; } - return .OK; + return .None; } \ No newline at end of file diff --git a/core/math/big/log.odin b/core/math/big/log.odin index 9c4fc3a2a..bb918f690 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -10,13 +10,18 @@ package big */ log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { - if a == nil { - return 0, .Nil_Pointer_Passed; - } else if !is_initialized(a) { - return 0, .Int_Not_Initialized; + if base < 2 || DIGIT(base) > _DIGIT_MAX { + return -1, .Invalid_Argument; } - if is_neg(a) || is_zero(a) || base < 2 || DIGIT(base) > _DIGIT_MAX { - return -1, .Invalid_Input; + + if err = clear_if_uninitialized(a); err != .None { + return -1, err; + } + if n, _ := is_neg(a); n { + return -1, .Invalid_Argument; + } + if z, _ := is_zero(a); z { + return -1, .Invalid_Argument; } /* @@ -80,14 +85,14 @@ log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { Therefore, we return 0. */ if a < base { - return 0, .OK; + return 0, .None; } /* If a number equals the base, the log is 1. */ if a == base { - return 1, .OK; + return 1, .None; } N := _WORD(a); @@ -116,13 +121,13 @@ log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { bracket_low = bracket_mid; } if N == bracket_mid { - return mid, .OK; + return mid, .None; } } if bracket_high == N { - return high, .OK; + return high, .None; } else { - return low, .OK; + return low, .None; } } \ No newline at end of file diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 6c857a2f2..47537d092 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -20,29 +20,33 @@ package big 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); - + if err = clear_if_uninitialized(a); err != .None { + return err; + } + if err = clear_if_uninitialized(b); err != .None { + return err; + } 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 { + if err = grow(dest, used); err != .None { return err; } + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a && neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + for i := 0; i < used; i += 1 { x, y: DIGIT; /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_a { ac += _MASK if i >= a.used else (~a.digit[i] & _MASK); x = ac & _MASK; ac >>= _DIGIT_BITS; @@ -53,7 +57,7 @@ and :: proc(dest, a, b: ^Int) -> (err: Error) { /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_b { bc += _MASK if i >= b.used else (~b.digit[i] & _MASK); y = bc & _MASK; bc >>= _DIGIT_BITS; @@ -75,37 +79,40 @@ and :: proc(dest, a, b: ^Int) -> (err: Error) { dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - clamp(dest); - return .OK; + return clamp(dest); } /* 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); - + if err = clear_if_uninitialized(a); err != .None { + return err; + } + if err = clear_if_uninitialized(b); err != .None { + return err; + } 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 { + if err = grow(dest, used); err != .None { return err; } + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a || neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + for i := 0; i < used; i += 1 { x, y: DIGIT; /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_a { ac += _MASK if i >= a.used else (~a.digit[i] & _MASK); x = ac & _MASK; ac >>= _DIGIT_BITS; @@ -116,7 +123,7 @@ or :: proc(dest, a, b: ^Int) -> (err: Error) { /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_b { bc += _MASK if i >= b.used else (~b.digit[i] & _MASK); y = bc & _MASK; bc >>= _DIGIT_BITS; @@ -138,37 +145,40 @@ or :: proc(dest, a, b: ^Int) -> (err: Error) { dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - clamp(dest); - return .OK; + return clamp(dest); } /* 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); - + if err = clear_if_uninitialized(a); err != .None { + return err; + } + if err = clear_if_uninitialized(b); err != .None { + return err; + } 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 { + if err = grow(dest, used); err != .None { return err; } + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a != neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + for i := 0; i < used; i += 1 { x, y: DIGIT; /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_a { ac += _MASK if i >= a.used else (~a.digit[i] & _MASK); x = ac & _MASK; ac >>= _DIGIT_BITS; @@ -179,7 +189,7 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) { /* Convert to 2's complement if negative. */ - if is_neg(a) { + if neg_b { bc += _MASK if i >= b.used else (~b.digit[i] & _MASK); y = bc & _MASK; bc >>= _DIGIT_BITS; @@ -201,6 +211,5 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) { dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - clamp(dest); - return .OK; + return clamp(dest); } \ No newline at end of file diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 0dd07672c..8b4abc76d 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -19,8 +19,10 @@ import "core:strings" 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); + a := a; radix := radix; + if err = clear_if_uninitialized(a); err != .None { + return "", err; + } /* Radix defaults to 10. */ @@ -32,14 +34,13 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator */ /* - Calculate the size of the buffer we need. + Calculate the size of the buffer we need, and */ size: int; - size, err = radix_size(a, radix, zero_terminate); /* Exit if calculating the size returned an error. */ - if err != .OK { + if size, err = radix_size(a, radix, zero_terminate); err != .None { f := strings.clone(fallback(a), allocator); if zero_terminate { c := strings.clone_to_cstring(f); @@ -57,14 +58,13 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator Write the digits out into the buffer. */ written: int; - written, err = itoa_raw(a, radix, buffer, size, zero_terminate); + if written, err = itoa_raw(a, radix, buffer, size, zero_terminate); err == .None { + return string(buffer[:written]), .None; + } /* For now, delete the buffer and fall back to the below on failure. */ - if err == .OK { - return string(buffer[:written]), .OK; - } delete(buffer); fallback :: proc(a: ^Int, print_raw := false) -> string { @@ -86,8 +86,10 @@ 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); + a := a; radix := radix; + if err = clear_if_uninitialized(a); err != .None { + return "", err; + } /* Radix defaults to 10. */ @@ -119,21 +121,25 @@ 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; + a := a; radix := radix; size := size; + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } /* Radix defaults to 10. */ radix = radix if radix > 0 else 10; if radix < 2 || radix > 64 { - return 0, .Invalid_Input; + return 0, .Invalid_Argument; } /* We weren't given a size. Let's compute it. */ if size == -1 { - size, err = radix_size(a, radix, zero_terminate); + if size, err = radix_size(a, radix, zero_terminate); err != .None { + return 0, err; + } } /* @@ -146,7 +152,8 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina /* Fast path for when `Int` == 0 or the entire `Int` fits in a single radix digit. */ - if is_zero(a) || (a.used == 1 && a.digit[0] < DIGIT(radix)) { + z, _ := is_zero(a); + if z || (a.used == 1 && a.digit[0] < DIGIT(radix)) { if zero_terminate { available -= 1; buffer[available] = 0; @@ -154,12 +161,12 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina available -= 1; buffer[available] = RADIX_TABLE[a.digit[0]]; - if is_neg(a) { + if n, _ := is_neg(a); n { available -= 1; buffer[available] = '-'; } - return len(buffer) - available, .OK; + return len(buffer) - available, .None; } /* @@ -179,11 +186,11 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina val = q; } - if is_neg(a) { + if n, _ := is_neg(a); n { available -= 1; buffer[available] = '-'; } - return len(buffer) - available, .OK; + return len(buffer) - available, .None; } /* At least 3 DIGITs are in use if we made it this far. @@ -202,24 +209,23 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina // mask := _WORD(radix - 1); shift, err = log_n(DIGIT(radix), 2); count, err = count_bits(a); - // digit: _WORD; + digit: _WORD; for offset := 0; offset < count; offset += 4 { bits_to_get := int(min(count - offset, shift)); - digit, err := extract_bits(a, offset, bits_to_get); - if err != .OK { - return len(buffer) - available, .Invalid_Input; + if digit, err = extract_bits(a, offset, bits_to_get); err != .None { + return len(buffer) - available, .Invalid_Argument; } available -= 1; buffer[available] = RADIX_TABLE[digit]; } - if is_neg(a) { + if n, _ := is_neg(a); n { available -= 1; buffer[available] = '-'; } - return len(buffer) - available, .OK; + return len(buffer) - available, .None; } return -1, .Unimplemented; @@ -233,37 +239,41 @@ int_to_cstring :: itoa_cstring; We size for `string`, not `cstring`. */ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, err: Error) { + a := a; if radix < 2 || radix > 64 { - return -1, .Invalid_Input; + return -1, .Invalid_Argument; + } + if err = clear_if_uninitialized(a); err != .None { + return 0, err; } - if is_zero(a) { - if zero_terminate { - return 2, .OK; - } - return 1, .OK; - } + if z, _ := is_zero(a); z { + if zero_terminate { + return 2, .None; + } + return 1, .None; + } - /* + /* Calculate `log` on a temporary "copy" with its sign set to positive. - */ + */ t := &Int{ used = a.used, sign = .Zero_or_Positive, digit = a.digit, }; - size, err = log_n(t, DIGIT(radix)); - if err != .OK { - return; - } + if size, err = log_n(t, DIGIT(radix)); err != .None { + return 0, err; + } - /* + /* log truncates to zero, so we need to add one more, and one for `-` if negative. - */ - size += 2 if is_neg(a) else 1; - size += 1 if zero_terminate else 0; - return size, .OK; + */ + n, _ := is_neg(a); + size += 2 if n else 1; + size += 1 if zero_terminate else 0; + return size, .None; } /* From 1d0b37c1d8e3c3b6a88a5e208124e118ebc91f9a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 21 Jul 2021 17:56:15 +0200 Subject: [PATCH 024/105] big: add shl1, shr1. --- core/math/big/basic.odin | 128 ++++++++++++++++++++++++++++++++++++- core/math/big/example.odin | 13 ++-- core/math/big/helpers.odin | 46 ++++++++++++- core/math/big/logical.odin | 34 +++++++++- 4 files changed, 213 insertions(+), 8 deletions(-) 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 From 78c0877994427d1c215c00a0623ce4d9a0bfb542 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 22 Jul 2021 17:21:39 +0200 Subject: [PATCH 025/105] big: Add `get(a, type)` and `get_float`. --- core/math/big/common.odin | 7 --- core/math/big/example.odin | 14 ++--- core/math/big/helpers.odin | 114 +++++++++++++++++++++++++++++++------ 3 files changed, 105 insertions(+), 30 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index bcbfafaef..c8e219927 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -35,13 +35,6 @@ _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, diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 380d1423b..014e19af6 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -57,16 +57,16 @@ demo :: proc() { a, b, c := &Int{}, &Int{}, &Int{}; defer destroy(a, b, c); - err = set(a, 512); + err = set(a, 0); + a.used = 2; + a.digit[1] = 1; 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, 10); + fmt.printf("%v (%v)\n", int_get_float(a)); + + + print("a", a, 16); print("b", b, 10); print("c", c, 10); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 471c501b2..a991ec9b0 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -309,8 +309,6 @@ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator } minus_one :: proc { int_minus_one, }; - - power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { /* Check that `a` is usable. @@ -339,7 +337,91 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Set the bit. */ a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS)); - return .None; + return .None; +} + +int_get_u128 :: proc(a: ^Int) -> (res: u128, err: Error) { + return int_get(a, u128); +} +get_u128 :: proc { int_get_u128, }; + +int_get_i128 :: proc(a: ^Int) -> (res: i128, err: Error) { + return int_get(a, i128); +} +get_i128 :: proc { int_get_i128, }; + +int_get_u64 :: proc(a: ^Int) -> (res: u64, err: Error) { + return int_get(a, u64); +} +get_u64 :: proc { int_get_u64, }; + +int_get_i64 :: proc(a: ^Int) -> (res: i64, err: Error) { + return int_get(a, i64); +} +get_i64 :: proc { int_get_i64, }; + +int_get_u32 :: proc(a: ^Int) -> (res: u32, err: Error) { + return int_get(a, u32); +} +get_u32 :: proc { int_get_u32, }; + +int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) { + return int_get(a, i32); +} +get_i32 :: proc { int_get_i32, }; + +/* + TODO: Think about using `count_bits` to check if the value could be returned completely, + and maybe return max(T), .Integer_Overflow if not? +*/ +int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } + + size_in_bits := int(size_of(T) * 8); + i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS); + i = min(int(a.used), i); + + for ; i >= 0; i -= 1 { + res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS; + res |= T(a.digit[i]); + if size_in_bits <= _DIGIT_BITS { + break; + }; + } + + when !intrinsics.type_is_unsigned(T) { + /* + Mask off sign bit. + */ + res ~= 1 << uint(size_in_bits - 1); + /* + Set the sign. + */ + if a.sign == .Negative { + res = -res; + } + } + return; +} +get :: proc { int_get, }; + +int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { + if err = clear_if_uninitialized(a); err != .None { + return 0, err; + } + + l := min(a.used, 17); // log2(max(f64)) is approximately 1020, or 17 legs. + fac := f64(1 << _DIGIT_BITS); + d := 0.0; + + for i := l; i >= 0; i -= 1 { + d = (d * fac) + f64(a.digit[i]); + } + + res = -d if a.sign == .Negative else d; + return; } /* @@ -393,21 +475,21 @@ count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { 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; - } + /* + 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; + return count, .None; } /* From d4d863c4db6fcc893ff3a04ff3ef4ca1ef757f4c Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 22 Jul 2021 18:12:19 +0200 Subject: [PATCH 026/105] big: Add `mod_power_of_two`. --- core/math/big/basic.odin | 59 ++++++++++++++++++++++++++++++++------ core/math/big/example.odin | 8 +++--- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 7a3bbf751..42db8b88e 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -322,7 +322,7 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { carry := dest.digit[i] >> ((size_of(DIGIT) * 8) - 1); dest.digit[i] &= _MASK; } - } + } zero_count := old_used - dest.used; /* @@ -374,13 +374,13 @@ int_halve :: proc(dest, src: ^Int) -> (err: Error) { 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; - } + /* + Forward carry to next iteration. + */ + fwd_carry = carry; + } - zero_count := old_used - dest.used; + zero_count := old_used - dest.used; /* Zero remainder. */ @@ -447,7 +447,7 @@ int_double :: proc(dest, src: ^Int) -> (err: Error) { */ dest.digit[dest.used] = 1; } - zero_count := old_used - dest.used; + zero_count := old_used - dest.used; /* Zero remainder. */ @@ -463,6 +463,49 @@ int_double :: proc(dest, src: ^Int) -> (err: Error) { double :: proc { int_double, }; shl1 :: double; + +/* + dest = src % (2^power); +*/ +int_mod_power_of_two :: proc(dest, src: ^Int, power: int) -> (err: Error) { + dest := dest; src := src; + if err = clear_if_uninitialized(dest); err != .None { + return err; + } + if err = clear_if_uninitialized(src); err != .None { + return err; + } + + if power < 0 { return .Invalid_Argument; } + if power == 0 { return zero(dest); } + + /* + If the modulus is larger than the value, return the value. + */ + err = copy(dest, src); + if power >= (src.used * _DIGIT_BITS) || err != .None { + return; + } + + /* + Zero digits above the last digit of the modulus. + */ + zero_count := (power / _DIGIT_BITS) + 0 if (power % _DIGIT_BITS == 0) else 1; + /* + Zero remainder. + */ + if zero_count > 0 { + mem.zero_slice(dest.digit[zero_count:]); + } + + /* + Clear the digit that is not completely outside/inside the modulus. + */ + dest.digit[power / _DIGIT_BITS] &= DIGIT(1 << DIGIT(power % _DIGIT_BITS)) - DIGIT(1); + return clamp(dest); +} +mod_power_of_two :: proc { int_mod_power_of_two, }; + /* ========================== Low-level routines diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 014e19af6..1b8503233 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -57,16 +57,16 @@ demo :: proc() { a, b, c := &Int{}, &Int{}, &Int{}; defer destroy(a, b, c); - err = set(a, 0); - a.used = 2; - a.digit[1] = 1; + err = set(a, -512); err = set(b, 1); err = set(c, -4); + + err = mod_power_of_two(a, a, 10); fmt.printf("%v (%v)\n", int_get_float(a)); - print("a", a, 16); + print("a", a, 10); print("b", b, 10); print("c", c, 10); From f34ba44bf88e830bdecebfd3948a7c7ffb4e2215 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 22 Jul 2021 23:00:36 +0200 Subject: [PATCH 027/105] big: Add `shl`, `shr` and `shrmod`. --- core/math/big/basic.odin | 29 +++--- core/math/big/common.odin | 8 +- core/math/big/example.odin | 9 +- core/math/big/logical.odin | 199 ++++++++++++++++++++++++++++++++++++- 4 files changed, 218 insertions(+), 27 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 42db8b88e..cbabd073b 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -463,48 +463,47 @@ int_double :: proc(dest, src: ^Int) -> (err: Error) { double :: proc { int_double, }; shl1 :: double; - /* - dest = src % (2^power); + remainder = numerator % (1 << bits) */ -int_mod_power_of_two :: proc(dest, src: ^Int, power: int) -> (err: Error) { - dest := dest; src := src; - if err = clear_if_uninitialized(dest); err != .None { +int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { + remainder := remainder; numerator := numerator; + if err = clear_if_uninitialized(remainder); err != .None { return err; } - if err = clear_if_uninitialized(src); err != .None { + if err = clear_if_uninitialized(numerator); err != .None { return err; } - if power < 0 { return .Invalid_Argument; } - if power == 0 { return zero(dest); } + if bits < 0 { return .Invalid_Argument; } + if bits == 0 { return zero(remainder); } /* If the modulus is larger than the value, return the value. */ - err = copy(dest, src); - if power >= (src.used * _DIGIT_BITS) || err != .None { + err = copy(remainder, numerator); + if bits >= (numerator.used * _DIGIT_BITS) || err != .None { return; } /* Zero digits above the last digit of the modulus. */ - zero_count := (power / _DIGIT_BITS) + 0 if (power % _DIGIT_BITS == 0) else 1; + zero_count := (bits / _DIGIT_BITS) + 0 if (bits % _DIGIT_BITS == 0) else 1; /* Zero remainder. */ if zero_count > 0 { - mem.zero_slice(dest.digit[zero_count:]); + mem.zero_slice(remainder.digit[zero_count:]); } /* Clear the digit that is not completely outside/inside the modulus. */ - dest.digit[power / _DIGIT_BITS] &= DIGIT(1 << DIGIT(power % _DIGIT_BITS)) - DIGIT(1); - return clamp(dest); + remainder.digit[bits / _DIGIT_BITS] &= DIGIT(1 << DIGIT(bits % _DIGIT_BITS)) - DIGIT(1); + return clamp(remainder); } -mod_power_of_two :: proc { int_mod_power_of_two, }; +mod_bits :: proc { int_mod_bits, }; /* ========================== diff --git a/core/math/big/common.odin b/core/math/big/common.odin index c8e219927..fcfdb3973 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -94,11 +94,11 @@ when size_of(rawptr) == 8 { /* We can use u128 as an intermediary. */ - DIGIT :: distinct(u64); - _WORD :: distinct(u128); + DIGIT :: distinct u64; + _WORD :: distinct u128; } else { - DIGIT :: distinct(u32); - _WORD :: distinct(u64); + DIGIT :: distinct u32; + _WORD :: distinct u64; } #assert(size_of(_WORD) == 2 * size_of(DIGIT)); diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 1b8503233..b004d115d 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -57,16 +57,11 @@ demo :: proc() { a, b, c := &Int{}, &Int{}, &Int{}; defer destroy(a, b, c); - err = set(a, -512); + err = set(a, 1); err = set(b, 1); err = set(c, -4); - - err = mod_power_of_two(a, a, 10); - fmt.printf("%v (%v)\n", int_get_float(a)); - - - print("a", a, 10); + print("a", a, 16); print("b", b, 10); print("c", c, 10); diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 8bc468203..53e296dc1 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -11,6 +11,8 @@ package big This file contains logical operations like `and`, `or` and `xor`. */ +import "core:mem" + /* The `and`, `or` and `xor` binops differ in two lines only. We could handle those with a switch, but that adds overhead. @@ -244,4 +246,199 @@ int_complement :: proc(dest, src: ^Int) -> (err: Error) { return err; } -complement :: proc { int_complement, }; \ No newline at end of file +complement :: proc { int_complement, }; + +/* + quotient, remainder := numerator >> bits; + `remainder` is allowed to be passed a `nil`, in which case `mod` won't be computed. +*/ +int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int) -> (err: Error) { + bits := bits; + if err = clear_if_uninitialized(quotient); err != .None { return err; } + if err = clear_if_uninitialized(numerator); err != .None { return err; } + + if bits < 0 { return .Invalid_Argument; } + + if err = copy(quotient, numerator); err != .None { return err; } + + /* + Shift right by a certain bit count (store quotient and optional remainder.) + `numerator` should not be used after this. + */ + if remainder != nil { + if err = mod_bits(remainder, numerator, bits); err != .None { return err; } + } + + /* + Shift by as many digits in the bit count. + */ + if bits >= _DIGIT_BITS { + if err = shr_digit(quotient, bits / _DIGIT_BITS); err != .None { return err; } + } + + /* + Shift any bit count < _DIGIT_BITS. + */ + bits %= _DIGIT_BITS; + if bits != 0 { + mask := DIGIT(1 << uint(bits)) - 1; + shift := DIGIT(_DIGIT_BITS - bits); + carry := DIGIT(0); + + for x := quotient.used; x >= 0; x -= 1 { + /* + Get the lower bits of this word in a temp. + */ + fwd_carry := quotient.digit[x] & mask; + + /* + Shift the current word and mix in the carry bits from the previous word. + */ + quotient.digit[x] = (quotient.digit[x] >> uint(bits)) | (carry << shift); + + /* + Update carry from forward carry. + */ + carry = fwd_carry; + } + + } + return clamp(numerator); +} +shrmod :: proc { int_shrmod, }; + +int_shr :: proc(dest, source: ^Int, bits: int) -> (err: Error) { + return shrmod(dest, nil, source, bits); +} +shr :: proc { int_shr, }; + +/* + Shift right by `digits` * _DIGIT_BITS bits. +*/ +int_shr_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { + /* + Check that `quotient` is usable. + */ + if err = clear_if_uninitialized(quotient); err != .None { return err; } + + if digits <= 0 { return .None; } + + /* + If digits > used simply zero and return. + */ + if digits > quotient.used { + return zero(quotient); + } + + /* + Much like `int_shl_digit`, this is implemented using a sliding window, + except the window goes the other way around. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + + for x := 0; x < (quotient.used - digits); x += 1 { + quotient.digit[x] = quotient.digit[x + digits]; + } + quotient.used -= digits; + _zero_unused(quotient); + return .None; +} +shr_digit :: proc { int_shr_digit, }; + +/* + Shift left by a certain bit count. +*/ +int_shl :: proc(dest, src: ^Int, bits: int) -> (err: Error) { + bits := bits; + if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != .None { return err; } + + if bits < 0 { + return .Invalid_Argument; + } + + if err = copy(dest, src); err != .None { return err; } + + /* + Grow `dest` to accommodate the additional bits. + */ + digits_needed := dest.used + (bits / _DIGIT_BITS) + 1; + if err = grow(dest, digits_needed); err != .None { return err; } + dest.used = digits_needed; + /* + Shift by as many digits in the bit count as we have. + */ + if bits >= _DIGIT_BITS { + if err = shl_digit(dest, bits / _DIGIT_BITS); err != .None { return err; } + } + + /* + Shift any remaining bit count < _DIGIT_BITS + */ + bits %= _DIGIT_BITS; + if bits != 0 { + mask := (DIGIT(1) << uint(bits)) - DIGIT(1); + shift := DIGIT(_DIGIT_BITS - bits); + carry := DIGIT(0); + + for x:= 0; x <= dest.used; x+= 1 { + fwd_carry := (dest.digit[x] >> shift) & mask; + dest.digit[x] = (dest.digit[x] << uint(bits) | carry) & _MASK; + carry = fwd_carry; + } + + /* + Use final carry. + */ + if carry != 0 { + dest.digit[dest.used] = carry; + dest.used += 1; + } + } + return clamp(dest); +} +shl :: proc { int_shl, }; + + +/* + Shift left by `digits` * _DIGIT_BITS bits. +*/ +int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { + /* + Check that `quotient` is usable. + */ + if err = clear_if_uninitialized(quotient); err != .None { return err; } + + if digits <= 0 { return .None; } + + /* + No need to shift a zero. + */ + z: bool; + if z, err = is_zero(quotient); z || err != .None { return err; } + + /* + Resize `quotient` to accomodate extra digits. + */ + if err = grow(quotient, quotient.used + digits); err != .None { return err; } + + /* + Increment the used by the shift amount then copy upwards. + */ + quotient.used += digits; + + /* + Much like `int_shr_digit`, this is implemented using a sliding window, + except the window goes the other way around. + */ + for x := quotient.used; x >= digits; x -= 1 { + quotient.digit[x] = quotient.digit[x - digits]; + } + + mem.zero_slice(quotient.digit[:digits]); + return .None; +} +shl_digit :: proc { int_shl_digit, }; \ No newline at end of file From 0254057f1b6f421b74379fcc48a42361d889edca Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 23 Jul 2021 01:13:24 +0200 Subject: [PATCH 028/105] big: Add `swap`. --- core/math/big/basic.odin | 61 ++++++++++++++++++++++++++++++++++++++ core/math/big/example.odin | 37 ++++++----------------- core/math/big/helpers.odin | 19 +++++++++++- 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index cbabd073b..b16635849 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -669,4 +669,65 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { Adjust dest.used based on leading zeroes. */ return clamp(dest); +} + + +/* + Multiplies |a| * |b| and only computes upto digs digits of result. + HAC pp. 595, Algorithm 14.12 Modified so you can control how + many digits of output are created. +*/ +_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { + + /* + Can we use the fast multiplier? + */ + when false { // Have Comba? + if digits < _WARRAY && min(a.used, b.used) < _MAX_COMBA { + return _int_mul_comba(dest, a, b, digits); + } + } + + if err = grow(dest, digits); err != .None { return err; } + dest.used = digits; + + /* + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + int iy, pb; + mp_digit u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MP_MIN(b->used, digs - ix); + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + mp_word r = (mp_word)t.dp[ix + iy] + + ((mp_word)a->dp[ix] * (mp_word)b->dp[iy]) + + (mp_word)u; + + /* the new column is the lower part of the result */ + t.dp[ix + iy] = (mp_digit)(r & (mp_word)MP_MASK); + + /* get the carry word from the result */ + u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + } + /* set carry if it is placed below digs */ + if ((ix + iy) < digs) { + t.dp[ix + pb] = u; + } + } + + mp_clamp(&t); + mp_exch(&t, c); + + mp_clear(&t); + return MP_OKAY; +} + +*/ + return .None; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index b004d115d..6da1a292c 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -19,7 +19,7 @@ print_configation :: proc() { DIGIT_BITS %v MIN_DIGIT_COUNT %v MAX_DIGIT_COUNT %v - EFAULT_DIGIT_COUNT %v + DEFAULT_DIGIT_COUNT %v MAX_COMBA %v WARRAY %v MUL_KARATSUBA_CUTOFF %v @@ -57,37 +57,18 @@ demo :: proc() { a, b, c := &Int{}, &Int{}, &Int{}; defer destroy(a, b, c); - err = set(a, 1); - err = set(b, 1); - err = set(c, -4); + err = set(a, -512); + err = set(b, 1024); print("a", a, 16); - print("b", b, 10); - print("c", c, 10); + print("b", b, 16); - fmt.println("=== a = a & b ==="); - err = and(a, a, b); - fmt.printf("a &= b error: %v\n", err); + fmt.println("--- swap ---"); + foo(a, b); - 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 != .None { - fmt.printf("Error %v while setting a to 1 << 120.\n", err); - } - if err = sub(a, a, 1); err != .None { - fmt.printf("Error %v while subtracting 1 from a\n", err); - } print("a", a, 16); - fmt.println("Expected a to be: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + print("b", b, 16); + } main :: proc() { @@ -95,7 +76,7 @@ main :: proc() { mem.tracking_allocator_init(&ta, context.allocator); context.allocator = mem.tracking_allocator(&ta); - // print_configation(); + print_configation(); demo(); if len(ta.allocation_map) > 0 { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index a991ec9b0..e36f0614b 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -20,7 +20,10 @@ int_destroy :: proc(integers: ..^Int) { for a in &integers { mem.zero_slice(a.digit[:]); - free(&a.digit[0]); + raw := transmute(mem.Raw_Dynamic_Array)a.digit; + if raw.cap > 0 { + free(&a.digit[0]); + } a = &Int{}; } } @@ -84,6 +87,20 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error } copy :: proc { int_copy, }; +/* + In normal code, you can also write `a, b = b, a`. + However, that only swaps within the current scope. + This helper swaps completely. +*/ +int_swap :: proc(a, b: ^Int) { + a := a; b := b; + + a.used, b.used = b.used, a.used; + a.sign, b.sign = b.sign, a.sign; + a.digit, b.digit = b.digit, a.digit; +} +swap :: proc { int_swap, }; + /* Set `dest` to |`src`|. */ From b4a29844e96756043933d38cf0e71cc86fe27f65 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 23 Jul 2021 19:04:55 +0200 Subject: [PATCH 029/105] big: Add multiplication. --- core/math/big/basic.odin | 241 ++++++++++++++++++++++++++++++------- core/math/big/example.odin | 16 ++- 2 files changed, 207 insertions(+), 50 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index b16635849..2e04c3c44 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -13,6 +13,7 @@ package big import "core:mem" import "core:intrinsics" +import "core:fmt" /* =========================== @@ -467,13 +468,8 @@ shl1 :: double; remainder = numerator % (1 << bits) */ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { - remainder := remainder; numerator := numerator; - if err = clear_if_uninitialized(remainder); err != .None { - return err; - } - if err = clear_if_uninitialized(numerator); err != .None { - return err; - } + if err = clear_if_uninitialized(remainder); err != .None { return err; } + if err = clear_if_uninitialized(numerator); err != .None { return err; } if bits < 0 { return .Invalid_Argument; } if bits == 0 { return zero(remainder); } @@ -505,6 +501,161 @@ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { } mod_bits :: proc { int_mod_bits, }; +/* + Multiply by a DIGIT. +*/ +int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { + if err = clear_if_uninitialized(src ); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != .None { return err; } + + if multiplier == 0 { + return zero(dest); + } + if multiplier == 1 { + return copy(dest, src); + } + + /* + Power of two? + */ + if multiplier == 2 { + return double(dest, src); + } + if is_power_of_two(int(multiplier)) { + ix: int; + if ix, err = log_n(multiplier, 2); err != .None { return err; } + return shl(dest, src, ix); + } + + /* + Ensure `dest` is big enough to hold `src` * `multiplier`. + */ + if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } + + /* + Save the original used count. + */ + old_used := dest.used; + /* + Set the sign. + */ + dest.sign = src.sign; + /* + Set up carry. + */ + carry := _WORD(0); + /* + Compute columns. + */ + ix := 0; + for ; ix < src.used; ix += 1 { + /* + Compute product and carry sum for this term + */ + product := carry + _WORD(src.digit[ix]) * _WORD(multiplier); + /* + Mask off higher bits to get a single DIGIT. + */ + dest.digit[ix] = DIGIT(product & _WORD(_MASK)); + /* + Send carry into next iteration + */ + carry = product >> _DIGIT_BITS; + } + + /* + Store final carry [if any] and increment used. + */ + dest.digit[ix] = DIGIT(carry); + dest.used = src.used + 1; + + /* + Zero unused digits. + */ + zero_count := old_used - dest.used; + if zero_count > 0 { + mem.zero_slice(dest.digit[zero_count:]); + } + return clamp(dest); +} + +/* + High level multiplication (handles sign). +*/ +int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(multiplier); err != .None { return err; } + + /* + Early out for `multiplier` is zero; Set `dest` to zero. + */ + if z, _ := is_zero(multiplier); z { + return zero(dest); + } + + min_used := min(src.used, multiplier.used); + max_used := max(src.used, multiplier.used); + digits := src.used + multiplier.used + 1; + neg := src.sign != multiplier.sign; + + if false && src == multiplier { + /* + Do we need to square? + */ + if false && src.used >= _SQR_TOOM_CUTOFF { + /* Use Toom-Cook? */ + // err = s_mp_sqr_toom(a, c); + } else if false && src.used >= _SQR_KARATSUBA_CUTOFF { + /* Karatsuba? */ + // err = s_mp_sqr_karatsuba(a, c); + } else if false && ((src.used * 2) + 1) < _WARRAY && + src.used < (_MAX_COMBA / 2) { + /* Fast comba? */ + // err = s_mp_sqr_comba(a, c); + } else { + // err = s_mp_sqr(a, c); + } + } else { + /* + Can we use the balance method? Check sizes. + * The smaller one needs to be larger than the Karatsuba cut-off. + * The bigger one needs to be at least about one `_MUL_KARATSUBA_CUTOFF` bigger + * to make some sense, but it depends on architecture, OS, position of the + * stars... so YMMV. + * Using it to cut the input into slices small enough for _mul_comba + * was actually slower on the author's machine, but YMMV. + */ + if false && min_used >= _MUL_KARATSUBA_CUTOFF && + max_used / 2 >= _MUL_KARATSUBA_CUTOFF && + /* + Not much effect was observed below a ratio of 1:2, but again: YMMV. + */ + max_used >= 2 * min_used { + // err = s_mp_mul_balance(a,b,c); + } else if false && min_used >= _MUL_TOOM_CUTOFF { + // err = s_mp_mul_toom(a, b, c); + } else if false && min_used >= _MUL_KARATSUBA_CUTOFF { + // err = s_mp_mul_karatsuba(a, b, c); + } else if false && digits < _WARRAY && min_used <= _MAX_COMBA { + /* + Can we use the fast multiplier? + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + // err = s_mp_mul_comba(a, b, c, digs); + } else { + fmt.println("Hai"); + err = _int_mul(dest, src, multiplier, digits); + } + } + dest.sign = .Negative if dest.used > 0 && neg else .Zero_or_Positive; + return err; +} + +mul :: proc { int_mul, int_mul_digit, }; + /* ========================== Low-level routines @@ -688,46 +839,54 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { } } - if err = grow(dest, digits); err != .None { return err; } - dest.used = digits; + /* + Set up temporary output `Int`, which we'll swap for `dest` when done. + */ + + t := &Int{}; + + if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } + t.used = digits; /* + Compute the digits of the product directly. + */ + pa := a.used; + for ix := 0; ix < pa; ix += 1 { + /* + Limit ourselves to `digits` DIGITs of output. + */ + pb := min(b.used, digits - ix); + carry := DIGIT(0); + iy := 0; + /* + Compute the column of the output and propagate the carry. + */ + for iy = 0; iy < pb; iy += 1 { + /* + Compute the column as a _WORD. + */ + column := t.digit[ix + iy] + a.digit[ix] * b.digit[iy] + carry; - /* compute the digits of the product directly */ - pa = a->used; - for (ix = 0; ix < pa; ix++) { - int iy, pb; - mp_digit u = 0; + /* + The new column is the lower part of the result. + */ + t.digit[ix + iy] = column & _MASK; - /* limit ourselves to making digs digits of output */ - pb = MP_MIN(b->used, digs - ix); - - /* compute the columns of the output and propagate the carry */ - for (iy = 0; iy < pb; iy++) { - /* compute the column as a mp_word */ - mp_word r = (mp_word)t.dp[ix + iy] + - ((mp_word)a->dp[ix] * (mp_word)b->dp[iy]) + - (mp_word)u; - - /* the new column is the lower part of the result */ - t.dp[ix + iy] = (mp_digit)(r & (mp_word)MP_MASK); - - /* get the carry word from the result */ - u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + /* + Get the carry word from the result. + */ + carry = column >> _DIGIT_BITS; } - /* set carry if it is placed below digs */ - if ((ix + iy) < digs) { - t.dp[ix + pb] = u; + /* + Set carry if it is placed below digits + */ + if ix + iy < digits { + t.digit[ix + pb] = carry; } } - mp_clamp(&t); - mp_exch(&t, c); - - mp_clear(&t); - return MP_OKAY; -} - -*/ - return .None; + swap(dest, t); + destroy(t); + return clamp(dest); } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 6da1a292c..e6eddf826 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -57,17 +57,15 @@ demo :: proc() { a, b, c := &Int{}, &Int{}, &Int{}; defer destroy(a, b, c); - err = set(a, -512); - err = set(b, 1024); + err = set(a, -1024); + err = set(b, -1024); - print("a", a, 16); - print("b", b, 16); + print("a", a, 10); + print("b", b, 10); - fmt.println("--- swap ---"); - foo(a, b); - - print("a", a, 16); - print("b", b, 16); + fmt.println("--- mul ---"); + mul(c, a, b); + print("c", c, 10); } From c3a4d7dda258d092fab7a101d63f7ffe6009db8e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 23 Jul 2021 21:20:37 +0200 Subject: [PATCH 030/105] big: Fast square method. --- core/math/big/basic.odin | 88 ++++++++++++++++++++++++++++++++------ core/math/big/example.odin | 3 +- core/math/big/helpers.odin | 20 +++++++-- 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 2e04c3c44..fbb533ddf 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -8,12 +8,11 @@ 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`, `sub`, `div`, ... + This file contains basic arithmetic operations like `add`, `sub`, `mul`, `div`, ... */ import "core:mem" import "core:intrinsics" -import "core:fmt" /* =========================== @@ -26,15 +25,9 @@ import "core:fmt" */ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if err = clear_if_uninitialized(a); err != .None { - return err; - } - if err = clear_if_uninitialized(b); err != .None { - return err; - } - if err = clear_if_uninitialized(dest); err != .None { - return err; - } + if err = clear_if_uninitialized(a); err != .None { return err; } + if err = clear_if_uninitialized(b); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != .None { return err; } /* All parameters have been initialized. We can now safely ignore errors from comparison routines. @@ -599,7 +592,7 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { digits := src.used + multiplier.used + 1; neg := src.sign != multiplier.sign; - if false && src == multiplier { + if src == multiplier { /* Do we need to square? */ @@ -614,7 +607,7 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { /* Fast comba? */ // err = s_mp_sqr_comba(a, c); } else { - // err = s_mp_sqr(a, c); + err = _int_sqr(dest, src); } } else { /* @@ -646,7 +639,6 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { */ // err = s_mp_mul_comba(a, b, c, digs); } else { - fmt.println("Hai"); err = _int_mul(dest, src, multiplier, digits); } } @@ -889,4 +881,72 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { swap(dest, t); destroy(t); return clamp(dest); +} + +/* + Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 +*/ +_int_sqr :: proc(dest, src: ^Int) -> (err: Error) { + pa := src.used; + + t := &Int{}; ix, iy: int; + /* + Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. + */ + if err = grow(t, min((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } + t.used = (2 * pa) + 1; + + for ix = 0; ix < pa; ix += 1 { + carry := DIGIT(0); + /* + First calculate the digit at 2*ix; calculate double precision result. + */ + r := _WORD(t.digit[ix+ix]) + _WORD(src.digit[ix] * src.digit[ix]); + + /* + Store lower part in result. + */ + t.digit[ix+ix] = DIGIT(r & _WORD(_MASK)); + + /* + Get the carry. + */ + carry = DIGIT(r >> _DIGIT_BITS); + + for iy = ix + 1; iy < pa; iy += 1 { + /* + First calculate the product. + */ + r = _WORD(src.digit[ix]) * _WORD(src.digit[iy]); + + /* Now calculate the double precision result. Nte we use + * addition instead of *2 since it's easier to optimize + */ + r = _WORD(t.digit[ix+iy]) + r + r + _WORD(carry); + + /* + Store lower part. + */ + t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); + + /* + Get carry. + */ + carry = DIGIT(r >> _DIGIT_BITS); + } + /* + Propagate upwards. + */ + for carry != 0 { + r = _WORD(t.digit[ix+iy]) + _WORD(carry); + t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); + carry = DIGIT(r >> _WORD(_DIGIT_BITS)); + iy += 1; + } + } + + err = clamp(t); + swap(dest, t); + destroy(t); + return err; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index e6eddf826..a9dde60f2 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -64,9 +64,8 @@ demo :: proc() { print("b", b, 10); fmt.println("--- mul ---"); - mul(c, a, b); + mul(c, a, a); print("c", c, 10); - } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index e36f0614b..20193e061 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -57,9 +57,7 @@ set :: proc { int_set_from_integer, int_copy }; Copy one `Int` to another. */ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { - if err = clear_if_uninitialized(src); err != .None { - return err; - } + if err = clear_if_uninitialized(src); err != .None { return err; } /* If dest == src, do nothing */ @@ -535,6 +533,22 @@ clear_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { return .None; } +_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { + digits := digits; + if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != .None { return err; } + /* + If dest == src, do nothing + */ + if (dest == src) { + return .None; + } + + digits = min(digits, len(src.digit), len(dest.digit)); + mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); + return .None; +} + /* Trim unused digits. From d953e40fb30daecb4571e600d64a19a9dd2baac4 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 23 Jul 2021 22:47:44 +0200 Subject: [PATCH 031/105] big: Add `pow`. --- core/math/big/basic.odin | 7 +++++- core/math/big/log.odin | 50 ++++++++++++++++++++++++++++++++++++---- core/math/big/radix.odin | 4 ++-- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index fbb533ddf..18186b38c 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -516,7 +516,7 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { } if is_power_of_two(int(multiplier)) { ix: int; - if ix, err = log_n(multiplier, 2); err != .None { return err; } + if ix, err = log(multiplier, 2); err != .None { return err; } return shl(dest, src, ix); } @@ -648,6 +648,11 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { mul :: proc { int_mul, int_mul_digit, }; +sqr :: proc(dest, src: ^Int) -> (err: Error) { + return mul(dest, src, src); +} + + /* ========================== Low-level routines diff --git a/core/math/big/log.odin b/core/math/big/log.odin index bb918f690..14b4c593b 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -9,7 +9,7 @@ package big 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) { +int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { if base < 2 || DIGIT(base) > _DIGIT_MAX { return -1, .Invalid_Argument; } @@ -35,7 +35,7 @@ log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { Fast path for `Int`s that fit within a single `DIGIT`. */ if a.used == 1 { - return log_n_digit(a.digit[0], DIGIT(base)); + return log(a.digit[0], DIGIT(base)); } // if (MP_HAS(S_MP_LOG)) { @@ -45,7 +45,49 @@ log_n_int :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { return -1, .Unimplemented; } -log_n :: proc{log_n_int, log_n_digit}; +log :: proc { int_log, int_log_digit, }; + +/* + Calculate c = a**b using a square-multiply algorithm. +*/ +int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { + if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(base); err != .None { return err; } + +// if ((err = mp_init_copy(&g, a)) != MP_OKAY) { +// return err; +// } + +// /* set initial result */ +// mp_set(c, 1uL); + +// while (b > 0) { +// /* if the bit is set multiply */ +// if ((b & 1) != 0) { +// if ((err = mp_mul(c, &g, c)) != MP_OKAY) { +// goto LBL_ERR; +// } +// } + +// /* square */ +// if (b > 1) { +// if ((err = mp_sqr(&g, &g)) != MP_OKAY) { +// goto LBL_ERR; +// } +// } + +// /* shift to next bit */ +// b >>= 1; +// } + +// LBL_ERR: +// mp_clear(&g); +// return err; + return err; +} + + +pow :: proc { int_pow, }; /* Returns the log2 of an `Int`, provided `base` is a power of two. @@ -79,7 +121,7 @@ small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { return result; } -log_n_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { +int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { /* If the number is smaller than the base, it fits within a fraction. Therefore, we return 0. diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 8b4abc76d..2dba5d8b4 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -207,7 +207,7 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina shift, count: int; // mask := _WORD(radix - 1); - shift, err = log_n(DIGIT(radix), 2); + shift, err = log(DIGIT(radix), 2); count, err = count_bits(a); digit: _WORD; @@ -263,7 +263,7 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e digit = a.digit, }; - if size, err = log_n(t, DIGIT(radix)); err != .None { + if size, err = log(t, DIGIT(radix)); err != .None { return 0, err; } From 5f63e3952e3345a340cd4c7a1b38f08530277f89 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 24 Jul 2021 09:59:38 +0200 Subject: [PATCH 032/105] big: Correct `pow` bugs from the original. --- core/math/big/common.odin | 3 ++ core/math/big/example.odin | 13 +++-- core/math/big/log.odin | 102 +++++++++++++++++++++++++++---------- 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index fcfdb3973..dd17a678f 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -60,6 +60,9 @@ Error :: enum byte { Buffer_Overflow = 6, Integer_Overflow = 7, + Division_by_Zero = 8, + Math_Domain_Error = 9, + Unimplemented = 127, }; diff --git a/core/math/big/example.odin b/core/math/big/example.odin index a9dde60f2..a6ba77667 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -57,15 +57,14 @@ demo :: proc() { a, b, c := &Int{}, &Int{}, &Int{}; defer destroy(a, b, c); - err = set(a, -1024); - err = set(b, -1024); + for base in -3..=3 { + for power in -3..=3 { + err = pow(a, base, power); + fmt.printf("err: %v | pow(%v, %v) = ", err, base, power); print("", a, 10); + } + } - print("a", a, 10); - print("b", b, 10); - fmt.println("--- mul ---"); - mul(c, a, a); - print("c", c, 10); } main :: proc() { diff --git a/core/math/big/log.odin b/core/math/big/log.odin index 14b4c593b..632973ec8 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -51,43 +51,93 @@ log :: proc { int_log, int_log_digit, }; Calculate c = a**b using a square-multiply algorithm. */ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { - if err = clear_if_uninitialized(dest); err != .None { return err; } + power := power; if err = clear_if_uninitialized(base); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != .None { return err; } + /* + Early outs. + */ + if z, _ := is_zero(base); z { + /* + A zero base is a special case. + */ + if power < 0 { + if err = zero(dest); err != .None { return err; } + return .Math_Domain_Error; + } + if power == 0 { return one(dest); } + if power > 0 { return zero(dest); } -// if ((err = mp_init_copy(&g, a)) != MP_OKAY) { -// return err; -// } + } + if power < 0 { + /* + Fraction, so we'll return zero. + */ + return zero(dest); + } + switch(power) { + case 0: + /* + Any base to the power zero is one. + */ + return one(dest); + case 1: + /* + Any base to the power one is itself. + */ + return copy(dest, base); + case 2: + return sqr(dest, base); + } -// /* set initial result */ -// mp_set(c, 1uL); + g := &Int{}; + if err = copy(g, base); err != .None { return err; } -// while (b > 0) { -// /* if the bit is set multiply */ -// if ((b & 1) != 0) { -// if ((err = mp_mul(c, &g, c)) != MP_OKAY) { -// goto LBL_ERR; -// } -// } + /* + Set initial result. + */ + if err = set(dest, 1); err != .None { return err; } -// /* square */ -// if (b > 1) { -// if ((err = mp_sqr(&g, &g)) != MP_OKAY) { -// goto LBL_ERR; -// } -// } + loop: for power > 0 { + /* + If the bit is set, multiply. + */ + if power & 1 != 0 { + if err = mul(dest, g, dest); err != .None { + break loop; + } + } + /* + Square. + */ + if power > 1 { + if err = sqr(g, g); err != .None { + break loop; + } + } -// /* shift to next bit */ -// b >>= 1; -// } + /* shift to next bit */ + power >>= 1; + } -// LBL_ERR: -// mp_clear(&g); -// return err; + destroy(g); return err; } +/* + Calculate c = a**b. +*/ +int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { + base_t := &Int{}; + defer destroy(base_t); -pow :: proc { int_pow, }; + if err = set(base_t, base); err != .None { return err; } + + return int_pow(dest, base_t, power); +} + +pow :: proc { int_pow, int_pow_int, }; +exp :: pow; /* Returns the log2 of an `Int`, provided `base` is a power of two. From 31c94bd7f83fcb1c2f32e6b168fd6e04cccbcfde Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 24 Jul 2021 13:40:36 +0200 Subject: [PATCH 033/105] big: Finish `log`, fix `sqr`. --- core/math/big/basic.odin | 7 +-- core/math/big/example.odin | 10 +--- core/math/big/helpers.odin | 15 ++++- core/math/big/log.odin | 114 ++++++++++++++++++++++++++++++------- 4 files changed, 113 insertions(+), 33 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 18186b38c..53733fdb0 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -898,7 +898,7 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { /* Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. */ - if err = grow(t, min((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } + if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } t.used = (2 * pa) + 1; for ix = 0; ix < pa; ix += 1 { @@ -906,13 +906,12 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { /* First calculate the digit at 2*ix; calculate double precision result. */ - r := _WORD(t.digit[ix+ix]) + _WORD(src.digit[ix] * src.digit[ix]); + r := _WORD(t.digit[ix+ix]) + (_WORD(src.digit[ix]) * _WORD(src.digit[ix])); /* Store lower part in result. */ t.digit[ix+ix] = DIGIT(r & _WORD(_MASK)); - /* Get the carry. */ @@ -924,7 +923,7 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { */ r = _WORD(src.digit[ix]) * _WORD(src.digit[iy]); - /* Now calculate the double precision result. Nte we use + /* Now calculate the double precision result. Nóte we use * addition instead of *2 since it's easier to optimize */ r = _WORD(t.digit[ix+iy]) + r + r + _WORD(carry); diff --git a/core/math/big/example.odin b/core/math/big/example.odin index a6ba77667..c0de1d7c3 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -57,14 +57,8 @@ demo :: proc() { a, b, c := &Int{}, &Int{}, &Int{}; defer destroy(a, b, c); - for base in -3..=3 { - for power in -3..=3 { - err = pow(a, base, power); - fmt.printf("err: %v | pow(%v, %v) = ", err, base, power); print("", a, 10); - } - } - - + err = set(a, 5125); + print("a", a); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 20193e061..1d756e601 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -75,7 +75,7 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error /* Copy everything over and zero high digits. */ - for v, i in src.digit[:src.used+1] { + for v, i in src.digit[:src.used] { dest.digit[i] = v; } dest.used = src.used; @@ -533,6 +533,19 @@ clear_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { return .None; } +/* + Allocates several `Int`s at once. +*/ +_int_init_multi :: proc(integers: ..^Int) -> (err: Error) { + integers := integers; + for a in &integers { + if err = clear(a); err != .None { return err; } + } + return .None; +} + +_init_multi :: proc { _int_init_multi, }; + _copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { digits := digits; if err = clear_if_uninitialized(src); err != .None { return err; } diff --git a/core/math/big/log.odin b/core/math/big/log.odin index 632973ec8..3bd1f4f25 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -13,36 +13,22 @@ int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { if base < 2 || DIGIT(base) > _DIGIT_MAX { return -1, .Invalid_Argument; } - - if err = clear_if_uninitialized(a); err != .None { - return -1, err; - } - if n, _ := is_neg(a); n { - return -1, .Invalid_Argument; - } - if z, _ := is_zero(a); z { - return -1, .Invalid_Argument; - } + if err = clear_if_uninitialized(a); err != .None { return -1, err; } + if n, _ := is_neg(a); n { return -1, .Invalid_Argument; } + if z, _ := is_zero(a); z { return -1, .Invalid_Argument; } /* Fast path for bases that are a power of two. */ - if is_power_of_two(int(base)) { - return _log_power_of_two(a, base); - } + if is_power_of_two(int(base)) { return _log_power_of_two(a, base); } /* Fast path for `Int`s that fit within a single `DIGIT`. */ - if a.used == 1 { - return log(a.digit[0], DIGIT(base)); - } + if a.used == 1 { return log(a.digit[0], DIGIT(base)); } - // if (MP_HAS(S_MP_LOG)) { - // return s_mp_log(a, (mp_digit)base, c); - // } + return _int_log(a, base); - return -1, .Unimplemented; } log :: proc { int_log, int_log_digit, }; @@ -222,4 +208,92 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { } else { return low, .None; } +} + + +/* + Internal implementation of log. +*/ +_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { + bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + + cnt := 0; + + ic, _ := cmp(a, base); + if ic == -1 || ic == 0 { + return 1 if ic == 0 else 0, .None; + } + + if err = set(bi_base, base); err != .None { return -1, err; } + if err = _init_multi(bracket_mid, t); err != .None { return -1, err; } + if err = one(bracket_low); err != .None { return -1, err; } + if err = set(bracket_high, base); err != .None { return -1, err; } + + low := 0; high := 1; + + /* + A kind of Giant-step/baby-step algorithm. + Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/ + The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped + for small n. + */ + + for { + /* + Iterate until `a` is bracketed between low + high. + */ + if bc, _ := cmp(bracket_high, a); bc != -1 { + break; + } + + low = high; + if err = copy(bracket_low, bracket_high); err != .None { + destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + return -1, err; + } + high <<= 1; + if err = sqr(bracket_high, bracket_high); err != .None { + destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + return -1, err; + } + + cnt += 1; + if cnt == 7 { + destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + return -2, .Max_Iterations_Reached; + } + } + + for (high - low) > 1 { + mid := (high + low) >> 1; + + if err = pow(t, bi_base, mid - low); err != .None { + destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + return -1, err; + } + + if err = mul(bracket_mid, bracket_low, t); err != .None { + destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + return -1, err; + } + mc, _ := cmp(a, bracket_mid); + if mc == -1 { + high = mid; + swap(bracket_mid, bracket_high); + } + if mc == 1 { + low = mid; + swap(bracket_mid, bracket_low); + } + if mc == 0 { + destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + return mid, .None; + } + } + + fc, _ := cmp(bracket_high, a); + res = high if fc == 0 else low; + + destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + return; } \ No newline at end of file From 2884fa55060ba870b217d38f2e3d3129f1108a55 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 24 Jul 2021 19:01:55 +0200 Subject: [PATCH 034/105] big: add div by 3. --- core/math/big/basic.odin | 104 +++++++++++++++++++++++++++++++++++++ core/math/big/example.odin | 21 +++++--- 2 files changed, 119 insertions(+), 6 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 53733fdb0..00411f80a 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -953,4 +953,108 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { swap(dest, t); destroy(t); return err; +} + +/* + Fivide by three (based on routine from MPI and the GMP manual). +*/ +_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { + /* + b = 2**MP_DIGIT_BIT / 3 + */ + b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); + + q := &Int{}; + if err = grow(q, numerator.used); err != .None { return -1, err; } + q.used = numerator.used; + q.sign = numerator.sign; + + w, t: _WORD; + for ix := numerator.used; ix >= 0; ix -= 1 { + w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix]); + if w >= 3 { + /* + Multiply w by [1/3]. + */ + t = (w * b) >> _WORD(_DIGIT_BITS); + + /* + Now subtract 3 * [w/3] from w, to get the remainder. + */ + w -= t+t+t; + + /* + Fixup the remainder as required since the optimization is not exact. + */ + for w >= 3 { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.digit[ix] = DIGIT(t); + } + + remainder = int(w); + + /* + [optional] store the quotient. + */ + if quotient != nil { + err = clamp(q); + swap(q, quotient); + } + destroy(q); + return remainder, .None; +} + +/* + Slower bit-bang division... also smaller. +*/ +_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { + +// mp_int ta, tb, tq, q; +// int n; +// bool neg; +// mp_err err; + +// /* init our temps */ +// if ((err = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { +// return err; +// } + +// mp_set(&tq, 1uL); +// n = mp_count_bits(a) - mp_count_bits(b); +// if ((err = mp_abs(a, &ta)) != MP_OKAY) goto LBL_ERR; +// if ((err = mp_abs(b, &tb)) != MP_OKAY) goto LBL_ERR; +// if ((err = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) goto LBL_ERR; +// if ((err = mp_mul_2d(&tq, n, &tq)) != MP_OKAY) goto LBL_ERR; + +// while (n-- >= 0) { +// if (mp_cmp(&tb, &ta) != MP_GT) { +// if ((err = mp_sub(&ta, &tb, &ta)) != MP_OKAY) goto LBL_ERR; +// if ((err = mp_add(&q, &tq, &q)) != MP_OKAY) goto LBL_ERR; +// } +// if ((err = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) goto LBL_ERR; +// if ((err = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY) goto LBL_ERR; +// } + +// /* now q == quotient and ta == remainder */ + +// neg = (a->sign != b->sign); +// if (c != NULL) { +// mp_exch(c, &q); +// c->sign = ((neg && !mp_iszero(c)) ? MP_NEG : MP_ZPOS); +// } +// if (d != NULL) { +// mp_exch(d, &ta); +// d->sign = (mp_iszero(d) ? MP_ZPOS : a->sign); +// } +// LBL_ERR: +// mp_clear_multi(&ta, &tb, &tq, &q, NULL); +// return err; + + + return .None; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index c0de1d7c3..103259b3e 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -26,6 +26,7 @@ print_configation :: proc() { SQR_KARATSUBA_CUTOFF %v MUL_TOOM_CUTOFF %v SQR_TOOM_CUTOFF %v + `, _DIGIT_BITS, _MIN_DIGIT_COUNT, _MAX_DIGIT_COUNT, @@ -37,8 +38,6 @@ _SQR_KARATSUBA_CUTOFF, _MUL_TOOM_CUTOFF, _SQR_TOOM_CUTOFF, ); - - fmt.println(); } print :: proc(name: string, a: ^Int, base := i8(16)) { @@ -54,11 +53,21 @@ print :: proc(name: string, a: ^Int, base := i8(16)) { demo :: proc() { err: Error; - a, b, c := &Int{}, &Int{}, &Int{}; - defer destroy(a, b, c); + i: int; + destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(destination, source, quotient, remainder, numerator, denominator); - err = set(a, 5125); - print("a", a); + err = set(numerator, 15625); + err = set(denominator, 3); + + print("numerator ", numerator, 10); + print("denominator", denominator, 10); + + i, err = _int_div_3(quotient, numerator); + + print("quotient ", quotient, 10); + fmt.println("remainder ", i); + fmt.println("error", err); } main :: proc() { From c2255c6c19a07eabc4a015aab35b12a3cc31e312 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 25 Jul 2021 13:25:40 +0200 Subject: [PATCH 035/105] big: Add `div`. --- core/math/big/basic.odin | 92 +++++++++++++++++++++++--------------- core/math/big/example.odin | 16 +++++++ 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 00411f80a..bd1d25bb9 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -652,6 +652,24 @@ sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src); } +/* + divmod. + Both the quotient and remainder are optional and may be passed a nil. +*/ +int_div :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { + /* + Early out if neither of the results is wanted. + */ + if quotient == nil && remainder == nil { return .None; } + + + if err = clear_if_uninitialized(numerator); err != .None { return err; } + if err = clear_if_uninitialized(denominator); err != .None { return err; } + + return _int_div(quotient, remainder, numerator, denominator); +} +div :: proc{ int_div, }; + /* ========================== @@ -1014,47 +1032,49 @@ _int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { */ _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { -// mp_int ta, tb, tq, q; -// int n; -// bool neg; -// mp_err err; + ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}; -// /* init our temps */ -// if ((err = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { -// return err; -// } + goto_end: for { + if err = one(tq); err != .None { break goto_end; } -// mp_set(&tq, 1uL); -// n = mp_count_bits(a) - mp_count_bits(b); -// if ((err = mp_abs(a, &ta)) != MP_OKAY) goto LBL_ERR; -// if ((err = mp_abs(b, &tb)) != MP_OKAY) goto LBL_ERR; -// if ((err = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) goto LBL_ERR; -// if ((err = mp_mul_2d(&tq, n, &tq)) != MP_OKAY) goto LBL_ERR; + num_bits, _ := count_bits(numerator); + den_bits, _ := count_bits(denominator); + n := num_bits - den_bits; -// while (n-- >= 0) { -// if (mp_cmp(&tb, &ta) != MP_GT) { -// if ((err = mp_sub(&ta, &tb, &ta)) != MP_OKAY) goto LBL_ERR; -// if ((err = mp_add(&q, &tq, &q)) != MP_OKAY) goto LBL_ERR; -// } -// if ((err = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) goto LBL_ERR; -// if ((err = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY) goto LBL_ERR; -// } + if err = abs(ta, numerator); err != .None { break goto_end; } + if err = abs(tb, denominator); err != .None { break goto_end; } -// /* now q == quotient and ta == remainder */ + if err = shl(tb, tb, n); err != .None { break goto_end; } + if err = shl(tq, tq, n); err != .None { break goto_end; } -// neg = (a->sign != b->sign); -// if (c != NULL) { -// mp_exch(c, &q); -// c->sign = ((neg && !mp_iszero(c)) ? MP_NEG : MP_ZPOS); -// } -// if (d != NULL) { -// mp_exch(d, &ta); -// d->sign = (mp_iszero(d) ? MP_ZPOS : a->sign); -// } -// LBL_ERR: -// mp_clear_multi(&ta, &tb, &tq, &q, NULL); -// return err; + for ; n >= 0; n -= 1 { + c: int; + if c, err = cmp(tb, ta); err != .None { break goto_end; } + if c != 1 { + if err = sub(ta, ta, tb); err != .None { break goto_end; } + if err = add( q, tq, q); err != .None { break goto_end; } + } + if err = shr1(tb, tb); err != .None { break goto_end; } + if err = shr1(tq, tq); err != .None { break goto_end; } + } + /* + Now q == quotient and ta == remainder. + */ + neg := numerator.sign != denominator.sign; + if quotient != nil { + swap(quotient, q); + z, _ := is_zero(quotient); + quotient.sign = .Negative if neg && !z else .Zero_or_Positive; + } + if remainder != nil { + swap(remainder, ta); + z, _ := is_zero(numerator); + remainder.sign = .Zero_or_Positive if z else numerator.sign; + } - return .None; + break goto_end; + } + destroy(ta, tb, tq, q); + return err; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 103259b3e..ca3fc5531 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -68,6 +68,22 @@ demo :: proc() { print("quotient ", quotient, 10); fmt.println("remainder ", i); fmt.println("error", err); + + fmt.println(); fmt.println(); + + err = set (numerator, 15625); + err = set (denominator, 3); + err = zero(quotient); + + print("numerator ", numerator, 10); + print("denominator", denominator, 10); + + err = _int_div_small(quotient, remainder, numerator, denominator); + + print("quotient ", quotient, 10); + print("remainder ", remainder, 10); + + } main :: proc() { From 1ebaa9fb3b64cb6f93bad066a2d0c8e2737dbe5b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 25 Jul 2021 19:32:08 +0200 Subject: [PATCH 036/105] big: `itoa` now works for arbitrary radixes. --- core/math/big/basic.odin | 88 ++++++++++++++++++++++++- core/math/big/example.odin | 59 ++++++++--------- core/math/big/log.odin | 8 --- core/math/big/radix.odin | 127 +++++++++++++++++++++++++------------ 4 files changed, 202 insertions(+), 80 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index bd1d25bb9..0807581b5 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -666,7 +666,7 @@ int_div :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Erro if err = clear_if_uninitialized(numerator); err != .None { return err; } if err = clear_if_uninitialized(denominator); err != .None { return err; } - return _int_div(quotient, remainder, numerator, denominator); + return _int_div_small(quotient, remainder, numerator, denominator); } div :: proc{ int_div, }; @@ -1077,4 +1077,90 @@ _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (er } destroy(ta, tb, tq, q); return err; +} + +/* + Single digit division (based on routine from MPI). +*/ +_int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: int, err: Error) { + q := &Int{}; + ix: int; + + /* + Cannot divide by zero. + */ + if denominator == 0 { + return 0, .Division_by_Zero; + } + + /* + Quick outs. + */ + if denominator == 1 || numerator.used == 0 { + err = .None; + if quotient != nil { + err = copy(quotient, numerator); + } + return 0, err; + } + /* + Power of two? + */ + if denominator == 2 { + if odd, _ := is_odd(numerator); odd { + remainder = 1; + } + if quotient == nil { + return remainder, .None; + } + return remainder, shr(quotient, numerator, 1); + } + + if is_power_of_two(int(denominator)) { + ix = 1; + for ix < _DIGIT_BITS && denominator != (1 << uint(ix)) { + ix += 1; + } + remainder = int(numerator.digit[0]) & ((1 << uint(ix)) - 1); + if quotient == nil { + return remainder, .None; + } + + return remainder, shr(quotient, numerator, int(ix)); + } + + /* + Three? + */ + if denominator == 3 { + return _int_div_3(quotient, numerator); + } + + /* + No easy answer [c'est la vie]. Just division. + */ + if err = grow(q, numerator.used); err != .None { return 0, err; } + + q.used = numerator.used; + q.sign = numerator.sign; + + w := _WORD(0); + + for ix = numerator.used - 1; ix >= 0; ix -= 1 { + t := DIGIT(0); + w = (w << _WORD(_DIGIT_BITS) | _WORD(numerator.digit[ix])); + if w >= _WORD(denominator) { + t = DIGIT(w / _WORD(denominator)); + w -= _WORD(t) * _WORD(denominator); + } + q.digit[ix] = t; + } + remainder = int(w); + + if quotient != nil { + clamp(q); + swap(q, quotient); + } + destroy(q); + return remainder, .None; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index ca3fc5531..6ceeb4d26 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -43,47 +43,44 @@ _SQR_TOOM_CUTOFF, print :: proc(name: string, a: ^Int, base := i8(16)) { as, err := itoa(a, base); defer delete(as); - if err == .None { - cb, _ := count_bits(a); - fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); - } else { - fmt.printf("%v (error: %v): %v\n", name, err, a); + cb, _ := count_bits(a); + fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); + if err != .None { + fmt.printf("%v (error: %v | %v)\n", name, err, a); } + +} + +num_threads :: 16; +global_traces_indexes := [num_threads]u16{}; +@thread_local local_traces_index : ^u16; + +init_thread_tracing :: proc(thread_id: u8) { + + fmt.printf("%p\n", &global_traces_indexes[thread_id]); + fmt.printf("%p\n", local_traces_index); } demo :: proc() { err: Error; - i: int; + destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - err = set(numerator, 15625); - err = set(denominator, 3); - - print("numerator ", numerator, 10); - print("denominator", denominator, 10); - - i, err = _int_div_3(quotient, numerator); - - print("quotient ", quotient, 10); - fmt.println("remainder ", i); - fmt.println("error", err); - - fmt.println(); fmt.println(); - - err = set (numerator, 15625); - err = set (denominator, 3); + err = set (numerator, 2); + err = set (denominator, 3); err = zero(quotient); + err = zero(remainder); - print("numerator ", numerator, 10); - print("denominator", denominator, 10); - - err = _int_div_small(quotient, remainder, numerator, denominator); - - print("quotient ", quotient, 10); - print("remainder ", remainder, 10); - - + err = pow(numerator, numerator, 260); + if err != .None { + fmt.printf("Error: %v\n", err); + } else { + print("numerator ", numerator, 16); + print("denominator", denominator, 10); + print("quotient ", quotient, 10); + print("remainder ", remainder, 10); + } } main :: proc() { diff --git a/core/math/big/log.odin b/core/math/big/log.odin index 3bd1f4f25..42f6ea6e3 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -217,8 +217,6 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - cnt := 0; - ic, _ := cmp(a, base); if ic == -1 || ic == 0 { return 1 if ic == 0 else 0, .None; @@ -256,12 +254,6 @@ _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); return -1, err; } - - cnt += 1; - if cnt == 7 { - destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return -2, .Max_Iterations_Reached; - } } for (high - low) > 1 { diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 2dba5d8b4..300ce12b9 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -12,8 +12,7 @@ package big */ import "core:intrinsics" -import "core:fmt" -import "core:strings" +import "core:mem" /* This version of `itoa` allocates one behalf of the caller. The caller must free the string. @@ -41,12 +40,7 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator Exit if calculating the size returned an error. */ if size, err = radix_size(a, radix, zero_terminate); err != .None { - f := strings.clone(fallback(a), allocator); - if zero_terminate { - c := strings.clone_to_cstring(f); - return string(c), err; - } - return f, err; + return "", err; } /* @@ -58,28 +52,9 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator Write the digits out into the buffer. */ written: int; - if written, err = itoa_raw(a, radix, buffer, size, zero_terminate); err == .None { - return string(buffer[:written]), .None; - } + written, err = itoa_raw(a, radix, buffer, size, zero_terminate); - /* - For now, delete the buffer and fall back to the below on failure. - */ - delete(buffer); - - fallback :: proc(a: ^Int, print_raw := false) -> string { - if print_raw { - return fmt.tprintf("%v", a); - } - sign := "-" if a.sign == .Negative else ""; - if a.used <= 2 { - v := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]); - return fmt.tprintf("%v%v", sign, v); - } else { - return fmt.tprintf("[%2d] %v%v", a.used, sign, a.digit[:a.used]); - } - } - return strings.clone(fallback(a), allocator), .Unimplemented; + return string(buffer[:written]), err; } /* @@ -166,7 +141,15 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina buffer[available] = '-'; } - return len(buffer) - available, .None; + /* + If we overestimated the size, we need to move the buffer left. + */ + written = len(buffer) - available; + if written < size { + diff := size - written; + mem.copy(&buffer[0], &buffer[diff], written); + } + return written, .None; } /* @@ -190,11 +173,17 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina available -= 1; buffer[available] = '-'; } - return len(buffer) - available, .None; + + /* + If we overestimated the size, we need to move the buffer left. + */ + written = len(buffer) - available; + if written < size { + diff := size - written; + mem.copy(&buffer[0], &buffer[diff], written); + } + return written, .None; } - /* - At least 3 DIGITs are in use if we made it this far. - */ /* Fast path for radixes that are a power of two. @@ -225,10 +214,18 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina buffer[available] = '-'; } - return len(buffer) - available, .None; + /* + If we overestimated the size, we need to move the buffer left. + */ + written = len(buffer) - available; + if written < size { + diff := size - written; + mem.copy(&buffer[0], &buffer[diff], written); + } + return written, .None; } - return -1, .Unimplemented; + return _itoa_raw_full(a, radix, buffer, zero_terminate); } itoa :: proc{itoa_string, itoa_raw}; @@ -236,7 +233,7 @@ int_to_string :: itoa; int_to_cstring :: itoa_cstring; /* - We size for `string`, not `cstring`. + We size for `string` by default. */ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, err: Error) { a := a; @@ -270,8 +267,7 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e /* log truncates to zero, so we need to add one more, and one for `-` if negative. */ - n, _ := is_neg(a); - size += 2 if n else 1; + size += 2 if a.sign == .Negative else 1; size += 1 if zero_terminate else 0; return size, .None; } @@ -289,4 +285,55 @@ RADIX_TABLE_REVERSE := [80]u8{ 0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */ 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */ -}; \ No newline at end of file +}; + +/* + Stores a bignum as a ASCII string in a given radix (2..64) + The buffer must be appropriately sized. This routine doesn't check. +*/ +_itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (written: int, err: Error) { + temp, denominator := &Int{}, &Int{}; + + if err = copy(temp, a); err != .None { return 0, err; } + if err = set(denominator, radix); err != .None { return 0, err; } + + available := len(buffer); + if zero_terminate { + available -= 1; + buffer[available] = 0; + } + + if a.sign == .Negative { + temp.sign = .Zero_or_Positive; + } + + remainder: int; + for { + if remainder, err = _int_div_digit(temp, temp, DIGIT(radix)); err != .None { + destroy(temp, denominator); + return len(buffer) - available, err; + } + available -= 1; + buffer[available] = RADIX_TABLE[remainder]; + if temp.used == 0 { + break; + } + } + + if a.sign == .Negative { + available -= 1; + buffer[available] = '-'; + } + + destroy(temp, denominator); + + /* + If we overestimated the size, we need to move the buffer left. + */ + written = len(buffer) - available; + if written < len(buffer) { + diff := len(buffer) - written; + mem.copy(&buffer[0], &buffer[diff], written); + } + return written, .None; +} \ No newline at end of file From 5f7aeb30450cb9ca794ffe45949f77ed67836ecf Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 26 Jul 2021 14:55:03 +0200 Subject: [PATCH 037/105] big: Add `mod` and `addmod`. --- core/math/big/basic.odin | 52 +++++++++++++++++++++++++++++++++++--- core/math/big/example.odin | 10 ++++---- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 0807581b5..fe443db87 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -662,14 +662,60 @@ int_div :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Erro */ if quotient == nil && remainder == nil { return .None; } - if err = clear_if_uninitialized(numerator); err != .None { return err; } if err = clear_if_uninitialized(denominator); err != .None { return err; } - return _int_div_small(quotient, remainder, numerator, denominator); + z: bool; + if z, err = is_zero(denominator); z { return .Division_by_Zero; } + + /* + If numerator < denominator then quotient = 0, remainder = numerator. + */ + c: int; + if c, err = cmp_mag(numerator, denominator); c == -1 { + if remainder != nil { + if err = copy(remainder, numerator); err != .None { return err; } + } + if quotient != nil { + zero(quotient); + } + return .None; + } + + if false && (denominator.used > 2 * _MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) { + // err = _int_div_recursive(quotient, remainder, numerator, denominator); + } else if false { + // err = _int_div_school(quotient, remainder, numerator, denominator); + } else { + err = _int_div_small(quotient, remainder, numerator, denominator); + } + + return err; } div :: proc{ int_div, }; +/* + remainder = numerator % denominator. + 0 <= remainder < denominator if denominator > 0 + denominator < remainder <= 0 if denominator < 0 +*/ +int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { + if err = div(nil, remainder, numerator, denominator); err != .None { return err; } + + z: bool; + if z, err = is_zero(remainder); z || denominator.sign == remainder.sign { return .None; } + return add(remainder, remainder, numerator); +} +mod :: proc { int_mod, }; + +/* + remainder = (number + addend) % modulus. +*/ +int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { + if err = add(remainder, number, addend); err != .None { return err; } + return mod(remainder, remainder, modulus); +} +addmod :: proc { int_addmod, }; /* ========================== @@ -974,7 +1020,7 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { } /* - Fivide by three (based on routine from MPI and the GMP manual). + Divide by three (based on routine from MPI and the GMP manual). */ _int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { /* diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 6ceeb4d26..5a7976a88 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -67,16 +67,16 @@ demo :: proc() { destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - err = set (numerator, 2); - err = set (denominator, 3); - err = zero(quotient); + err = set (numerator, 3); + err = set (denominator, 2); + err = set (quotient, 3); err = zero(remainder); - err = pow(numerator, numerator, 260); + err = addmod(remainder, numerator, denominator, quotient); if err != .None { fmt.printf("Error: %v\n", err); } else { - print("numerator ", numerator, 16); + print("numerator ", numerator, 10); print("denominator", denominator, 10); print("quotient ", quotient, 10); print("remainder ", remainder, 10); From 9646d1f2b842dd22893b3a70eb9c8aef9b1ffa75 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 26 Jul 2021 15:07:58 +0200 Subject: [PATCH 038/105] big: Add `submod`, `mulmod`, `sqrmod`. --- core/math/big/basic.odin | 38 +++++++++++++++++++++++++++++++++++--- core/math/big/example.odin | 4 ++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index fe443db87..52992e15f 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -656,7 +656,7 @@ sqr :: proc(dest, src: ^Int) -> (err: Error) { divmod. Both the quotient and remainder are optional and may be passed a nil. */ -int_div :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { +int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { /* Early out if neither of the results is wanted. */ @@ -692,7 +692,12 @@ int_div :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Erro return err; } -div :: proc{ int_div, }; +divmod :: proc{ int_divmod, }; + +int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { + return int_divmod(quotient, nil, numerator, denominator); +} +div :: proc { int_div, }; /* remainder = numerator % denominator. @@ -700,7 +705,7 @@ div :: proc{ int_div, }; denominator < remainder <= 0 if denominator < 0 */ int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { - if err = div(nil, remainder, numerator, denominator); err != .None { return err; } + if err = divmod(nil, remainder, numerator, denominator); err != .None { return err; } z: bool; if z, err = is_zero(remainder); z || denominator.sign == remainder.sign { return .None; } @@ -717,6 +722,33 @@ int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { } addmod :: proc { int_addmod, }; +/* + remainder = (number - decrease) % modulus. +*/ +int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { + if err = add(remainder, number, decrease); err != .None { return err; } + return mod(remainder, remainder, modulus); +} +submod :: proc { int_submod, }; + +/* + remainder = (number * multiplicand) % modulus. +*/ +int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { + if err = mul(remainder, number, multiplicand); err != .None { return err; } + return mod(remainder, remainder, modulus); +} +mulmod :: proc { int_mulmod, }; + +/* + remainder = (number * number) % modulus. +*/ +int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { + if err = sqr(remainder, number); err != .None { return err; } + return mod(remainder, remainder, modulus); +} +sqrmod :: proc { int_sqrmod, }; + /* ========================== Low-level routines diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 5a7976a88..ceb047f74 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -69,10 +69,10 @@ demo :: proc() { err = set (numerator, 3); err = set (denominator, 2); - err = set (quotient, 3); + err = set (quotient, 5); err = zero(remainder); - err = addmod(remainder, numerator, denominator, quotient); + err = mulmod(remainder, numerator, denominator, quotient); if err != .None { fmt.printf("Error: %v\n", err); } else { From 0a431eef19ac47ce39afdd9e9840771be7f111e9 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 26 Jul 2021 16:32:59 +0200 Subject: [PATCH 039/105] big: Add another way to estimate radix size. --- core/math/big/helpers.odin | 1 - core/math/big/log.odin | 1 - core/math/big/radix.odin | 75 +++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 1d756e601..6d7ef97aa 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -37,7 +37,6 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : if err = clear_if_uninitialized(dest); err != .None { return err; } - dest.used = 0; dest.sign = .Zero_or_Positive if src >= 0 else .Negative; src = abs(src); diff --git a/core/math/big/log.odin b/core/math/big/log.odin index 42f6ea6e3..777cc1ea2 100644 --- a/core/math/big/log.odin +++ b/core/math/big/log.odin @@ -210,7 +210,6 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { } } - /* Internal implementation of log. */ diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 300ce12b9..afe02d1c1 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -232,6 +232,8 @@ itoa :: proc{itoa_string, itoa_raw}; int_to_string :: itoa; int_to_cstring :: itoa_cstring; + + /* We size for `string` by default. */ @@ -251,17 +253,39 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e return 1, .None; } - /* - Calculate `log` on a temporary "copy" with its sign set to positive. - */ - t := &Int{ - used = a.used, - sign = .Zero_or_Positive, - digit = a.digit, - }; + if pot, _ := is_power_of_two(a); pot { + /* + Calculate `log` on a temporary "copy" with its sign set to positive. + */ + t := &Int{ + used = a.used, + sign = .Zero_or_Positive, + digit = a.digit, + }; - if size, err = log(t, DIGIT(radix)); err != .None { - return 0, err; + if size, err = log(t, DIGIT(radix)); err != .None { + return 0, err; + } + } else { + la, k := &Int{}, &Int{}; + defer destroy(la, k); + + /* la = floor(log_2(a)) + 1 */ + bit_count, _ := count_bits(a); + err = set(la, bit_count); + + /* k = floor(2^29/log_2(radix)) + 1 */ + lb := _log_bases; + err = set(k, lb[radix]); + + /* n = floor((la * k) / 2^29) + 1 */ + if err = mul(k, la, k); err != .None { return 0, err; } + if err = shr(k, k, _RADIX_SIZE_SCALE); err != .None { return 0, err; } + + /* The "+1" here is the "+1" in "floor((la * k) / 2^29) + 1" */ + /* n = n + 1 + EOS + sign */ + size_, _ := get(k, u128); + size = int(size_); } /* @@ -272,6 +296,37 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e return size, .None; } +/* + Overestimate the size needed for the bigint to string conversion by a very small amount. + The error is about 10^-8; it will overestimate the result by at most 11 elements for + a number of the size 2^(2^31)-1 which is currently the largest possible in this library. + Some short tests gave no results larger than 5 (plus 2 for sign and EOS). + */ + +/* + Table of {0, INT(log_2([1..64])*2^p)+1 } where p is the scale + factor defined in MP_RADIX_SIZE_SCALE and INT() extracts the integer part (truncating). + Good for 32 bit "int". Set MP_RADIX_SIZE_SCALE = 61 and recompute values + for 64 bit "int". + */ + +_RADIX_SIZE_SCALE :: 29; +_log_bases :: [65]u32{ + 0, 0, 0x20000001, 0x14309399, 0x10000001, + 0xdc81a35, 0xc611924, 0xb660c9e, 0xaaaaaab, 0xa1849cd, + 0x9a209a9, 0x94004e1, 0x8ed19c2, 0x8a5ca7d, 0x867a000, + 0x830cee3, 0x8000001, 0x7d42d60, 0x7ac8b32, 0x7887847, + 0x7677349, 0x749131f, 0x72d0163, 0x712f657, 0x6fab5db, + 0x6e40d1b, 0x6ced0d0, 0x6badbde, 0x6a80e3b, 0x6964c19, + 0x6857d31, 0x6758c38, 0x6666667, 0x657fb21, 0x64a3b9f, + 0x63d1ab4, 0x6308c92, 0x624869e, 0x618ff47, 0x60dedea, + 0x6034ab0, 0x5f90e7b, 0x5ef32cb, 0x5e5b1b2, 0x5dc85c3, + 0x5d3aa02, 0x5cb19d9, 0x5c2d10f, 0x5bacbbf, 0x5b3064f, + 0x5ab7d68, 0x5a42df0, 0x59d1506, 0x5962ffe, 0x58f7c57, + 0x588f7bc, 0x582a000, 0x57c7319, 0x5766f1d, 0x5709243, + 0x56adad9, 0x565474d, 0x55fd61f, 0x55a85e8, 0x5555556, +}; + /* Characters used in radix conversions. */ From 9c2468ecf71180c854f6d2bb5a0a46599f1e8b31 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 26 Jul 2021 21:32:29 +0200 Subject: [PATCH 040/105] big: Add `atoi`. --- core/math/big/basic.odin | 15 +++--- core/math/big/example.odin | 26 +++++++--- core/math/big/radix.odin | 104 +++++++++++++++++++++++++++++++++---- 3 files changed, 122 insertions(+), 23 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 52992e15f..5e0f47d59 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -13,7 +13,6 @@ package big import "core:mem" import "core:intrinsics" - /* =========================== User-level routines @@ -68,11 +67,10 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* Grow destination as required. */ - if dest != a { - if err = grow(dest, a.used + 1); err != .None { - return err; - } + if err = grow(dest, a.used + 1); err != .None { + return err; } + /* All parameters have been initialized. We can now safely ignore errors from comparison routines. @@ -87,14 +85,16 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ if p, _ := is_pos(dest); p && (dest.digit[0] + digit < _DIGIT_MAX) { dest.digit[0] += digit; - return .None; + dest.used += 1; + return clamp(dest); } /* Can be subtracted from dest.digit[0] without underflow. */ if n, _ := is_neg(a); n && (dest.digit[0] > digit) { dest.digit[0] -= digit; - return .None; + dest.used += 1; + return clamp(dest); } } @@ -561,7 +561,6 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { */ dest.digit[ix] = DIGIT(carry); dest.used = src.used + 1; - /* Zero unused digits. */ diff --git a/core/math/big/example.odin b/core/math/big/example.odin index ceb047f74..16b2da262 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -40,7 +40,7 @@ _SQR_TOOM_CUTOFF, ); } -print :: proc(name: string, a: ^Int, base := i8(16)) { +print :: proc(name: string, a: ^Int, base := i8(10)) { as, err := itoa(a, base); defer delete(as); cb, _ := count_bits(a); @@ -67,12 +67,11 @@ demo :: proc() { destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - err = set (numerator, 3); - err = set (denominator, 2); - err = set (quotient, 5); + err = set (numerator, 2); + err = set (denominator, 1); + err = set (quotient, u128(1 << 120)); err = zero(remainder); - - err = mulmod(remainder, numerator, denominator, quotient); + err = pow(remainder, numerator, 120); if err != .None { fmt.printf("Error: %v\n", err); } else { @@ -81,6 +80,21 @@ demo :: proc() { print("quotient ", quotient, 10); print("remainder ", remainder, 10); } + if c, _ := cmp(quotient, remainder); c == 0 { + fmt.println("c == r"); + } else { + fmt.println("c != r"); + } + + foozle := "-1329227995784915872903807060280344576"; + err = atoi(destination, foozle, 10); + if err != .None { + fmt.printf("Error %v while parsing `%v`", err, foozle); + } else { + print("destination", destination); + err = add(remainder, remainder, destination); + print("remainder + destination", remainder); + } } main :: proc() { diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index afe02d1c1..7548ace4b 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -17,7 +17,7 @@ import "core:mem" /* 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) { +int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { a := a; radix := radix; if err = clear_if_uninitialized(a); err != .None { return "", err; @@ -52,7 +52,7 @@ itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator Write the digits out into the buffer. */ written: int; - written, err = itoa_raw(a, radix, buffer, size, zero_terminate); + written, err = int_itoa_raw(a, radix, buffer, size, zero_terminate); return string(buffer[:written]), err; } @@ -60,7 +60,7 @@ 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) { +int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) { a := a; radix := radix; if err = clear_if_uninitialized(a); err != .None { return "", err; @@ -71,7 +71,7 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) - radix = radix if radix > 0 else 10; s: string; - s, err = itoa_string(a, radix, true, allocator); + s, err = int_itoa_string(a, radix, true, allocator); return cstring(raw_data(s)), err; } @@ -95,7 +95,7 @@ itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) - it'll result in buffer overflows, as we use it to avoid reversing at the end 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) { +int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) { a := a; radix := radix; size := size; if err = clear_if_uninitialized(a); err != .None { return 0, err; @@ -228,11 +228,96 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_termina return _itoa_raw_full(a, radix, buffer, zero_terminate); } -itoa :: proc{itoa_string, itoa_raw}; -int_to_string :: itoa; -int_to_cstring :: itoa_cstring; +itoa :: proc{int_itoa_string, int_itoa_raw}; +int_to_string :: int_itoa_string; +int_to_cstring :: int_itoa_cstring; + +/* + Read a string [ASCII] in a given radix. +*/ +int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { + input := input; + /* + Make sure the radix is ok. + */ + if radix < 2 || radix > 64 { + return .Invalid_Argument; + } + + /* + Set the integer to the default of zero. + */ + if err = zero(res); err != .None { return err; } + + /* + We'll interpret an empty string as zero. + */ + if len(input) == 0 { + return .None; + } + + /* + If the leading digit is a minus set the sign to negative. + Given the above early out, the length should be at least 1. + */ + sign := Sign.Zero_or_Positive; + if input[0] == '-' { + input = input[1:]; + sign = .Negative; + } + + /* + Process each digit of the string. + */ + ch: rune; + for len(input) > 0 { + /* if the radix <= 36 the conversion is case insensitive + * this allows numbers like 1AB and 1ab to represent the same value + * [e.g. in hex] + */ + + ch = rune(input[0]); + if radix <= 36 && ch >= 'a' && ch <= 'z' { + ch += 'a' - 'A'; + } + + pos := ch - '+'; + if RADIX_TABLE_REVERSE_SIZE <= pos { + break; + } + y := RADIX_TABLE_REVERSE[pos]; + /* if the char was found in the map + * and is less than the given radix add it + * to the number, otherwise exit the loop. + */ + if y >= u8(radix) { + break; + } + + if err = mul(res, res, DIGIT(radix)); err != .None { return err; } + if err = add(res, res, DIGIT(y)); err != .None { return err; } + + input = input[1:]; + } + + /* + If an illegal character was found, fail. + */ + if len(input) > 0 && ch != 0 && ch != '\r' && ch != '\n' { + return .Invalid_Argument; + } + /* + Set the sign only if res != 0. + */ + if res.used > 0 { + res.sign = sign; + } + + return .None; +} +atoi :: proc { int_atoi, }; /* We size for `string` by default. @@ -331,7 +416,7 @@ _log_bases :: [65]u32{ Characters used in radix conversions. */ RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; -RADIX_TABLE_REVERSE := [80]u8{ +RADIX_TABLE_REVERSE := [RADIX_TABLE_REVERSE_SIZE]u8{ 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */ 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */ 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */ @@ -341,6 +426,7 @@ RADIX_TABLE_REVERSE := [80]u8{ 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */ }; +RADIX_TABLE_REVERSE_SIZE :: 80; /* Stores a bignum as a ASCII string in a given radix (2..64) From 2aae1016ab31a89c185639f24a095133a7bef0a2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 27 Jul 2021 16:41:20 +0200 Subject: [PATCH 041/105] big: Add `sqrt`. --- core/math/big/basic.odin | 45 ++++++++++++++++++++++++++++++++++ core/math/big/example.odin | 49 ++++++++++---------------------------- core/math/big/logical.odin | 18 ++++++++++++++ core/math/big/radix.odin | 2 +- 4 files changed, 77 insertions(+), 37 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 5e0f47d59..567bc05d8 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -13,6 +13,7 @@ package big import "core:mem" import "core:intrinsics" + /* =========================== User-level routines @@ -748,6 +749,50 @@ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { } sqrmod :: proc { int_sqrmod, }; +/* + This function is less generic than `nth_root`, simpler and faster. +*/ +int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src); err != .None { return err; } + + /* Must be positive. */ + if src.sign == .Negative { return .Invalid_Argument; } + + /* Easy out. If src is zero, so is dest. */ + if z, _ := is_zero(src); z { return zero(dest); } + + /* Set up temporaries. */ + t1, t2 := &Int{}, &Int{}; + defer destroy(t1, t2); + + if err = copy(t1, src); err != .None { return err; } + if err = zero(t2); err != .None { return err; } + + /* First approximation. Not very bad for large arguments. */ + if err = shr_digit(t1, t1.used / 2); err != .None { return err; } + /* t1 > 0 */ + if err = div(t2, src, t1); err != .None { return err; } + if err = add(t1, t1, t2); err != .None { return err; } + if err = shr(t1, t1, 1); err != .None { return err; } + + /* And now t1 > sqrt(arg). */ + for { + if err = div(t2, src, t1); err != .None { return err; } + if err = add(t1, t1, t2); err != .None { return err; } + if err = shr(t1, t1, 1); err != .None { return err; } + /* t1 >= sqrt(arg) >= t2 at this point */ + + cm, _ := cmp_mag(t1, t2); + if cm != 1 { break; } + } + + swap(dest, t1); + return err; +} + +sqrt :: proc { int_sqrt, }; + /* ========================== Low-level routines diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 16b2da262..7f64967de 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -51,15 +51,7 @@ print :: proc(name: string, a: ^Int, base := i8(10)) { } -num_threads :: 16; -global_traces_indexes := [num_threads]u16{}; -@thread_local local_traces_index : ^u16; - -init_thread_tracing :: proc(thread_id: u8) { - - fmt.printf("%p\n", &global_traces_indexes[thread_id]); - fmt.printf("%p\n", local_traces_index); -} +@thread_local string_buffer: [1024]u8; demo :: proc() { err: Error; @@ -67,34 +59,19 @@ demo :: proc() { destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - err = set (numerator, 2); - err = set (denominator, 1); - err = set (quotient, u128(1 << 120)); - err = zero(remainder); - err = pow(remainder, numerator, 120); - if err != .None { - fmt.printf("Error: %v\n", err); - } else { - print("numerator ", numerator, 10); - print("denominator", denominator, 10); - print("quotient ", quotient, 10); - print("remainder ", remainder, 10); - } - if c, _ := cmp(quotient, remainder); c == 0 { - fmt.println("c == r"); - } else { - fmt.println("c != r"); - } + // string_buffer := make([]u8, 1024); + // defer delete(string_buffer); - foozle := "-1329227995784915872903807060280344576"; - err = atoi(destination, foozle, 10); - if err != .None { - fmt.printf("Error %v while parsing `%v`", err, foozle); - } else { - print("destination", destination); - err = add(remainder, remainder, destination); - print("remainder + destination", remainder); - } + err = set (numerator, 1024); + err = int_sqrt(destination, numerator); + fmt.printf("int_sqrt returned: %v\n", err); + + print("destination", destination); + // print("source ", source); + // print("quotient ", quotient); + // print("remainder ", remainder); + print("numerator ", numerator); + // print("denominator", denominator); } main :: proc() { diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 53e296dc1..9b6654a5c 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -348,6 +348,24 @@ int_shr_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { } shr_digit :: proc { int_shr_digit, }; +/* + Shift right by a certain bit count with sign extension. +*/ +int_shr_signed :: proc(dest, src: ^Int, bits: int) -> (err: Error) { + if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != .None { return err; } + + if src.sign == .Zero_or_Positive { + return shr(dest, src, bits); + } + if err = add(dest, src, DIGIT(1)); err != .None { return err; } + + if err = shr(dest, dest, bits); err != .None { return err; } + return sub(dest, src, DIGIT(1)); +} + +shr_signed :: proc { int_shr_signed, }; + /* Shift left by a certain bit count. */ diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 7548ace4b..d64956ba8 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -46,7 +46,7 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc /* Allocate the buffer we need. */ - buffer := make([]u8, size); + buffer := make([]u8, size, allocator); /* Write the digits out into the buffer. From 531c4936dd2ccf026748cc390094c9b51691a534 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 27 Jul 2021 20:33:32 +0200 Subject: [PATCH 042/105] big: Add `root_n`. --- core/math/big/basic.odin | 44 ------ core/math/big/example.odin | 18 ++- core/math/big/{log.odin => exp_log.odin} | 184 +++++++++++++++++++++++ 3 files changed, 195 insertions(+), 51 deletions(-) rename core/math/big/{log.odin => exp_log.odin} (54%) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 567bc05d8..ef069de17 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -749,50 +749,6 @@ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { } sqrmod :: proc { int_sqrmod, }; -/* - This function is less generic than `nth_root`, simpler and faster. -*/ -int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(dest); err != .None { return err; } - if err = clear_if_uninitialized(src); err != .None { return err; } - - /* Must be positive. */ - if src.sign == .Negative { return .Invalid_Argument; } - - /* Easy out. If src is zero, so is dest. */ - if z, _ := is_zero(src); z { return zero(dest); } - - /* Set up temporaries. */ - t1, t2 := &Int{}, &Int{}; - defer destroy(t1, t2); - - if err = copy(t1, src); err != .None { return err; } - if err = zero(t2); err != .None { return err; } - - /* First approximation. Not very bad for large arguments. */ - if err = shr_digit(t1, t1.used / 2); err != .None { return err; } - /* t1 > 0 */ - if err = div(t2, src, t1); err != .None { return err; } - if err = add(t1, t1, t2); err != .None { return err; } - if err = shr(t1, t1, 1); err != .None { return err; } - - /* And now t1 > sqrt(arg). */ - for { - if err = div(t2, src, t1); err != .None { return err; } - if err = add(t1, t1, t2); err != .None { return err; } - if err = shr(t1, t1, 1); err != .None { return err; } - /* t1 >= sqrt(arg) >= t2 at this point */ - - cm, _ := cmp_mag(t1, t2); - if cm != 1 { break; } - } - - swap(dest, t1); - return err; -} - -sqrt :: proc { int_sqrt, }; - /* ========================== Low-level routines diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 7f64967de..7b0df89ae 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -63,15 +63,19 @@ demo :: proc() { // defer delete(string_buffer); err = set (numerator, 1024); - err = int_sqrt(destination, numerator); + err = sqrt(destination, numerator); fmt.printf("int_sqrt returned: %v\n", err); - print("destination", destination); - // print("source ", source); - // print("quotient ", quotient); - // print("remainder ", remainder); - print("numerator ", numerator); - // print("denominator", denominator); + print("num ", numerator); + print("sqrt(num)", destination); + + fmt.println("\n\n"); + + err = root_n(destination, numerator, 2); + fmt.printf("root_n(2) returned: %v\n", err); + + print("num ", numerator); + print("root_n(num)", destination); } main :: proc() { diff --git a/core/math/big/log.odin b/core/math/big/exp_log.odin similarity index 54% rename from core/math/big/log.odin rename to core/math/big/exp_log.odin index 777cc1ea2..2ccfa7146 100644 --- a/core/math/big/log.odin +++ b/core/math/big/exp_log.odin @@ -210,6 +210,190 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { } } +/* + This function is less generic than `root_n`, simpler and faster. +*/ +int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src); err != .None { return err; } + + /* Must be positive. */ + if src.sign == .Negative { return .Invalid_Argument; } + + /* Easy out. If src is zero, so is dest. */ + if z, _ := is_zero(src); z { return zero(dest); } + + /* Set up temporaries. */ + t1, t2 := &Int{}, &Int{}; + defer destroy(t1, t2); + + if err = copy(t1, src); err != .None { return err; } + if err = zero(t2); err != .None { return err; } + + /* First approximation. Not very bad for large arguments. */ + if err = shr_digit(t1, t1.used / 2); err != .None { return err; } + /* t1 > 0 */ + if err = div(t2, src, t1); err != .None { return err; } + if err = add(t1, t1, t2); err != .None { return err; } + if err = shr(t1, t1, 1); err != .None { return err; } + + /* And now t1 > sqrt(arg). */ + for { + if err = div(t2, src, t1); err != .None { return err; } + if err = add(t1, t1, t2); err != .None { return err; } + if err = shr(t1, t1, 1); err != .None { return err; } + /* t1 >= sqrt(arg) >= t2 at this point */ + + cm, _ := cmp_mag(t1, t2); + if cm != 1 { break; } + } + + swap(dest, t1); + return err; +} +sqrt :: proc { int_sqrt, }; + + +/* + Find the nth root of an Integer. + Result found such that `(dest)**n <= src` and `(dest+1)**n > src` + + This algorithm uses Newton's approximation `x[i+1] = x[i] - f(x[i])/f'(x[i])`, + which will find the root in `log(n)` time where each step involves a fair bit. +*/ +int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { + /* Fast path for n == 2 */ + if n == 2 { return sqrt(dest, src); } + + /* Initialize dest + src if needed. */ + if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src); err != .None { return err; } + + if n < 0 || n > int(_DIGIT_MAX) { + return .Invalid_Argument; + } + + neg: bool; + if n & 1 == 0 { + if neg, err = is_neg(src); neg || err != .None { return .Invalid_Argument; } + } + + /* Set up temporaries. */ + t1, t2, t3, a := &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(t1, t2, t3); + + /* If a is negative fudge the sign but keep track. */ + a.sign = .Zero_or_Positive; + a.used = src.used; + a.digit = src.digit; + + /* + If "n" is larger than INT_MAX it is also larger than + log_2(src) because the bit-length of the "src" is measured + with an int and hence the root is always < 2 (two). + */ + if n > max(int) / 2 { + err = set(dest, 1); + dest.sign = a.sign; + return err; + } + + /* Compute seed: 2^(log_2(src)/n + 2) */ + ilog2: int; + ilog2, err = count_bits(src); + + /* "src" is smaller than max(int), we can cast safely. */ + if ilog2 < n { + err = set(dest, 1); + dest.sign = a.sign; + return err; + } + + ilog2 /= n; + if ilog2 == 0 { + err = set(dest, 1); + dest.sign = a.sign; + return err; + } + + /* Start value must be larger than root. */ + ilog2 += 2; + if err = power_of_two(t2, ilog2); err != .None { return err; } + + c: int; + for { + /* t1 = t2 */ + if err = copy(t1, t2); err != .None { return err; } + + /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ + + /* t3 = t1**(b-1) */ + if err = pow(t3, t1, n-1); err != .None { return err; } + + /* numerator */ + /* t2 = t1**b */ + if err = mul(t2, t1, t3); err != .None { return err; } + + /* t2 = t1**b - a */ + if err = sub(t2, t2, a); err != .None { return err; } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if err = mul(t3, t3, DIGIT(n)); err != .None { return err; } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if err = div(t3, t2, t3); err != .None { return err; } + if err = sub(t2, t1, t3); err != .None { return err; } + + /* + Number of rounds is at most log_2(root). If it is more it + got stuck, so break out of the loop and do the rest manually. + */ + if ilog2 -= 1; ilog2 == 0 { + break; + } + if c, err = cmp(t1, t2); c == 0 { break; } + } + + /* Result can be off by a few so check. */ + /* Loop beneath can overshoot by one if found root is smaller than actual root. */ + + for { + if err = pow(t2, t1, n); err != .None { return err; } + + c, err = cmp(t2, a); + if c == 0 { + swap(dest, t1); + return .None; + } else if c == -1 { + if err = add(t1, t1, DIGIT(1)); err != .None { return err; } + } else { + break; + } + } + + /* Correct overshoot from above or from recurrence. */ + for { + if err = pow(t2, t1, n); err != .None { return err; } + + c, err = cmp(t2, a); + if c == 1 { + if err = sub(t1, t1, DIGIT(1)); err != .None { return err; } + } else { + break; + } + } + + /* Set the result. */ + swap(dest, t1); + + /* set the sign of the result */ + dest.sign = src.sign; + + return err; +} +root_n :: proc { int_root_n, }; + /* Internal implementation of log. */ From 9c150381bf7ec94557c14b8cc477e9e30312986c Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 00:58:28 +0200 Subject: [PATCH 043/105] big: Add `rand`. --- core/math/big/example.odin | 26 ++++++++-------------- core/math/big/exp_log.odin | 2 +- core/math/big/helpers.odin | 44 ++++++++++++++++++++++++++++++++++++-- core/math/rand/rand.odin | 2 +- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 7b0df89ae..c95fde549 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -59,23 +59,15 @@ demo :: proc() { destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - // string_buffer := make([]u8, 1024); - // defer delete(string_buffer); - - err = set (numerator, 1024); - err = sqrt(destination, numerator); - fmt.printf("int_sqrt returned: %v\n", err); - - print("num ", numerator); - print("sqrt(num)", destination); - - fmt.println("\n\n"); - - err = root_n(destination, numerator, 2); - fmt.printf("root_n(2) returned: %v\n", err); - - print("num ", numerator); - print("root_n(num)", destination); + for i in 1..=10 { + err = rand(destination, 1200); // 1200 random bits + if err != .None { + fmt.printf("rand error: %v\n", err); + } else { + fmt.printf("#%3d: ", i); + print("", destination); + } + } } main :: proc() { diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index 2ccfa7146..943fd51e9 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -406,7 +406,7 @@ _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { } if err = set(bi_base, base); err != .None { return -1, err; } - if err = _init_multi(bracket_mid, t); err != .None { return -1, err; } + if err = init_multi(bracket_mid, t); err != .None { return -1, err; } if err = one(bracket_low); err != .None { return -1, err; } if err = set(bracket_high, base); err != .None { return -1, err; } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 6d7ef97aa..a15929d2e 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" +import "core:math/rand" /* Deallocates the backing memory of one or more `Int`s. @@ -506,6 +507,43 @@ count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { return count, .None; } +int_random_digit :: proc(r: ^rand.Rand = nil) -> (res: DIGIT) { + when _DIGIT_BITS == 60 { // DIGIT = u64 + return DIGIT(rand.uint64(r)) & _MASK; + } else when _DIGIT_BITS == 28 { // DIGIT = u32 + return DIGIT(rand.uint32(r)) & _MASK; + } else { + panic("Unsupported DIGIT size."); + } + + return 0; // We shouldn't get here. +} + +int_rand :: proc(dest: ^Int, bits: int, r: ^rand.Rand = nil) -> (err: Error) { + bits := bits; + + if bits <= 0 { return .Invalid_Argument; } + + digits := bits / _DIGIT_BITS; + bits %= _DIGIT_BITS; + + if bits > 0 { + digits += 1; + } + + if err = grow(dest, digits); err != .None { return err; } + + for i := 0; i < digits; i += 1 { + dest.digit[i] = int_random_digit(r) & _MASK; + } + if bits > 0 { + dest.digit[digits - 1] &= ((1 << uint(bits)) - 1); + } + dest.used = digits; + return .None; +} +rand :: proc { int_rand, }; + /* Internal helpers. */ @@ -532,10 +570,12 @@ clear_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { return .None; } + + /* Allocates several `Int`s at once. */ -_int_init_multi :: proc(integers: ..^Int) -> (err: Error) { +int_init_multi :: proc(integers: ..^Int) -> (err: Error) { integers := integers; for a in &integers { if err = clear(a); err != .None { return err; } @@ -543,7 +583,7 @@ _int_init_multi :: proc(integers: ..^Int) -> (err: Error) { return .None; } -_init_multi :: proc { _int_init_multi, }; +init_multi :: proc { int_init_multi, }; _copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { digits := digits; diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index f5558bb8c..812cdc53d 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -95,7 +95,7 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 { int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 { if n <= 0 { - panic("Invalid argument to int63_max"); + panic("Invalid argument to int127_max"); } if n&(n-1) == 0 { return int127(r) & (n-1); From 2fbff25a18672597a6c6b4a73733f649ccdc8648 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 15:23:48 +0200 Subject: [PATCH 044/105] big: Improve `int_bitfield_extract`. --- core/math/big/example.odin | 48 ++++++++++++++++++++++++++------ core/math/big/helpers.odin | 57 +++++++++++++++++++++++++------------- core/math/big/radix.odin | 2 +- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index c95fde549..60e2a4447 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -12,6 +12,8 @@ package big import "core:fmt" import "core:mem" +import "core:time" +import rnd "core:math/rand" print_configation :: proc() { fmt.printf( @@ -40,32 +42,51 @@ _SQR_TOOM_CUTOFF, ); } +Category :: enum { + itoa, + atoi, +}; +Event :: struct { + t: time.Duration, + c: int, +} +Timings := [Category]Event{}; + print :: proc(name: string, a: ^Int, base := i8(10)) { + s := time.tick_now(); as, err := itoa(a, base); + Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; + defer delete(as); cb, _ := count_bits(a); fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); if err != .None { fmt.printf("%v (error: %v | %v)\n", name, err, a); } - } -@thread_local string_buffer: [1024]u8; - demo :: proc() { err: Error; + as: string; + + r := &rnd.Rand{}; + rnd.init(r, 12345); destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - for i in 1..=10 { - err = rand(destination, 1200); // 1200 random bits + err = rand(destination, 120, r); + // print("destination", destination, 10); + for _ in 0 ..< 10_000 { if err != .None { - fmt.printf("rand error: %v\n", err); + fmt.printf("set error: %v\n", err); } else { - fmt.printf("#%3d: ", i); - print("", destination); + s := time.tick_now(); + as, err = itoa(destination, 16); + e := time.tick_since(s); + Timings[.itoa].t += e; Timings[.itoa].c += 1; + assert(as == "B38677A01B8EF75CF434CA68677495", as); + delete(as); } } } @@ -75,9 +96,18 @@ main :: proc() { mem.tracking_allocator_init(&ta, context.allocator); context.allocator = mem.tracking_allocator(&ta); - print_configation(); + // print_configation(); demo(); + fmt.printf("\nTimings:\n"); + for v, i in Timings { + if v.c > 0 { + avg := time.duration_milliseconds(time.Duration(f64(v.t) / f64(v.c))); + total := time.duration_milliseconds(time.Duration(v.t)); + fmt.printf("%v: %.3f ms (avg), %.3f (total, %v calls)\n", i, avg, total, v.c); + } + } + if len(ta.allocation_map) > 0 { for _, v in ta.allocation_map { fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index a15929d2e..dc4fc697d 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -11,7 +11,7 @@ package big import "core:mem" import "core:intrinsics" -import "core:math/rand" +import rnd "core:math/rand" /* Deallocates the backing memory of one or more `Int`s. @@ -177,7 +177,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Helpers to extract values from the `Int`. */ -extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { +int_extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { /* Check that `a`is usable. */ @@ -195,10 +195,7 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { return 1 if ((a.digit[limb] & i) != 0) else 0, .None; } -/* - TODO: Optimize. -*/ -extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { +int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* Check that `a`is usable. */ @@ -210,18 +207,40 @@ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { return 0, .Invalid_Argument; } - v: DIGIT; - e: Error; - for shift := 0; shift < count; shift += 1 { - o := offset + shift; - v, e = extract_bit(a, o); - if e != .None { - break; - } - res = res + _WORD(v) << uint(shift); + limb_lo := offset / _DIGIT_BITS; + bits_lo := offset % _DIGIT_BITS; + limb_hi := (offset + count) / _DIGIT_BITS; + bits_hi := (offset + count) % _DIGIT_BITS; + + if limb_lo < 0 || limb_lo >= a.used || limb_hi < 0 || limb_hi >= a.used { + return 0, .Invalid_Argument; } - return res, e; + for i := limb_hi; i >= limb_lo; i -= 1 { + res <<= _DIGIT_BITS; + + /* + Determine which bits to extract from each DIGIT. The whole DIGIT's worth by default. + */ + bit_count := _DIGIT_BITS; + bit_offset := 0; + if i == limb_lo { + bit_count -= bits_lo; + bit_offset = _DIGIT_BITS - bit_count; + } else if i == limb_hi { + bit_count = bits_hi; + bit_offset = 0; + } + + d := a.digit[i]; + + v := (d >> uint(bit_offset)) & DIGIT(1 << uint(bit_count - 1)); + m := DIGIT(1 << uint(bit_count-1)); + r := v & m; + + res |= _WORD(r); + } + return res, .None; } /* @@ -507,9 +526,9 @@ count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { return count, .None; } -int_random_digit :: proc(r: ^rand.Rand = nil) -> (res: DIGIT) { +int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { when _DIGIT_BITS == 60 { // DIGIT = u64 - return DIGIT(rand.uint64(r)) & _MASK; + return DIGIT(rnd.uint64(r)) & _MASK; } else when _DIGIT_BITS == 28 { // DIGIT = u32 return DIGIT(rand.uint32(r)) & _MASK; } else { @@ -519,7 +538,7 @@ int_random_digit :: proc(r: ^rand.Rand = nil) -> (res: DIGIT) { return 0; // We shouldn't get here. } -int_rand :: proc(dest: ^Int, bits: int, r: ^rand.Rand = nil) -> (err: Error) { +int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Error) { bits := bits; if bits <= 0 { return .Invalid_Argument; } diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index d64956ba8..06066af18 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -202,7 +202,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter for offset := 0; offset < count; offset += 4 { bits_to_get := int(min(count - offset, shift)); - if digit, err = extract_bits(a, offset, bits_to_get); err != .None { + if digit, err = int_bitfield_extract(a, offset, bits_to_get); err != .None { return len(buffer) - available, .Invalid_Argument; } available -= 1; From 74258a170a20fd5a8fb6afe420b7d877d674c100 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 15:59:39 +0200 Subject: [PATCH 045/105] big: fix `itoa` base PoT other than 16. --- core/math/big/example.odin | 5 +-- core/math/big/helpers.odin | 78 ++++++++++++++++++++++++-------------- core/math/big/radix.odin | 2 +- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 60e2a4447..f6b7236c6 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -76,7 +76,6 @@ demo :: proc() { defer destroy(destination, source, quotient, remainder, numerator, denominator); err = rand(destination, 120, r); - // print("destination", destination, 10); for _ in 0 ..< 10_000 { if err != .None { fmt.printf("set error: %v\n", err); @@ -85,7 +84,7 @@ demo :: proc() { as, err = itoa(destination, 16); e := time.tick_since(s); Timings[.itoa].t += e; Timings[.itoa].c += 1; - assert(as == "B38677A01B8EF75CF434CA68677495", as); + //assert(as == "ADCC737B67B0FCD7F189074CBE088B718141A383F9CF09B4D3824A09A3AEBAC155B810C29D62385F8F85616794C25393A757CEDEEBE3B0FE24573894DF7842A76F543D64A78FFD24D325CE044E9A0F69DE00CFFCC41427170096BC6D3537C856CD930A3794F03DB558CD5DB6A65971E618C5D0DBAE1E7AF52DDB8F5F84CD5BFC0B2EEEDBFB70E6B38677A01B8EF75CF434CA68677495", as); delete(as); } } @@ -104,7 +103,7 @@ main :: proc() { if v.c > 0 { avg := time.duration_milliseconds(time.Duration(f64(v.t) / f64(v.c))); total := time.duration_milliseconds(time.Duration(v.t)); - fmt.printf("%v: %.3f ms (avg), %.3f (total, %v calls)\n", i, avg, total, v.c); + fmt.printf("%v: %.3f ms (avg), %.3f ms (total, %v calls)\n", i, avg, total, v.c); } } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index dc4fc697d..d5cf16b33 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -177,7 +177,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Helpers to extract values from the `Int`. */ -int_extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { +extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { /* Check that `a`is usable. */ @@ -195,6 +195,9 @@ int_extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { return 1 if ((a.digit[limb] & i) != 0) else 0, .None; } +/* + TODO: Optimize. +*/ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* Check that `a`is usable. @@ -207,40 +210,57 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: E return 0, .Invalid_Argument; } - limb_lo := offset / _DIGIT_BITS; - bits_lo := offset % _DIGIT_BITS; - limb_hi := (offset + count) / _DIGIT_BITS; - bits_hi := (offset + count) % _DIGIT_BITS; + when true { + v: DIGIT; + e: Error; - if limb_lo < 0 || limb_lo >= a.used || limb_hi < 0 || limb_hi >= a.used { - return 0, .Invalid_Argument; - } - - for i := limb_hi; i >= limb_lo; i -= 1 { - res <<= _DIGIT_BITS; - - /* - Determine which bits to extract from each DIGIT. The whole DIGIT's worth by default. - */ - bit_count := _DIGIT_BITS; - bit_offset := 0; - if i == limb_lo { - bit_count -= bits_lo; - bit_offset = _DIGIT_BITS - bit_count; - } else if i == limb_hi { - bit_count = bits_hi; - bit_offset = 0; + for shift := 0; shift < count; shift += 1 { + o := offset + shift; + v, e = extract_bit(a, o); + if e != .None { + break; + } + res = res + _WORD(v) << uint(shift); } - d := a.digit[i]; + return res, e; + } else { + limb_lo := offset / _DIGIT_BITS; + bits_lo := offset % _DIGIT_BITS; + limb_hi := (offset + count) / _DIGIT_BITS; + bits_hi := (offset + count) % _DIGIT_BITS; - v := (d >> uint(bit_offset)) & DIGIT(1 << uint(bit_count - 1)); - m := DIGIT(1 << uint(bit_count-1)); - r := v & m; + if limb_lo < 0 || limb_lo >= a.used || limb_hi < 0 || limb_hi >= a.used { + return 0, .Invalid_Argument; + } + + for i := limb_hi; i >= limb_lo; i -= 1 { + res <<= _DIGIT_BITS; + + /* + Determine which bits to extract from each DIGIT. The whole DIGIT's worth by default. + */ + bit_count := _DIGIT_BITS; + bit_offset := 0; + if i == limb_lo { + bit_count -= bits_lo; + bit_offset = _DIGIT_BITS - bit_count; + } else if i == limb_hi { + bit_count = bits_hi; + bit_offset = 0; + } + + d := a.digit[i]; + + v := (d >> uint(bit_offset)) & DIGIT(1 << uint(bit_count - 1)); + m := DIGIT(1 << uint(bit_count-1)); + r := v & m; + + res |= _WORD(r); + } + return res, .None; - res |= _WORD(r); } - return res, .None; } /* diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 06066af18..572adec6a 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -200,7 +200,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter count, err = count_bits(a); digit: _WORD; - for offset := 0; offset < count; offset += 4 { + for offset := 0; offset < count; offset += shift { bits_to_get := int(min(count - offset, shift)); if digit, err = int_bitfield_extract(a, offset, bits_to_get); err != .None { return len(buffer) - available, .Invalid_Argument; From 85aa4dd670ce308eaf51b6eefd10dd62a227c998 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 21:09:52 +0200 Subject: [PATCH 046/105] big: Start test suite. --- core/math/big/common.odin | 17 ++++++++ core/math/big/example.odin | 27 +++---------- core/math/big/test.odin | 44 +++++++++++++++++++++ core/math/big/test.py | 79 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 core/math/big/test.odin create mode 100644 core/math/big/test.py diff --git a/core/math/big/common.odin b/core/math/big/common.odin index dd17a678f..59e76a9aa 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -66,6 +66,23 @@ Error :: enum byte { Unimplemented = 127, }; +Error_String :: #partial [Error]string{ + .None = "None", + .Out_Of_Memory = "Out of memory", + .Invalid_Pointer = "Invalid pointer", + .Invalid_Argument = "Invalid argument", + + .Unknown_Error = "Unknown error", + .Max_Iterations_Reached = "Max iterations reached", + .Buffer_Overflow = "Buffer overflow", + .Integer_Overflow = "Integer overflow", + + .Division_by_Zero = "Division by zero", + .Math_Domain_Error = "Math domain error", + + .Unimplemented = "Unimplemented", +}; + Primality_Flag :: enum u8 { Blum_Blum_Shub = 0, /* BBS style prime */ Safe = 1, /* Safe prime (p-1)/2 == prime */ diff --git a/core/math/big/example.odin b/core/math/big/example.odin index f6b7236c6..6dad8ebd7 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -13,7 +13,7 @@ package big import "core:fmt" import "core:mem" import "core:time" -import rnd "core:math/rand" +// import rnd "core:math/rand" print_configation :: proc() { fmt.printf( @@ -66,28 +66,13 @@ print :: proc(name: string, a: ^Int, base := i8(10)) { } demo :: proc() { - err: Error; - as: string; + // err: Error; - r := &rnd.Rand{}; - rnd.init(r, 12345); + // r := &rnd.Rand{}; + // rnd.init(r, 12345); - destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(destination, source, quotient, remainder, numerator, denominator); - - err = rand(destination, 120, r); - for _ in 0 ..< 10_000 { - if err != .None { - fmt.printf("set error: %v\n", err); - } else { - s := time.tick_now(); - as, err = itoa(destination, 16); - e := time.tick_since(s); - Timings[.itoa].t += e; Timings[.itoa].c += 1; - //assert(as == "ADCC737B67B0FCD7F189074CBE088B718141A383F9CF09B4D3824A09A3AEBAC155B810C29D62385F8F85616794C25393A757CEDEEBE3B0FE24573894DF7842A76F543D64A78FFD24D325CE044E9A0F69DE00CFFCC41427170096BC6D3537C856CD930A3794F03DB558CD5DB6A65971E618C5D0DBAE1E7AF52DDB8F5F84CD5BFC0B2EEEDBFB70E6B38677A01B8EF75CF434CA68677495", as); - delete(as); - } - } + // destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + // defer destroy(destination, source, quotient, remainder, numerator, denominator); } main :: proc() { diff --git a/core/math/big/test.odin b/core/math/big/test.odin new file mode 100644 index 000000000..f1bdcff99 --- /dev/null +++ b/core/math/big/test.odin @@ -0,0 +1,44 @@ +//+ignore +package big + +/* + Copyright 2021 Jeroen van Rijn . + Made available under Odin's BSD-2 license. + + An arbitrary precision mathematics 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`, `sub`, `mul`, `div`, ... +*/ + +import "core:runtime" +import "core:strings" + +PyRes :: struct { + res: cstring, + err: Error, +} + +@export test_error_string :: proc "c" (err: Error) -> (res: cstring) { + context = runtime.default_context(); + es := Error_String; + return strings.clone_to_cstring(es[err], context.temp_allocator); +} + +@export test_add_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + aa, bb, sum := &Int{}, &Int{}, &Int{}; + defer destroy(aa, bb, sum); + + if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":add_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":add_two:atoi(b):", err=err}; } + if err = add(sum, aa, bb); err != .None { return PyRes{res=":add_two:add(sum,a,b):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(sum, i8(radix), context.temp_allocator); + if err != .None { return PyRes{res=":add_two:itoa(sum):", err=err}; } + return PyRes{res = r, err = .None}; +} \ No newline at end of file diff --git a/core/math/big/test.py b/core/math/big/test.py new file mode 100644 index 000000000..c0e2c2173 --- /dev/null +++ b/core/math/big/test.py @@ -0,0 +1,79 @@ +from math import * +from ctypes import * +import os + +# +# Where is the DLL? If missing, build using: `odin build . -build-mode:dll` +# +LIB_PATH = os.getcwd() + os.sep + "big.dll" + +# +# Result values will be passed in a struct { res: cstring, err: Error } +# +class Res(Structure): + _fields_ = [("res", c_char_p), ("err", c_byte)] + +# +# Error enum values +# +E_None = 0 +E_Out_Of_Memory = 1 +E_Invalid_Pointer = 2 +E_Invalid_Argument = 3 +E_Unknown_Error = 4 +E_Max_Iterations_Reached = 5 +E_Buffer_Overflow = 6 +E_Integer_Overflow = 7 +E_Division_by_Zero = 8 +E_Math_Domain_Error = 9 +E_Unimplemented = 127 + +# +# Set up exported procedures +# + +try: + l = cdll.LoadLibrary(LIB_PATH) +except: + print("Couldn't find or load " + LIB_PATH + ".") + exit(1) + +try: + l.test_add_two.argtypes = [c_char_p, c_char_p, c_longlong] + l.test_add_two.restype = Res +except: + print("Couldn't find exported function 'test_add_two'") + exit(2) + +add_two = l.test_add_two + +try: + l.test_error_string.argtypes = [c_byte] + l.test_error_string.restype = c_char_p +except: + print("Couldn't find exported function 'test_error_string'") + exit(2) + +def error(res: Res, param=[]): + if res.err != E_None: + error_type = l.test_error_string(res.err).decode('utf-8') + error_loc = res.res.decode('utf-8') + + error_string = "'{}' error in '{}'".format(error_type, error_loc) + if len(param): + error_string += " with params {}".format(param) + + print(error_string, flush=True) + os._exit(res.err) + + +def test_add_two(a = 0, b = 0, radix = 10): + res = add_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + error(res, [str(a), str(b), radix]) + +if __name__ == '__main__': + print("---- core:math/big tests ----") + print() + + test_add_two(1234, 5432, 10) + test_add_two(1234, 5432, 110) \ No newline at end of file From fb6c9af1ae76be61acbc5881ae520ace265bb704 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 21:57:49 +0200 Subject: [PATCH 047/105] big: Improve tests. --- core/math/big/example.odin | 1 - core/math/big/test.py | 51 ++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 6dad8ebd7..5857c0a2e 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -67,7 +67,6 @@ print :: proc(name: string, a: ^Int, base := i8(10)) { demo :: proc() { // err: Error; - // r := &rnd.Rand{}; // rnd.init(r, 12345); diff --git a/core/math/big/test.py b/core/math/big/test.py index c0e2c2173..913e0ac24 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -54,26 +54,61 @@ except: print("Couldn't find exported function 'test_error_string'") exit(2) -def error(res: Res, param=[]): - if res.err != E_None: +def test(test_name: "", res: Res, param=[], expected_result = "", expected_error = E_None): + had_error = False + r = None + + if res.err != expected_error: error_type = l.test_error_string(res.err).decode('utf-8') error_loc = res.res.decode('utf-8') - error_string = "'{}' error in '{}'".format(error_type, error_loc) + error_string = "{}: '{}' error in '{}'".format(test_name, error_type, error_loc) if len(param): error_string += " with params {}".format(param) print(error_string, flush=True) - os._exit(res.err) + had_error = True + elif res.err == E_None: + try: + r = res.res.decode('utf-8') + except: + pass + r = eval(res.res) + if r != expected_result: + error_string = "{}: Result was '{}', expected '{}'".format(test_name, r, expected_result) + if len(param): + error_string += " with params {}".format(param) -def test_add_two(a = 0, b = 0, radix = 10): + print(error_string, flush=True) + had_error = True + + return had_error + +def test_add_two(a = 0, b = 0, radix = 10, expected_result = "", expected_error = E_None): res = add_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) - error(res, [str(a), str(b), radix]) + return test("test_add_two", res, [str(a), str(b), radix], expected_result, expected_error) + + +ADD_TESTS = [ + [ 1234, 5432, 10, + 6666, E_None, ], + [ 1234, 5432, 110, + 6666, E_Invalid_Argument, ], +] if __name__ == '__main__': print("---- core:math/big tests ----") print() - test_add_two(1234, 5432, 10) - test_add_two(1234, 5432, 110) \ No newline at end of file + count_pass = 0 + count_fail = 0 + + for t in ADD_TESTS: + res = test_add_two(*t) + if res: + count_fail += 1 + else: + count_pass += 1 + + print("ADD_TESTS: {} passes, {} failures.".format(count_pass, count_fail)) \ No newline at end of file From 708389a7eebf675b821a2eba094592ca63cdd82f Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 22:35:25 +0200 Subject: [PATCH 048/105] big: Improve test driver. --- core/math/big/test.py | 47 ++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/core/math/big/test.py b/core/math/big/test.py index 913e0ac24..9d7539d0e 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -54,8 +54,8 @@ except: print("Couldn't find exported function 'test_error_string'") exit(2) -def test(test_name: "", res: Res, param=[], expected_result = "", expected_error = E_None): - had_error = False +def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_result = ""): + passed = True r = None if res.err != expected_error: @@ -67,7 +67,7 @@ def test(test_name: "", res: Res, param=[], expected_result = "", expected_error error_string += " with params {}".format(param) print(error_string, flush=True) - had_error = True + passed = False elif res.err == E_None: try: r = res.res.decode('utf-8') @@ -81,34 +81,35 @@ def test(test_name: "", res: Res, param=[], expected_result = "", expected_error error_string += " with params {}".format(param) print(error_string, flush=True) - had_error = True + passed = False - return had_error + return passed -def test_add_two(a = 0, b = 0, radix = 10, expected_result = "", expected_error = E_None): +def test_add_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): res = add_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) - return test("test_add_two", res, [str(a), str(b), radix], expected_result, expected_error) + if expected_result == None: + expected_result = a + b + return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result) -ADD_TESTS = [ - [ 1234, 5432, 10, - 6666, E_None, ], - [ 1234, 5432, 110, - 6666, E_Invalid_Argument, ], -] +TESTS = { + test_add_two: [ + [ 1234, 5432, 10, ], + [ 1234, 5432, 110, E_Invalid_Argument, ], + ], +} if __name__ == '__main__': print("---- core:math/big tests ----") print() - count_pass = 0 - count_fail = 0 + for test_proc in TESTS: + count_pass = 0 + count_fail = 0 + for t in TESTS[test_proc]: + if test_proc(*t): + count_pass += 1 + else: + count_fail += 1 - for t in ADD_TESTS: - res = test_add_two(*t) - if res: - count_fail += 1 - else: - count_pass += 1 - - print("ADD_TESTS: {} passes, {} failures.".format(count_pass, count_fail)) \ No newline at end of file + print("{}: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail)) \ No newline at end of file From 13fab366394c4c1bd4f1e8eb2eefea81208764a6 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 29 Jul 2021 16:37:16 +0200 Subject: [PATCH 049/105] big: Fix `mul`. --- core/math/big/basic.odin | 8 +-- core/math/big/build.bat | 6 ++- core/math/big/test.odin | 54 ++++++++++++++++++++ core/math/big/test.py | 108 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 168 insertions(+), 8 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index ef069de17..7d0c467a8 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -950,7 +950,7 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { Limit ourselves to `digits` DIGITs of output. */ pb := min(b.used, digits - ix); - carry := DIGIT(0); + carry := _WORD(0); iy := 0; /* Compute the column of the output and propagate the carry. @@ -959,12 +959,12 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Compute the column as a _WORD. */ - column := t.digit[ix + iy] + a.digit[ix] * b.digit[iy] + carry; + column := _WORD(t.digit[ix + iy]) + _WORD(a.digit[ix]) * _WORD(b.digit[iy]) + carry; /* The new column is the lower part of the result. */ - t.digit[ix + iy] = column & _MASK; + t.digit[ix + iy] = DIGIT(column & _WORD(_MASK)); /* Get the carry word from the result. @@ -975,7 +975,7 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { Set carry if it is placed below digits */ if ix + iy < digits { - t.digit[ix + pb] = carry; + t.digit[ix + pb] = DIGIT(carry); } } diff --git a/core/math/big/build.bat b/core/math/big/build.bat index fa1a0d382..7df001c43 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,2 +1,6 @@ @echo off -odin run . -vet \ No newline at end of file +:odin run . -vet +odin build . -build-mode:dll + +:dumpbin /EXPORTS big.dll +python test.py \ No newline at end of file diff --git a/core/math/big/test.odin b/core/math/big/test.odin index f1bdcff99..0b0d65c26 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -41,4 +41,58 @@ PyRes :: struct { r, err = int_itoa_cstring(sum, i8(radix), context.temp_allocator); if err != .None { return PyRes{res=":add_two:itoa(sum):", err=err}; } return PyRes{res = r, err = .None}; +} + +@export test_sub_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + aa, bb, sum := &Int{}, &Int{}, &Int{}; + defer destroy(aa, bb, sum); + + if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":sub_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":sub_two:atoi(b):", err=err}; } + if err = sub(sum, aa, bb); err != .None { return PyRes{res=":sub_two:sub(sum,a,b):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(sum, i8(radix), context.temp_allocator); + if err != .None { return PyRes{res=":sub_two:itoa(sum):", err=err}; } + return PyRes{res = r, err = .None}; +} + +@export test_mul_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + aa, bb, product := &Int{}, &Int{}, &Int{}; + defer destroy(aa, bb, product); + + if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":mul_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":mul_two:atoi(b):", err=err}; } + if err = mul(product, aa, bb); err != .None { return PyRes{res=":mul_two:mul(product,a,b):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(product, i8(radix), context.temp_allocator); + if err != .None { return PyRes{res=":mul_two:itoa(product):", err=err}; } + return PyRes{res = r, err = .None}; +} + +/* + NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient. +*/ +@export test_div_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + aa, bb, quotient := &Int{}, &Int{}, &Int{}; + defer destroy(aa, bb, quotient); + + if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":div_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":div_two:atoi(b):", err=err}; } + if err = div(quotient, aa, bb); err != .None { return PyRes{res=":div_two:div(quotient,a,b):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(quotient, i8(radix), context.temp_allocator); + if err != .None { return PyRes{res=":div_two:itoa(quotient):", err=err}; } + return PyRes{res = r, err = .None}; } \ No newline at end of file diff --git a/core/math/big/test.py b/core/math/big/test.py index 9d7539d0e..ad5cc9f35 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -1,5 +1,6 @@ from math import * from ctypes import * +from random import * import os # @@ -38,6 +39,9 @@ except: print("Couldn't find or load " + LIB_PATH + ".") exit(1) +# +# res = a + b, err +# try: l.test_add_two.argtypes = [c_char_p, c_char_p, c_longlong] l.test_add_two.restype = Res @@ -47,6 +51,44 @@ except: add_two = l.test_add_two +# +# res = a - b, err +# +try: + l.test_sub_two.argtypes = [c_char_p, c_char_p, c_longlong] + l.test_sub_two.restype = Res +except: + print("Couldn't find exported function 'test_sub_two'") + exit(2) + +sub_two = l.test_sub_two + +# +# res = a * b, err +# +try: + l.test_mul_two.argtypes = [c_char_p, c_char_p, c_longlong] + l.test_mul_two.restype = Res +except: + print("Couldn't find exported function 'test_add_two'") + exit(2) + +mul_two = l.test_mul_two + +# +# res = a / b, err +# +try: + l.test_div_two.argtypes = [c_char_p, c_char_p, c_longlong] + l.test_div_two.restype = Res +except: + print("Couldn't find exported function 'test_div_two'") + exit(2) + +div_two = l.test_div_two + + + try: l.test_error_string.argtypes = [c_byte] l.test_error_string.restype = c_char_p @@ -91,15 +133,52 @@ def test_add_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_res expected_result = a + b return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result) +def test_sub_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): + res = sub_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + if expected_result == None: + expected_result = a - b + return test("test_sub_two", res, [str(a), str(b), radix], expected_error, expected_result) + +def test_mul_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): + res = mul_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + if expected_result == None: + expected_result = a * b + return test("test_mul_two", res, [str(a), str(b), radix], expected_error, expected_result) + +def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): + res = div_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + if expected_result == None: + expected_result = a // b if b != 0 else None + return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result) + +# TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. +# +# The last two arguments in tests are the expected error and expected result. +# +# The expected error defaults to None. +# By default the Odin implementation will be tested against the Python one. +# You can override that by supplying an expected result as the last argument instead. TESTS = { test_add_two: [ - [ 1234, 5432, 10, ], - [ 1234, 5432, 110, E_Invalid_Argument, ], + [ 1234, 5432, 10, ], + [ 1234, 5432, 110, E_Invalid_Argument, ], + ], + test_sub_two: [ + [ 1234, 5432, 10, ], + ], + test_mul_two: [ + [ 1234, 5432, 10, ], + [ 1099243943008198766717263669950239669, 137638828577110581150675834234248871, 10, ] + ], + test_div_two: [ + [ 54321, 12345, 10, ], + [ 55431, 0, 10, E_Division_by_Zero, ], ], } if __name__ == '__main__': + print() print("---- core:math/big tests ----") print() @@ -112,4 +191,27 @@ if __name__ == '__main__': else: count_fail += 1 - print("{}: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail)) \ No newline at end of file + print("{}: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail)) + + print() + print("---- core:math/big random tests ----") + print() + + for test_proc in [test_add_two, test_sub_two, test_mul_two, test_div_two]: + count_pass = 0 + count_fail = 0 + + a = randint(0, 1 << 120) + b = randint(0, 1 << 120) + res = None + + # We've already tested division by zero above. + if b == 0 and test_proc == test_div_two: + b = b + 1 + + if test_proc(a, b): + count_pass += 1 + else: + count_fail += 1 + + print("{} random: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail)) \ No newline at end of file From c1a001c331b47e3f49241896deb48c2130b4abbb Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 29 Jul 2021 17:29:32 +0200 Subject: [PATCH 050/105] big: Add randomized testing. --- core/math/big/build.bat | 3 +- core/math/big/example.odin | 5 ++ core/math/big/logical.odin | 2 +- core/math/big/test.py | 94 +++++++++++++++++++++++++++++--------- 4 files changed, 81 insertions(+), 23 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 7df001c43..b4f340399 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,6 +1,7 @@ @echo off :odin run . -vet -odin build . -build-mode:dll +odin build . -build-mode:dll -show-timings -opt:3 +:odin build . -build-mode:dll -show-timings :dumpbin /EXPORTS big.dll python test.py \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 5857c0a2e..e677af8b9 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -70,6 +70,11 @@ demo :: proc() { // r := &rnd.Rand{}; // rnd.init(r, 12345); + // as := cstring("596360079055148742691396559496540363"); + // bs := cstring("159671292010002348397151706347412301"); + + // res := test_div_two(as, bs); + // fmt.print(res); // destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; // defer destroy(destination, source, quotient, remainder, numerator, denominator); } diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 9b6654a5c..1d2ba0895 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -402,7 +402,7 @@ int_shl :: proc(dest, src: ^Int, bits: int) -> (err: Error) { shift := DIGIT(_DIGIT_BITS - bits); carry := DIGIT(0); - for x:= 0; x <= dest.used; x+= 1 { + for x:= 0; x < dest.used; x+= 1 { fwd_carry := (dest.digit[x] >> shift) & mask; dest.digit[x] = (dest.digit[x] << uint(bits) | carry) & _MASK; carry = fwd_carry; diff --git a/core/math/big/test.py b/core/math/big/test.py index ad5cc9f35..bd363caf0 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -2,12 +2,18 @@ from math import * from ctypes import * from random import * import os +import time # # Where is the DLL? If missing, build using: `odin build . -build-mode:dll` # LIB_PATH = os.getcwd() + os.sep + "big.dll" +# +# How many iterations of each random test do we want to run? +# +RANDOM_ITERATIONS = 10_000 + # # Result values will be passed in a struct { res: cstring, err: Error } # @@ -128,28 +134,48 @@ def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_re return passed def test_add_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - res = add_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + sa = str(a) + sb = str(b) + sa_c = sa.encode('utf-8') + sb_c = sb.encode('utf-8') + res = add_two(sa_c, sb_c, radix) if expected_result == None: expected_result = a + b - return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result) + return test("test_add_two", res, [sa, sb, radix], expected_error, expected_result) def test_sub_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - res = sub_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + sa = str(a) + sb = str(b) + sa_c = sa.encode('utf-8') + sb_c = sb.encode('utf-8') + res = sub_two(sa_c, sb_c, radix) if expected_result == None: expected_result = a - b - return test("test_sub_two", res, [str(a), str(b), radix], expected_error, expected_result) + return test("test_sub_two", res, [sa_c, sb_c, radix], expected_error, expected_result) def test_mul_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - res = mul_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + sa = str(a) + sb = str(b) + sa_c = sa.encode('utf-8') + sb_c = sb.encode('utf-8') + res = mul_two(sa_c, sb_c, radix) if expected_result == None: expected_result = a * b - return test("test_mul_two", res, [str(a), str(b), radix], expected_error, expected_result) + return test("test_mul_two", res, [sa_c, sb_c, radix], expected_error, expected_result) def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - res = div_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix) + sa = str(a) + sb = str(b) + sa_c = sa.encode('utf-8') + sb_c = sb.encode('utf-8') + try: + res = div_two(sa_c, sb_c, radix) + except: + print("Exception with arguments:", a, b, radix) + return False if expected_result == None: expected_result = a // b if b != 0 else None - return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result) + return test("test_div_two", res, [sa_c, sb_c, radix], expected_error, expected_result) # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # @@ -177,6 +203,8 @@ TESTS = { ], } +TIMINGS = {} + if __name__ == '__main__': print() print("---- core:math/big tests ----") @@ -186,12 +214,20 @@ if __name__ == '__main__': count_pass = 0 count_fail = 0 for t in TESTS[test_proc]: - if test_proc(*t): + start = time.perf_counter() + res = test_proc(*t) + diff = time.perf_counter() - start + if test_proc not in TIMINGS: + TIMINGS[test_proc] = diff + else: + TIMINGS[test_proc] += diff + + if res: count_pass += 1 else: count_fail += 1 - print("{}: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail)) + print("{name}: {count_pass:,} passes, {count_fail:,} failures.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail)) print() print("---- core:math/big random tests ----") @@ -201,17 +237,33 @@ if __name__ == '__main__': count_pass = 0 count_fail = 0 - a = randint(0, 1 << 120) - b = randint(0, 1 << 120) - res = None + for i in range(RANDOM_ITERATIONS): + a = randint(0, 1 << 120) + b = randint(0, 1 << 120) + res = None - # We've already tested division by zero above. - if b == 0 and test_proc == test_div_two: - b = b + 1 + # We've already tested division by zero above. + if b == 0 and test_proc == test_div_two: + b = b + 1 - if test_proc(a, b): - count_pass += 1 - else: - count_fail += 1 + start = time.perf_counter() + res = test_proc(a, b) + diff = time.perf_counter() - start + if test_proc not in TIMINGS: + TIMINGS[test_proc] = diff + else: + TIMINGS[test_proc] += diff - print("{} random: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail)) \ No newline at end of file + if res: + count_pass += 1 + else: + count_fail += 1 + + print("{name}: {count_pass:,} passes, {count_fail:,} failures.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail)) + + print() + total = 0 + for k in TIMINGS: + print("{name}: {total:.3f} ms in {calls:,} calls".format(name=k.__name__, total=TIMINGS[k] * 1_000, calls=RANDOM_ITERATIONS + len(TESTS[k]))) + total += TIMINGS[k] + print("\ntotal: {0:.3f} ms".format(total * 1_000)) \ No newline at end of file From 922df6a438eeb2e3e547aedbfbb4580b7393ce5a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 29 Jul 2021 18:25:32 +0200 Subject: [PATCH 051/105] big: Add more exhaustive tests. --- core/math/big/build.bat | 5 +-- core/math/big/test.py | 98 +++++++++++++++++++++++++---------------- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index b4f340399..de6003238 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,7 +1,6 @@ @echo off :odin run . -vet -odin build . -build-mode:dll -show-timings -opt:3 -:odin build . -build-mode:dll -show-timings +odin build . -build-mode:shared -show-timings -o:speed +:odin build . -build-mode:shared -show-timings -:dumpbin /EXPORTS big.dll python test.py \ No newline at end of file diff --git a/core/math/big/test.py b/core/math/big/test.py index bd363caf0..e00af473d 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -2,17 +2,30 @@ from math import * from ctypes import * from random import * import os +import platform import time # # Where is the DLL? If missing, build using: `odin build . -build-mode:dll` # -LIB_PATH = os.getcwd() + os.sep + "big.dll" - +if platform.system() == "Windows": + LIB_PATH = os.getcwd() + os.sep + "big.dll" +elif platform.system() == "Linux": + LIB_PATH = os.getcwd() + os.sep + "big.so" +elif platform.system() == "Darwin": + LIB_PATH = os.getcwd() + os.sep + "big.dylib" +else: + print("Platform is unsupported.") + os.exit(1) # # How many iterations of each random test do we want to run? # -RANDOM_ITERATIONS = 10_000 +BITS_AND_ITERATIONS = [ + ( 120, 10_000), + ( 1_200, 1_000), + ( 4_096, 100), + (12_000, 10), +] # # Result values will be passed in a struct { res: cstring, err: Error } @@ -203,67 +216,74 @@ TESTS = { ], } -TIMINGS = {} +TOTAL_TIME = 0 +total_failures = 0 if __name__ == '__main__': - print() print("---- core:math/big tests ----") print() for test_proc in TESTS: count_pass = 0 count_fail = 0 + TIMINGS = {} for t in TESTS[test_proc]: start = time.perf_counter() res = test_proc(*t) diff = time.perf_counter() - start + TOTAL_TIME += diff + if test_proc not in TIMINGS: TIMINGS[test_proc] = diff else: TIMINGS[test_proc] += diff if res: - count_pass += 1 + count_pass += 1 else: - count_fail += 1 + count_fail += 1 + total_failures += 1 - print("{name}: {count_pass:,} passes, {count_fail:,} failures.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail)) + print("{name}: {count_pass:,} passes and {count_fail:,} failures in {timing:.3f} ms.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000)) - print() - print("---- core:math/big random tests ----") - print() + for BITS, ITERATIONS in BITS_AND_ITERATIONS: + print() + print("---- core:math/big with two random {bits:,} bit numbers ----".format(bits=BITS)) + print() - for test_proc in [test_add_two, test_sub_two, test_mul_two, test_div_two]: - count_pass = 0 - count_fail = 0 + for test_proc in [test_add_two, test_sub_two, test_mul_two, test_div_two]: + count_pass = 0 + count_fail = 0 + TIMINGS = {} - for i in range(RANDOM_ITERATIONS): - a = randint(0, 1 << 120) - b = randint(0, 1 << 120) - res = None + for i in range(ITERATIONS): + a = randint(0, 1 << BITS) + b = randint(0, 1 << BITS) + res = None - # We've already tested division by zero above. - if b == 0 and test_proc == test_div_two: - b = b + 1 + # We've already tested division by zero above. + if b == 0 and test_proc == test_div_two: + b = b + 1 - start = time.perf_counter() - res = test_proc(a, b) - diff = time.perf_counter() - start - if test_proc not in TIMINGS: - TIMINGS[test_proc] = diff - else: - TIMINGS[test_proc] += diff + start = time.perf_counter() + res = test_proc(a, b) + diff = time.perf_counter() - start + TOTAL_TIME += diff - if res: - count_pass += 1 - else: - count_fail += 1 + if test_proc not in TIMINGS: + TIMINGS[test_proc] = diff + else: + TIMINGS[test_proc] += diff - print("{name}: {count_pass:,} passes, {count_fail:,} failures.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail)) + if res: + count_pass += 1 + else: + count_fail += 1 + total_failures += 1 - print() - total = 0 - for k in TIMINGS: - print("{name}: {total:.3f} ms in {calls:,} calls".format(name=k.__name__, total=TIMINGS[k] * 1_000, calls=RANDOM_ITERATIONS + len(TESTS[k]))) - total += TIMINGS[k] - print("\ntotal: {0:.3f} ms".format(total * 1_000)) \ No newline at end of file + print("{name}: {count_pass:,} passes and {count_fail:,} failures in {timing:.3f} ms.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000)) + + print("\ntotal: {0:.3f} ms".format(TOTAL_TIME * 1_000)) + + if total_failures: + os.exit(1) \ No newline at end of file From 385b9c99229f09a6d7a45fd143747e1135331f94 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 29 Jul 2021 21:12:59 +0200 Subject: [PATCH 052/105] big: Add tests for `log`. --- core/math/big/example.odin | 4 +- core/math/big/exp_log.odin | 4 +- core/math/big/test.odin | 29 ++++++- core/math/big/test.py | 165 ++++++++++++++++++++----------------- 4 files changed, 123 insertions(+), 79 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index e677af8b9..5d30c85ae 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -70,10 +70,10 @@ demo :: proc() { // r := &rnd.Rand{}; // rnd.init(r, 12345); - // as := cstring("596360079055148742691396559496540363"); + // as := cstring("12341234"); // bs := cstring("159671292010002348397151706347412301"); - // res := test_div_two(as, bs); + // res := test_log(as, 2, 10); // fmt.print(res); // destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; // defer destroy(destination, source, quotient, remainder, numerator, denominator); diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index 943fd51e9..4c31a99d8 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -14,8 +14,8 @@ int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { return -1, .Invalid_Argument; } if err = clear_if_uninitialized(a); err != .None { return -1, err; } - if n, _ := is_neg(a); n { return -1, .Invalid_Argument; } - if z, _ := is_zero(a); z { return -1, .Invalid_Argument; } + if n, _ := is_neg(a); n { return -1, .Math_Domain_Error; } + if z, _ := is_zero(a); z { return -1, .Math_Domain_Error; } /* Fast path for bases that are a power of two. diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 0b0d65c26..d05d3ecaa 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -95,4 +95,31 @@ PyRes :: struct { r, err = int_itoa_cstring(quotient, i8(radix), context.temp_allocator); if err != .None { return PyRes{res=":div_two:itoa(quotient):", err=err}; } return PyRes{res = r, err = .None}; -} \ No newline at end of file +} + + +/* + res = log(a, base) +*/ +@export test_log :: proc "c" (a: cstring, base := DIGIT(2), radix := int(10)) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + l: int; + + aa := &Int{}; + defer destroy(aa); + + if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":log:atoi(a):", err=err}; } + if l, err = log(aa, base); err != .None { return PyRes{res=":log:log(a, base):", err=err}; } + + zero(aa); + aa.digit[0] = DIGIT(l) & _MASK; + aa.digit[1] = DIGIT(l) >> _DIGIT_BITS; + aa.used = 2; + clamp(aa); + + r: cstring; + r, err = int_itoa_cstring(aa, i8(radix), context.temp_allocator); + if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} diff --git a/core/math/big/test.py b/core/math/big/test.py index e00af473d..f325d7787 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -6,7 +6,12 @@ import platform import time # -# Where is the DLL? If missing, build using: `odin build . -build-mode:dll` +# Fast tests? +# +FAST_TESTS = True + +# +# Where is the DLL? If missing, build using: `odin build . -build-mode:shared` # if platform.system() == "Windows": LIB_PATH = os.getcwd() + os.sep + "big.dll" @@ -16,7 +21,8 @@ elif platform.system() == "Darwin": LIB_PATH = os.getcwd() + os.sep + "big.dylib" else: print("Platform is unsupported.") - os.exit(1) + exit(1) + # # How many iterations of each random test do we want to run? # @@ -27,6 +33,14 @@ BITS_AND_ITERATIONS = [ (12_000, 10), ] +# +# Fast tests? +# +if FAST_TESTS: + for k in range(len(BITS_AND_ITERATIONS)): + b, i = BITS_AND_ITERATIONS[k] + BITS_AND_ITERATIONS[k] = (b, i // 10 if i >= 100 else 5) + # # Result values will be passed in a struct { res: cstring, err: Error } # @@ -58,76 +72,53 @@ except: print("Couldn't find or load " + LIB_PATH + ".") exit(1) +def load(export_name, args, res): + export_name.argtypes = args + export_name.restype = res + return export_name + +error_string = load(l.test_error_string, [c_byte], c_char_p) + # # res = a + b, err # -try: - l.test_add_two.argtypes = [c_char_p, c_char_p, c_longlong] - l.test_add_two.restype = Res -except: - print("Couldn't find exported function 'test_add_two'") - exit(2) - -add_two = l.test_add_two +add_two = load(l.test_add_two, [c_char_p, c_char_p, c_longlong], Res) # # res = a - b, err # -try: - l.test_sub_two.argtypes = [c_char_p, c_char_p, c_longlong] - l.test_sub_two.restype = Res -except: - print("Couldn't find exported function 'test_sub_two'") - exit(2) - -sub_two = l.test_sub_two +sub_two = load(l.test_sub_two, [c_char_p, c_char_p, c_longlong], Res) # # res = a * b, err # -try: - l.test_mul_two.argtypes = [c_char_p, c_char_p, c_longlong] - l.test_mul_two.restype = Res -except: - print("Couldn't find exported function 'test_add_two'") - exit(2) - -mul_two = l.test_mul_two +mul_two = load(l.test_mul_two, [c_char_p, c_char_p, c_longlong], Res) # # res = a / b, err # -try: - l.test_div_two.argtypes = [c_char_p, c_char_p, c_longlong] - l.test_div_two.restype = Res -except: - print("Couldn't find exported function 'test_div_two'") - exit(2) - -div_two = l.test_div_two +div_two = load(l.test_div_two, [c_char_p, c_char_p, c_longlong], Res) +# +# res = log(a, base) +# +int_log = load(l.test_log, [c_char_p, c_longlong, c_longlong], Res) -try: - l.test_error_string.argtypes = [c_byte] - l.test_error_string.restype = c_char_p -except: - print("Couldn't find exported function 'test_error_string'") - exit(2) def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_result = ""): passed = True r = None if res.err != expected_error: - error_type = l.test_error_string(res.err).decode('utf-8') + error_type = error_string(res.err).decode('utf-8') error_loc = res.res.decode('utf-8') - error_string = "{}: '{}' error in '{}'".format(test_name, error_type, error_loc) + error = "{}: '{}' error in '{}'".format(test_name, error_type, error_loc) if len(param): - error_string += " with params {}".format(param) + error += " with params {}".format(param) - print(error_string, flush=True) + print(error, flush=True) passed = False elif res.err == E_None: try: @@ -137,50 +128,43 @@ def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_re r = eval(res.res) if r != expected_result: - error_string = "{}: Result was '{}', expected '{}'".format(test_name, r, expected_result) + error = "{}: Result was '{}', expected '{}'".format(test_name, r, expected_result) if len(param): - error_string += " with params {}".format(param) + error += " with params {}".format(param) - print(error_string, flush=True) + print(error, flush=True) passed = False return passed + def test_add_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - sa = str(a) - sb = str(b) - sa_c = sa.encode('utf-8') - sb_c = sb.encode('utf-8') + args = [str(a), str(b), radix] + sa_c, sb_c = args[0].encode('utf-8'), args[1].encode('utf-8') res = add_two(sa_c, sb_c, radix) if expected_result == None: expected_result = a + b - return test("test_add_two", res, [sa, sb, radix], expected_error, expected_result) + return test("test_add_two", res, args, expected_error, expected_result) def test_sub_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - sa = str(a) - sb = str(b) - sa_c = sa.encode('utf-8') - sb_c = sb.encode('utf-8') + sa, sb = str(a), str(b) + sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') res = sub_two(sa_c, sb_c, radix) if expected_result == None: expected_result = a - b return test("test_sub_two", res, [sa_c, sb_c, radix], expected_error, expected_result) def test_mul_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - sa = str(a) - sb = str(b) - sa_c = sa.encode('utf-8') - sb_c = sb.encode('utf-8') + sa, sb = str(a), str(b) + sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') res = mul_two(sa_c, sb_c, radix) if expected_result == None: expected_result = a * b return test("test_mul_two", res, [sa_c, sb_c, radix], expected_error, expected_result) def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): - sa = str(a) - sb = str(b) - sa_c = sa.encode('utf-8') - sb_c = sb.encode('utf-8') + sa, sb = str(a), str(b) + sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') try: res = div_two(sa_c, sb_c, radix) except: @@ -190,6 +174,17 @@ def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_res expected_result = a // b if b != 0 else None return test("test_div_two", res, [sa_c, sb_c, radix], expected_error, expected_result) + +def test_log(a = 0, base = 0, radix = 10, expected_error = E_None, expected_result = None): + args = [str(a), base, radix] + sa_c = args[0].encode('utf-8') + res = int_log(sa_c, base, radix) + + if expected_result == None: + expected_result = int(log(a, base)) + return test("test_log", res, args, expected_error, expected_result) + + # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # # The last two arguments in tests are the expected error and expected result. @@ -211,15 +206,26 @@ TESTS = { [ 1099243943008198766717263669950239669, 137638828577110581150675834234248871, 10, ] ], test_div_two: [ - [ 54321, 12345, 10, ], - [ 55431, 0, 10, E_Division_by_Zero, ], + [ 54321, 12345, 10, ], + [ 55431, 0, 10, E_Division_by_Zero, ], + ], + test_log: [ + [ 3192, 1, 10, E_Invalid_Argument, ":log:log(a, base):"], + [ -1234, 2, 10, E_Math_Domain_Error, ":log:log(a, base):"], + [ 0, 2, 10, E_Math_Domain_Error, ":log:log(a, base):"], + [ 1024, 2, 10, ], ], } TOTAL_TIME = 0 +total_passes = 0 total_failures = 0 + if __name__ == '__main__': + + test_log(1234, 2, 10) + print("---- core:math/big tests ----") print() @@ -240,6 +246,7 @@ if __name__ == '__main__': if res: count_pass += 1 + total_passes += 1 else: count_fail += 1 total_failures += 1 @@ -251,19 +258,25 @@ if __name__ == '__main__': print("---- core:math/big with two random {bits:,} bit numbers ----".format(bits=BITS)) print() - for test_proc in [test_add_two, test_sub_two, test_mul_two, test_div_two]: + for test_proc in [test_add_two, test_sub_two, test_mul_two, test_div_two, test_log]: count_pass = 0 count_fail = 0 TIMINGS = {} for i in range(ITERATIONS): a = randint(0, 1 << BITS) - b = randint(0, 1 << BITS) - res = None - # We've already tested division by zero above. - if b == 0 and test_proc == test_div_two: - b = b + 1 + if test_proc == test_div_two: + # We've already tested division by zero above. + b = randint(1, 1 << BITS) + elif test_proc == test_log: + # We've already tested log's domain errors. + a = randint(1, 1 << BITS) + b = randint(2, 1 << 60) + else: + b = randint(0, 1 << BITS) + + res = None start = time.perf_counter() res = test_proc(a, b) @@ -277,13 +290,17 @@ if __name__ == '__main__': if res: count_pass += 1 + total_passes += 1 else: count_fail += 1 total_failures += 1 print("{name}: {count_pass:,} passes and {count_fail:,} failures in {timing:.3f} ms.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000)) - print("\ntotal: {0:.3f} ms".format(TOTAL_TIME * 1_000)) + print() + print("---- THE END ----") + print() + print("total: {count_pass:,} passes and {count_fail:,} failures in {timing:.3f} ms.".format(count_pass=total_passes, count_fail=total_failures, timing=TOTAL_TIME * 1_000)) if total_failures: - os.exit(1) \ No newline at end of file + exit(1) \ No newline at end of file From 961adfedd9adee86be6bb5984145bdfe8d6f8fe2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 29 Jul 2021 21:33:45 +0200 Subject: [PATCH 053/105] big: Test negative inputs as well. --- core/math/big/test.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/math/big/test.py b/core/math/big/test.py index f325d7787..1700f42aa 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -171,7 +171,15 @@ def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_res print("Exception with arguments:", a, b, radix) return False if expected_result == None: - expected_result = a // b if b != 0 else None + # + # We don't round the division results, so if one component is negative, we're off by one. + # + if a < 0 and b > 0: + expected_result = int(-(abs(a) / b)) + elif b < 0 and a > 0: + expected_result = int(-(a / abs((b)))) + else: + expected_result = a // b if b != 0 else None return test("test_div_two", res, [sa_c, sb_c, radix], expected_error, expected_result) @@ -264,11 +272,13 @@ if __name__ == '__main__': TIMINGS = {} for i in range(ITERATIONS): - a = randint(0, 1 << BITS) + a = randint(-(1 << BITS), 1 << BITS) + b = randint(-(1 << BITS), 1 << BITS) if test_proc == test_div_two: # We've already tested division by zero above. - b = randint(1, 1 << BITS) + if b == 0: + b == 42 elif test_proc == test_log: # We've already tested log's domain errors. a = randint(1, 1 << BITS) From 2179cc2bc79895d7966163cff3d86d9ed26628f3 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 30 Jul 2021 16:45:41 +0200 Subject: [PATCH 054/105] big: Improved test driver. --- core/math/big/build.bat | 8 +- core/math/big/common.odin | 2 +- core/math/big/test.py | 175 ++++++++++++++++++++------------------ 3 files changed, 100 insertions(+), 85 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index de6003238..69dfe7995 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,6 +1,8 @@ @echo off :odin run . -vet -odin build . -build-mode:shared -show-timings -o:speed -:odin build . -build-mode:shared -show-timings +:odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules +odin build . -build-mode:shared -show-timings -o:size -use-separate-modules +:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -python test.py \ No newline at end of file +python test.py +:del big.* \ No newline at end of file diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 59e76a9aa..f030f111a 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -49,7 +49,7 @@ Int :: struct { /* Errors are a strict superset of runtime.Allocation_Error. */ -Error :: enum byte { +Error :: enum int { None = 0, Out_Of_Memory = 1, Invalid_Pointer = 2, diff --git a/core/math/big/test.py b/core/math/big/test.py index 1700f42aa..94369018c 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -4,12 +4,36 @@ from random import * import os import platform import time +from enum import Enum # -# Fast tests? +# How many iterations of each random test do we want to run? +# +BITS_AND_ITERATIONS = [ + ( 120, 10_000), + ( 1_200, 1_000), + ( 4_096, 100), + (12_000, 10), +] + +# +# For timed tests we budget a second per `n` bits and iterate until we hit that time. +# Otherwise, we specify the number of iterations per bit depth in BITS_AND_ITERATIONS. +# +TIMED_TESTS = False +TIMED_BITS_PER_SECOND = 20_000 + +# +# If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. +# See below. # FAST_TESTS = True +if FAST_TESTS: + for k in range(len(BITS_AND_ITERATIONS)): + b, i = BITS_AND_ITERATIONS[k] + BITS_AND_ITERATIONS[k] = (b, i // 10 if i >= 100 else 5) + # # Where is the DLL? If missing, build using: `odin build . -build-mode:shared` # @@ -23,44 +47,34 @@ else: print("Platform is unsupported.") exit(1) -# -# How many iterations of each random test do we want to run? -# -BITS_AND_ITERATIONS = [ - ( 120, 10_000), - ( 1_200, 1_000), - ( 4_096, 100), - (12_000, 10), -] -# -# Fast tests? -# -if FAST_TESTS: - for k in range(len(BITS_AND_ITERATIONS)): - b, i = BITS_AND_ITERATIONS[k] - BITS_AND_ITERATIONS[k] = (b, i // 10 if i >= 100 else 5) +TOTAL_TIME = 0 +UNTIL_TIME = 0 +UNTIL_ITERS = 0 -# -# Result values will be passed in a struct { res: cstring, err: Error } -# -class Res(Structure): - _fields_ = [("res", c_char_p), ("err", c_byte)] +def we_iterate(): + if TIMED_TESTS: + return TOTAL_TIME < UNTIL_TIME + else: + global UNTIL_ITERS + UNTIL_ITERS -= 1 + return UNTIL_ITERS != -1 # # Error enum values # -E_None = 0 -E_Out_Of_Memory = 1 -E_Invalid_Pointer = 2 -E_Invalid_Argument = 3 -E_Unknown_Error = 4 -E_Max_Iterations_Reached = 5 -E_Buffer_Overflow = 6 -E_Integer_Overflow = 7 -E_Division_by_Zero = 8 -E_Math_Domain_Error = 9 -E_Unimplemented = 127 +class Error(Enum): + Okay = 0 + Out_Of_Memory = 1 + Invalid_Pointer = 2 + Invalid_Argument = 3 + Unknown_Error = 4 + Max_Iterations_Reached = 5 + Buffer_Overflow = 6 + Integer_Overflow = 7 + Division_by_Zero = 8 + Math_Domain_Error = 9 + Unimplemented = 127 # # Set up exported procedures @@ -77,56 +91,44 @@ def load(export_name, args, res): export_name.restype = res return export_name +# +# Result values will be passed in a struct { res: cstring, err: Error } +# +class Res(Structure): + _fields_ = [("res", c_char_p), ("err", c_uint64)] + error_string = load(l.test_error_string, [c_byte], c_char_p) -# -# res = a + b, err -# add_two = load(l.test_add_two, [c_char_p, c_char_p, c_longlong], Res) - -# -# res = a - b, err -# sub_two = load(l.test_sub_two, [c_char_p, c_char_p, c_longlong], Res) - -# -# res = a * b, err -# mul_two = load(l.test_mul_two, [c_char_p, c_char_p, c_longlong], Res) - -# -# res = a / b, err -# div_two = load(l.test_div_two, [c_char_p, c_char_p, c_longlong], Res) - -# -# res = log(a, base) -# int_log = load(l.test_log, [c_char_p, c_longlong, c_longlong], Res) -def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_result = ""): +def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = ""): passed = True r = None + err = Error(res.err) - if res.err != expected_error: - error_type = error_string(res.err).decode('utf-8') + if err != expected_error: error_loc = res.res.decode('utf-8') + error = "{}: {} in '{}'".format(test_name, err, error_loc) - error = "{}: '{}' error in '{}'".format(test_name, error_type, error_loc) if len(param): error += " with params {}".format(param) print(error, flush=True) passed = False - elif res.err == E_None: + elif err == Error.Okay: + r = None try: r = res.res.decode('utf-8') + r = int(res.res, 10) except: pass - r = eval(res.res) if r != expected_result: error = "{}: Result was '{}', expected '{}'".format(test_name, r, expected_result) if len(param): @@ -135,34 +137,40 @@ def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_re print(error, flush=True) passed = False + if not passed: + exit() + return passed -def test_add_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): +def test_add_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): args = [str(a), str(b), radix] sa_c, sb_c = args[0].encode('utf-8'), args[1].encode('utf-8') res = add_two(sa_c, sb_c, radix) - if expected_result == None: + expected_result = None + if expected_error == Error.Okay: expected_result = a + b return test("test_add_two", res, args, expected_error, expected_result) -def test_sub_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): +def test_sub_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') res = sub_two(sa_c, sb_c, radix) - if expected_result == None: + expected_result = None + if expected_error == Error.Okay: expected_result = a - b return test("test_sub_two", res, [sa_c, sb_c, radix], expected_error, expected_result) -def test_mul_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): +def test_mul_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') res = mul_two(sa_c, sb_c, radix) - if expected_result == None: + expected_result = None + if expected_error == Error.Okay: expected_result = a * b return test("test_mul_two", res, [sa_c, sb_c, radix], expected_error, expected_result) -def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None): +def test_div_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') try: @@ -170,7 +178,8 @@ def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_res except: print("Exception with arguments:", a, b, radix) return False - if expected_result == None: + expected_result = None + if expected_error == Error.Okay: # # We don't round the division results, so if one component is negative, we're off by one. # @@ -183,12 +192,13 @@ def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_res return test("test_div_two", res, [sa_c, sb_c, radix], expected_error, expected_result) -def test_log(a = 0, base = 0, radix = 10, expected_error = E_None, expected_result = None): +def test_log(a = 0, base = 0, radix = 10, expected_error = Error.Okay): args = [str(a), base, radix] sa_c = args[0].encode('utf-8') res = int_log(sa_c, base, radix) - if expected_result == None: + expected_result = None + if expected_error == Error.Okay: expected_result = int(log(a, base)) return test("test_log", res, args, expected_error, expected_result) @@ -204,28 +214,29 @@ def test_log(a = 0, base = 0, radix = 10, expected_error = E_None, expected_resu TESTS = { test_add_two: [ [ 1234, 5432, 10, ], - [ 1234, 5432, 110, E_Invalid_Argument, ], + [ 1234, 5432, 110, Error.Invalid_Argument], ], test_sub_two: [ [ 1234, 5432, 10, ], ], test_mul_two: [ [ 1234, 5432, 10, ], - [ 1099243943008198766717263669950239669, 137638828577110581150675834234248871, 10, ] + [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7, 10, ] ], test_div_two: [ [ 54321, 12345, 10, ], - [ 55431, 0, 10, E_Division_by_Zero, ], + [ 55431, 0, 10, Error.Division_by_Zero], ], test_log: [ - [ 3192, 1, 10, E_Invalid_Argument, ":log:log(a, base):"], - [ -1234, 2, 10, E_Math_Domain_Error, ":log:log(a, base):"], - [ 0, 2, 10, E_Math_Domain_Error, ":log:log(a, base):"], + [ 3192, 1, 10, Error.Invalid_Argument], + [ -1234, 2, 10, Error.Math_Domain_Error], + [ 0, 2, 10, Error.Math_Domain_Error], [ 1024, 2, 10, ], ], } -TOTAL_TIME = 0 +RANDOM_TESTS = [test_add_two, test_sub_two, test_mul_two, test_div_two, test_log] + total_passes = 0 total_failures = 0 @@ -266,12 +277,16 @@ if __name__ == '__main__': print("---- core:math/big with two random {bits:,} bit numbers ----".format(bits=BITS)) print() - for test_proc in [test_add_two, test_sub_two, test_mul_two, test_div_two, test_log]: + for test_proc in RANDOM_TESTS: count_pass = 0 count_fail = 0 TIMINGS = {} - for i in range(ITERATIONS): + UNTIL_ITERS = ITERATIONS + UNTIL_TIME = TOTAL_TIME + BITS / TIMED_BITS_PER_SECOND + # We run each test for a second per 20k bits + + while we_iterate(): a = randint(-(1 << BITS), 1 << BITS) b = randint(-(1 << BITS), 1 << BITS) @@ -299,11 +314,9 @@ if __name__ == '__main__': TIMINGS[test_proc] += diff if res: - count_pass += 1 - total_passes += 1 + count_pass += 1; total_passes += 1 else: - count_fail += 1 - total_failures += 1 + count_fail += 1; total_failures += 1 print("{name}: {count_pass:,} passes and {count_fail:,} failures in {timing:.3f} ms.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000)) From f12672727d73cdcb5df940e62aca5aab8ceaf197 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 30 Jul 2021 18:55:37 +0200 Subject: [PATCH 055/105] big: Add `test_pow` and some more switches. --- core/math/big/test.odin | 68 ++++++++++++------- core/math/big/test.py | 140 +++++++++++++++++++++++++--------------- 2 files changed, 131 insertions(+), 77 deletions(-) diff --git a/core/math/big/test.odin b/core/math/big/test.odin index d05d3ecaa..1ce653023 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -26,53 +26,53 @@ PyRes :: struct { return strings.clone_to_cstring(es[err], context.temp_allocator); } -@export test_add_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { +@export test_add_two :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":add_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":add_two:atoi(b):", err=err}; } - if err = add(sum, aa, bb); err != .None { return PyRes{res=":add_two:add(sum,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":add_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":add_two:atoi(b):", err=err}; } + if err = add(sum, aa, bb); err != .None { return PyRes{res=":add_two:add(sum,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(sum, i8(radix), context.temp_allocator); + r, err = int_itoa_cstring(sum, 10, context.temp_allocator); if err != .None { return PyRes{res=":add_two:itoa(sum):", err=err}; } return PyRes{res = r, err = .None}; } -@export test_sub_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { +@export test_sub_two :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":sub_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":sub_two:atoi(b):", err=err}; } - if err = sub(sum, aa, bb); err != .None { return PyRes{res=":sub_two:sub(sum,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":sub_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":sub_two:atoi(b):", err=err}; } + if err = sub(sum, aa, bb); err != .None { return PyRes{res=":sub_two:sub(sum,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(sum, i8(radix), context.temp_allocator); + r, err = int_itoa_cstring(sum, 10, context.temp_allocator); if err != .None { return PyRes{res=":sub_two:itoa(sum):", err=err}; } return PyRes{res = r, err = .None}; } -@export test_mul_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { +@export test_mul_two :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, product := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, product); - if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":mul_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":mul_two:atoi(b):", err=err}; } - if err = mul(product, aa, bb); err != .None { return PyRes{res=":mul_two:mul(product,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":mul_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":mul_two:atoi(b):", err=err}; } + if err = mul(product, aa, bb); err != .None { return PyRes{res=":mul_two:mul(product,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(product, i8(radix), context.temp_allocator); + r, err = int_itoa_cstring(product, 10, context.temp_allocator); if err != .None { return PyRes{res=":mul_two:itoa(product):", err=err}; } return PyRes{res = r, err = .None}; } @@ -80,19 +80,19 @@ PyRes :: struct { /* NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient. */ -@export test_div_two :: proc "c" (a, b: cstring, radix := int(10)) -> (res: PyRes) { +@export test_div_two :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, quotient := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, quotient); - if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":div_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), i8(radix)); err != .None { return PyRes{res=":div_two:atoi(b):", err=err}; } - if err = div(quotient, aa, bb); err != .None { return PyRes{res=":div_two:div(quotient,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":div_two:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":div_two:atoi(b):", err=err}; } + if err = div(quotient, aa, bb); err != .None { return PyRes{res=":div_two:div(quotient,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(quotient, i8(radix), context.temp_allocator); + r, err = int_itoa_cstring(quotient, 10, context.temp_allocator); if err != .None { return PyRes{res=":div_two:itoa(quotient):", err=err}; } return PyRes{res = r, err = .None}; } @@ -101,7 +101,7 @@ PyRes :: struct { /* res = log(a, base) */ -@export test_log :: proc "c" (a: cstring, base := DIGIT(2), radix := int(10)) -> (res: PyRes) { +@export test_log :: proc "c" (a: cstring, base := DIGIT(2)) -> (res: PyRes) { context = runtime.default_context(); err: Error; l: int; @@ -109,8 +109,8 @@ PyRes :: struct { aa := &Int{}; defer destroy(aa); - if err = atoi(aa, string(a), i8(radix)); err != .None { return PyRes{res=":log:atoi(a):", err=err}; } - if l, err = log(aa, base); err != .None { return PyRes{res=":log:log(a, base):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":log:atoi(a):", err=err}; } + if l, err = log(aa, base); err != .None { return PyRes{res=":log:log(a, base):", err=err}; } zero(aa); aa.digit[0] = DIGIT(l) & _MASK; @@ -119,7 +119,27 @@ PyRes :: struct { clamp(aa); r: cstring; - r, err = int_itoa_cstring(aa, i8(radix), context.temp_allocator); + r, err = int_itoa_cstring(aa, 10, context.temp_allocator); if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } + +/* + dest = base^power +*/ +@export test_pow :: proc "c" (base: cstring, power := int(2)) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + l: int; + + dest, bb := &Int{}, &Int{}; + defer destroy(dest, bb); + + if err = atoi(bb, string(base), 10); err != .None { return PyRes{res=":pow:atoi(base):", err=err}; } + if err = pow(dest, bb, power); err != .None { return PyRes{res=":pow:pow(dest, base, power):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(dest, 10, context.temp_allocator); + if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} \ No newline at end of file diff --git a/core/math/big/test.py b/core/math/big/test.py index 94369018c..8558f20ae 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -6,6 +6,30 @@ import platform import time from enum import Enum +# +# Normally, we report the number of passes and fails. +# With EXIT_ON_FAIL set, we exit at the first fail. +# +EXIT_ON_FAIL = False + +# +# We skip randomized tests altogether if NO_RANDOM_TESTS is set. +# +NO_RANDOM_TESTS = False + +# +# If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. +# See below. +# +FAST_TESTS = True + +# +# For timed tests we budget a second per `n` bits and iterate until we hit that time. +# Otherwise, we specify the number of iterations per bit depth in BITS_AND_ITERATIONS. +# +TIMED_TESTS = False +TIMED_BITS_PER_SECOND = 20_000 + # # How many iterations of each random test do we want to run? # @@ -16,24 +40,14 @@ BITS_AND_ITERATIONS = [ (12_000, 10), ] -# -# For timed tests we budget a second per `n` bits and iterate until we hit that time. -# Otherwise, we specify the number of iterations per bit depth in BITS_AND_ITERATIONS. -# -TIMED_TESTS = False -TIMED_BITS_PER_SECOND = 20_000 - -# -# If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. -# See below. -# -FAST_TESTS = True - if FAST_TESTS: for k in range(len(BITS_AND_ITERATIONS)): b, i = BITS_AND_ITERATIONS[k] BITS_AND_ITERATIONS[k] = (b, i // 10 if i >= 100 else 5) +if NO_RANDOM_TESTS: + BITS_AND_ITERATIONS = [] + # # Where is the DLL? If missing, build using: `odin build . -build-mode:shared` # @@ -99,13 +113,13 @@ class Res(Structure): error_string = load(l.test_error_string, [c_byte], c_char_p) -add_two = load(l.test_add_two, [c_char_p, c_char_p, c_longlong], Res) -sub_two = load(l.test_sub_two, [c_char_p, c_char_p, c_longlong], Res) -mul_two = load(l.test_mul_two, [c_char_p, c_char_p, c_longlong], Res) -div_two = load(l.test_div_two, [c_char_p, c_char_p, c_longlong], Res) - -int_log = load(l.test_log, [c_char_p, c_longlong, c_longlong], Res) +add_two = load(l.test_add_two, [c_char_p, c_char_p], Res) +sub_two = load(l.test_sub_two, [c_char_p, c_char_p], Res) +mul_two = load(l.test_mul_two, [c_char_p, c_char_p], Res) +div_two = load(l.test_div_two, [c_char_p, c_char_p], Res) +int_log = load(l.test_log, [c_char_p, c_longlong], Res) +int_pow = load(l.test_pow, [c_char_p, c_longlong], Res) def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = ""): passed = True @@ -137,46 +151,45 @@ def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expecte print(error, flush=True) passed = False - if not passed: - exit() + if EXIT_ON_FAIL and not passed: exit(res.err) return passed -def test_add_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): - args = [str(a), str(b), radix] +def test_add_two(a = 0, b = 0, expected_error = Error.Okay): + args = [str(a), str(b)] sa_c, sb_c = args[0].encode('utf-8'), args[1].encode('utf-8') - res = add_two(sa_c, sb_c, radix) + res = add_two(sa_c, sb_c) expected_result = None if expected_error == Error.Okay: expected_result = a + b return test("test_add_two", res, args, expected_error, expected_result) -def test_sub_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): +def test_sub_two(a = 0, b = 0, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') - res = sub_two(sa_c, sb_c, radix) + res = sub_two(sa_c, sb_c) expected_result = None if expected_error == Error.Okay: expected_result = a - b - return test("test_sub_two", res, [sa_c, sb_c, radix], expected_error, expected_result) + return test("test_sub_two", res, [sa_c, sb_c], expected_error, expected_result) -def test_mul_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): +def test_mul_two(a = 0, b = 0, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') - res = mul_two(sa_c, sb_c, radix) + res = mul_two(sa_c, sb_c) expected_result = None if expected_error == Error.Okay: expected_result = a * b - return test("test_mul_two", res, [sa_c, sb_c, radix], expected_error, expected_result) + return test("test_mul_two", res, [sa_c, sb_c], expected_error, expected_result) -def test_div_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): +def test_div_two(a = 0, b = 0, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') try: - res = div_two(sa_c, sb_c, radix) + res = div_two(sa_c, sb_c) except: - print("Exception with arguments:", a, b, radix) + print("Exception with arguments:", a, b) return False expected_result = None if expected_error == Error.Okay: @@ -189,19 +202,31 @@ def test_div_two(a = 0, b = 0, radix = 10, expected_error = Error.Okay): expected_result = int(-(a / abs((b)))) else: expected_result = a // b if b != 0 else None - return test("test_div_two", res, [sa_c, sb_c, radix], expected_error, expected_result) + return test("test_div_two", res, [sa_c, sb_c], expected_error, expected_result) -def test_log(a = 0, base = 0, radix = 10, expected_error = Error.Okay): - args = [str(a), base, radix] +def test_log(a = 0, base = 0, expected_error = Error.Okay): + args = [str(a), base] sa_c = args[0].encode('utf-8') - res = int_log(sa_c, base, radix) + res = int_log(sa_c, base) expected_result = None if expected_error == Error.Okay: expected_result = int(log(a, base)) return test("test_log", res, args, expected_error, expected_result) +def test_pow(base = 0, power = 0, expected_error = Error.Okay): + args = [str(base), power] + sa_c = args[0].encode('utf-8') + res = int_pow(sa_c, power) + + expected_result = None + if expected_error == Error.Okay: + if power < 0: + expected_result = 0 + else: + expected_result = pow(base, power) + return test("test_pow", res, args, expected_error, expected_result) # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # @@ -213,25 +238,33 @@ def test_log(a = 0, base = 0, radix = 10, expected_error = Error.Okay): TESTS = { test_add_two: [ - [ 1234, 5432, 10, ], - [ 1234, 5432, 110, Error.Invalid_Argument], + [ 1234, 5432], ], test_sub_two: [ - [ 1234, 5432, 10, ], + [ 1234, 5432], ], test_mul_two: [ - [ 1234, 5432, 10, ], - [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7, 10, ] + [ 1234, 5432], + [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7] ], test_div_two: [ - [ 54321, 12345, 10, ], - [ 55431, 0, 10, Error.Division_by_Zero], + [ 54321, 12345], + [ 55431, 0, Error.Division_by_Zero], ], test_log: [ - [ 3192, 1, 10, Error.Invalid_Argument], - [ -1234, 2, 10, Error.Math_Domain_Error], - [ 0, 2, 10, Error.Math_Domain_Error], - [ 1024, 2, 10, ], + [ 3192, 1, Error.Invalid_Argument], + [ -1234, 2, Error.Math_Domain_Error], + [ 0, 2, Error.Math_Domain_Error], + [ 1024, 2], + ], + test_pow: [ + [ 0, -1, Error.Math_Domain_Error ], # Math + [ 0, 0 ], # 1 + [ 0, 2 ], # 0 + [ 42, -1,], # 0 + [ 42, 1 ], # 1 + [ 42, 0 ], # 42 + [ 42, 2 ], # 42*42 ], } @@ -240,12 +273,13 @@ RANDOM_TESTS = [test_add_two, test_sub_two, test_mul_two, test_div_two, test_log total_passes = 0 total_failures = 0 +# Untimed warmup. +for test_proc in TESTS: + for t in TESTS[test_proc]: + res = test_proc(*t) if __name__ == '__main__': - - test_log(1234, 2, 10) - - print("---- core:math/big tests ----") + print("---- math/big tests ----") print() for test_proc in TESTS: @@ -274,7 +308,7 @@ if __name__ == '__main__': for BITS, ITERATIONS in BITS_AND_ITERATIONS: print() - print("---- core:math/big with two random {bits:,} bit numbers ----".format(bits=BITS)) + print("---- math/big with two random {bits:,} bit numbers ----".format(bits=BITS)) print() for test_proc in RANDOM_TESTS: From 7afd1b15a85ed9d7420e402e3adfe8a2ce2c4595 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 30 Jul 2021 19:28:44 +0200 Subject: [PATCH 056/105] big: `test_pow` for larger ints. --- core/math/big/build.bat | 1 + core/math/big/test.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 69dfe7995..ac533db16 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,4 +1,5 @@ @echo off +clear :odin run . -vet :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules odin build . -build-mode:shared -show-timings -o:size -use-separate-modules diff --git a/core/math/big/test.py b/core/math/big/test.py index 8558f20ae..8308c2d5e 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -15,7 +15,7 @@ EXIT_ON_FAIL = False # # We skip randomized tests altogether if NO_RANDOM_TESTS is set. # -NO_RANDOM_TESTS = False +NO_RANDOM_TESTS = False #True # # If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. @@ -225,7 +225,7 @@ def test_pow(base = 0, power = 0, expected_error = Error.Okay): if power < 0: expected_result = 0 else: - expected_result = pow(base, power) + expected_result = int(base**power) return test("test_pow", res, args, expected_error, expected_result) # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. @@ -265,14 +265,19 @@ TESTS = { [ 42, 1 ], # 1 [ 42, 0 ], # 42 [ 42, 2 ], # 42*42 + + ], } -RANDOM_TESTS = [test_add_two, test_sub_two, test_mul_two, test_div_two, test_log] - total_passes = 0 total_failures = 0 +RANDOM_TESTS = [ + test_add_two, test_sub_two, test_mul_two, test_div_two, + test_log, test_pow, +] + # Untimed warmup. for test_proc in TESTS: for t in TESTS[test_proc]: @@ -312,6 +317,8 @@ if __name__ == '__main__': print() for test_proc in RANDOM_TESTS: + if test_proc == test_pow and BITS > 1_200: continue + count_pass = 0 count_fail = 0 TIMINGS = {} @@ -332,6 +339,8 @@ if __name__ == '__main__': # We've already tested log's domain errors. a = randint(1, 1 << BITS) b = randint(2, 1 << 60) + elif test_proc == test_pow: + b = randint(1, 10) else: b = randint(0, 1 << BITS) From 149c7b88dfa8aa23d598ed5d4be4d938969a4772 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 31 Jul 2021 17:58:52 +0200 Subject: [PATCH 057/105] big: Fix `sqrt`, `div`, `add` with certain inputs. --- core/math/big/basic.odin | 30 ++--- core/math/big/build.bat | 1 - core/math/big/example.odin | 27 ++-- core/math/big/exp_log.odin | 87 ++++++++---- core/math/big/logical.odin | 10 +- core/math/big/test.odin | 159 ++++++++++++++++++---- core/math/big/test.py | 263 +++++++++++++++++++++++++++++++------ 7 files changed, 455 insertions(+), 122 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 7d0c467a8..fde11eba6 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -359,7 +359,7 @@ int_halve :: proc(dest, src: ^Int) -> (err: Error) { */ fwd_carry := DIGIT(0); - for x := dest.used; x >= 0; x -= 1 { + for x := dest.used - 1; x >= 0; x -= 1 { /* Get the carry for the next iteration. */ @@ -761,21 +761,16 @@ sqrmod :: proc { int_sqrmod, }; */ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if err = clear_if_uninitialized(x); err != .None { - return err; - } - if err = clear_if_uninitialized(y); err != .None { - return err; - } old_used, min_used, max_used, i: int; if x.used < y.used { x, y = y, x; + assert(x.used >= y.used); } - min_used = x.used; - max_used = y.used; + min_used = y.used; + max_used = x.used; old_used = dest.used; if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != .None { @@ -827,7 +822,6 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { Add remaining carry. */ dest.digit[i] = carry; - zero_count := old_used - dest.used; /* Zero remainder. @@ -1111,6 +1105,7 @@ _int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}; + c: int; goto_end: for { if err = one(tq); err != .None { break goto_end; } @@ -1121,20 +1116,21 @@ _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (er if err = abs(ta, numerator); err != .None { break goto_end; } if err = abs(tb, denominator); err != .None { break goto_end; } - if err = shl(tb, tb, n); err != .None { break goto_end; } if err = shl(tq, tq, n); err != .None { break goto_end; } - for ; n >= 0; n -= 1 { - c: int; - if c, err = cmp(tb, ta); err != .None { break goto_end; } - if c != 1 { + for n >= 0 { + if c, _ = cmp_mag(ta, tb); c == 0 || c == 1 { + // ta -= tb if err = sub(ta, ta, tb); err != .None { break goto_end; } - if err = add( q, tq, q); err != .None { break goto_end; } + // q += tq + if err = add( q, q, tq); err != .None { break goto_end; } } if err = shr1(tb, tb); err != .None { break goto_end; } if err = shr1(tq, tq); err != .None { break goto_end; } - } + + n -= 1; + } /* Now q == quotient and ta == remainder. diff --git a/core/math/big/build.bat b/core/math/big/build.bat index ac533db16..69dfe7995 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,5 +1,4 @@ @echo off -clear :odin run . -vet :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules odin build . -build-mode:shared -show-timings -o:size -use-separate-modules diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 5d30c85ae..0c8ef35e1 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -66,17 +66,26 @@ print :: proc(name: string, a: ^Int, base := i8(10)) { } demo :: proc() { - // err: Error; - // r := &rnd.Rand{}; - // rnd.init(r, 12345); + err: Error; + destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(destination, source, quotient, remainder, numerator, denominator); - // as := cstring("12341234"); - // bs := cstring("159671292010002348397151706347412301"); + err = atoi(source, "711456452774621215865929644892071691538299606591173717356248653735056872543694196490784640730887936656406546625676792022", 10); + print("src ", source); - // res := test_log(as, 2, 10); - // fmt.print(res); - // destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - // defer destroy(destination, source, quotient, remainder, numerator, denominator); + fmt.println("sqrt should be 843478780275248664696797599030708027195155136953848512749494"); + + fmt.println(); + err = sqrt(destination, source); + fmt.printf("sqrt returned: %v\n", err); + print("sqrt ", destination); + + err = atoi(denominator, "711456452774621215865929644892071691538299606591173717356248653735056872543694196490784640730887936656406546625676792022", 10); + err = root_n(quotient, denominator, 2); + fmt.printf("root_n(2) returned: %v\n", err); + print("root_n(2)", quotient); + + // fmt.println(); } main :: proc() { diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index 4c31a99d8..840aec8df 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -214,42 +214,53 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { This function is less generic than `root_n`, simpler and faster. */ int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(dest); err != .None { return err; } - if err = clear_if_uninitialized(src); err != .None { return err; } - /* Must be positive. */ - if src.sign == .Negative { return .Invalid_Argument; } + when true { + if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src); err != .None { return err; } - /* Easy out. If src is zero, so is dest. */ - if z, _ := is_zero(src); z { return zero(dest); } + /* Must be positive. */ + if src.sign == .Negative { return .Invalid_Argument; } - /* Set up temporaries. */ - t1, t2 := &Int{}, &Int{}; - defer destroy(t1, t2); + /* Easy out. If src is zero, so is dest. */ + if z, _ := is_zero(src); z { return zero(dest); } - if err = copy(t1, src); err != .None { return err; } - if err = zero(t2); err != .None { return err; } + /* Set up temporaries. */ + x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(x, y, t1, t2); - /* First approximation. Not very bad for large arguments. */ - if err = shr_digit(t1, t1.used / 2); err != .None { return err; } - /* t1 > 0 */ - if err = div(t2, src, t1); err != .None { return err; } - if err = add(t1, t1, t2); err != .None { return err; } - if err = shr(t1, t1, 1); err != .None { return err; } + count: int; + if count, err = count_bits(src); err != .None { return err; } - /* And now t1 > sqrt(arg). */ - for { - if err = div(t2, src, t1); err != .None { return err; } - if err = add(t1, t1, t2); err != .None { return err; } - if err = shr(t1, t1, 1); err != .None { return err; } - /* t1 >= sqrt(arg) >= t2 at this point */ + a, b := count >> 1, count & 1; + err = power_of_two(x, a+b); - cm, _ := cmp_mag(t1, t2); - if cm != 1 { break; } + iter := 0; + for { + iter += 1; + if iter > 100 { + swap(dest, x); + return .Max_Iterations_Reached; + } + /* + y = (x + n//x)//2 + */ + div(t1, src, x); + add(t2, t1, x); + shr(y, t2, 1); + + if c, _ := cmp(y, x); c == 0 || c == 1 { + swap(dest, x); + return .None; + } + swap(x, y); + } + + swap(dest, x); + return err; + } else { + // return root_n(dest, src, 2); } - - swap(dest, t1); - return err; } sqrt :: proc { int_sqrt, }; @@ -263,7 +274,7 @@ sqrt :: proc { int_sqrt, }; */ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { /* Fast path for n == 2 */ - if n == 2 { return sqrt(dest, src); } + // if n == 2 { return sqrt(dest, src); } /* Initialize dest + src if needed. */ if err = clear_if_uninitialized(dest); err != .None { return err; } @@ -321,6 +332,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { if err = power_of_two(t2, ilog2); err != .None { return err; } c: int; + iterations := 0; for { /* t1 = t2 */ if err = copy(t1, t2); err != .None { return err; } @@ -353,12 +365,23 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { break; } if c, err = cmp(t1, t2); c == 0 { break; } + iterations += 1; + if iterations == 101 { + return .Max_Iterations_Reached; + } } /* Result can be off by a few so check. */ /* Loop beneath can overshoot by one if found root is smaller than actual root. */ + iterations = 0; for { + if iterations == 101 { + return .Max_Iterations_Reached; + } + //fmt.printf("root_n iteration: %v\n", iterations); + iterations += 1; + if err = pow(t2, t1, n); err != .None { return err; } c, err = cmp(t2, a); @@ -372,8 +395,14 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } } + iterations = 0; /* Correct overshoot from above or from recurrence. */ for { + if iterations == 101 { + return .Max_Iterations_Reached; + } + iterations += 1; + if err = pow(t2, t1, n); err != .None { return err; } c, err = cmp(t2, a); diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 1d2ba0895..e721db603 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -285,7 +285,7 @@ int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int) -> (err: Err shift := DIGIT(_DIGIT_BITS - bits); carry := DIGIT(0); - for x := quotient.used; x >= 0; x -= 1 { + for x := quotient.used - 1; x >= 0; x -= 1 { /* Get the lower bits of this word in a temp. */ @@ -344,7 +344,7 @@ int_shr_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { } quotient.used -= digits; _zero_unused(quotient); - return .None; + return clamp(quotient); } shr_digit :: proc { int_shr_digit, }; @@ -446,16 +446,16 @@ int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { /* Increment the used by the shift amount then copy upwards. */ - quotient.used += digits; /* Much like `int_shr_digit`, this is implemented using a sliding window, except the window goes the other way around. */ - for x := quotient.used; x >= digits; x -= 1 { - quotient.digit[x] = quotient.digit[x - digits]; + for x := quotient.used; x > 0; x -= 1 { + quotient.digit[x+digits-1] = quotient.digit[x-1]; } + quotient.used += digits; mem.zero_slice(quotient.digit[:digits]); return .None; } diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 1ce653023..04635295d 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -26,74 +26,74 @@ PyRes :: struct { return strings.clone_to_cstring(es[err], context.temp_allocator); } -@export test_add_two :: proc "c" (a, b: cstring) -> (res: PyRes) { +@export test_add :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":add_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":add_two:atoi(b):", err=err}; } - if err = add(sum, aa, bb); err != .None { return PyRes{res=":add_two:add(sum,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":add:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":add:atoi(b):", err=err}; } + if err = add(sum, aa, bb); err != .None { return PyRes{res=":add:add(sum,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(sum, 10, context.temp_allocator); - if err != .None { return PyRes{res=":add_two:itoa(sum):", err=err}; } + if err != .None { return PyRes{res=":add:itoa(sum):", err=err}; } return PyRes{res = r, err = .None}; } -@export test_sub_two :: proc "c" (a, b: cstring) -> (res: PyRes) { +@export test_sub :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":sub_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":sub_two:atoi(b):", err=err}; } - if err = sub(sum, aa, bb); err != .None { return PyRes{res=":sub_two:sub(sum,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":sub:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":sub:atoi(b):", err=err}; } + if err = sub(sum, aa, bb); err != .None { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(sum, 10, context.temp_allocator); - if err != .None { return PyRes{res=":sub_two:itoa(sum):", err=err}; } + if err != .None { return PyRes{res=":sub:itoa(sum):", err=err}; } return PyRes{res = r, err = .None}; } -@export test_mul_two :: proc "c" (a, b: cstring) -> (res: PyRes) { +@export test_mul :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, product := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, product); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":mul_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":mul_two:atoi(b):", err=err}; } - if err = mul(product, aa, bb); err != .None { return PyRes{res=":mul_two:mul(product,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":mul:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":mul:atoi(b):", err=err}; } + if err = mul(product, aa, bb); err != .None { return PyRes{res=":mul:mul(product,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(product, 10, context.temp_allocator); - if err != .None { return PyRes{res=":mul_two:itoa(product):", err=err}; } + if err != .None { return PyRes{res=":mul:itoa(product):", err=err}; } return PyRes{res = r, err = .None}; } /* NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient. */ -@export test_div_two :: proc "c" (a, b: cstring) -> (res: PyRes) { +@export test_div :: proc "c" (a, b: cstring) -> (res: PyRes) { context = runtime.default_context(); err: Error; aa, bb, quotient := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, quotient); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":div_two:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":div_two:atoi(b):", err=err}; } - if err = div(quotient, aa, bb); err != .None { return PyRes{res=":div_two:div(quotient,a,b):", err=err}; } + if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":div:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":div:atoi(b):", err=err}; } + if err = div(quotient, aa, bb); err != .None { return PyRes{res=":div:div(quotient,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(quotient, 10, context.temp_allocator); - if err != .None { return PyRes{res=":div_two:itoa(quotient):", err=err}; } + if err != .None { return PyRes{res=":div:itoa(quotient):", err=err}; } return PyRes{res = r, err = .None}; } @@ -130,7 +130,6 @@ PyRes :: struct { @export test_pow :: proc "c" (base: cstring, power := int(2)) -> (res: PyRes) { context = runtime.default_context(); err: Error; - l: int; dest, bb := &Int{}, &Int{}; defer destroy(dest, bb); @@ -142,4 +141,120 @@ PyRes :: struct { r, err = int_itoa_cstring(dest, 10, context.temp_allocator); if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; -} \ No newline at end of file +} + +/* + dest = sqrt(src) +*/ +@export test_sqrt :: proc "c" (source: cstring) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + src := &Int{}; + defer destroy(src); + + if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":sqrt:atoi(src):", err=err}; } + if err = sqrt(src, src); err != .None { return PyRes{res=":sqrt:sqrt(src):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(src, 10, context.temp_allocator); + if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + + +/* + dest = shr_digit(src, digits) +*/ +@export test_shr_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + src := &Int{}; + defer destroy(src); + + if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shr_digit:atoi(src):", err=err}; } + if err = shr_digit(src, digits); err != .None { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(src, 10, context.temp_allocator); + if err != .None { return PyRes{res=":shr_digit:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + +/* + dest = shl_digit(src, digits) +*/ +@export test_shl_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + src := &Int{}; + defer destroy(src); + + if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shl_digit:atoi(src):", err=err}; } + if err = shl_digit(src, digits); err != .None { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(src, 10, context.temp_allocator); + if err != .None { return PyRes{res=":shl_digit:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + +/* + dest = shr(src, bits) +*/ +@export test_shr :: proc "c" (source: cstring, bits: int) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + src := &Int{}; + defer destroy(src); + + if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shr:atoi(src):", err=err}; } + if err = shr(src, src, bits); err != .None { return PyRes{res=":shr:shr(src, bits):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(src, 10, context.temp_allocator); + if err != .None { return PyRes{res=":shr:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + +/* + dest = shr_signed(src, bits) +*/ +@export test_shr_signed :: proc "c" (source: cstring, bits: int) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + src := &Int{}; + defer destroy(src); + + if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shr_signed:atoi(src):", err=err}; } + if err = shr_signed(src, src, bits); err != .None { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(src, 10, context.temp_allocator); + if err != .None { return PyRes{res=":shr_signed:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + +/* + dest = shl(src, bits) +*/ +@export test_shl :: proc "c" (source: cstring, bits: int) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + src := &Int{}; + defer destroy(src); + + if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shl:atoi(src):", err=err}; } + if err = shl(src, src, bits); err != .None { return PyRes{res=":shl:shl(src, bits):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(src, 10, context.temp_allocator); + if err != .None { return PyRes{res=":shl:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + diff --git a/core/math/big/test.py b/core/math/big/test.py index 8308c2d5e..956d75e1c 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -1,6 +1,6 @@ -from math import * from ctypes import * from random import * +import math import os import platform import time @@ -10,12 +10,14 @@ from enum import Enum # Normally, we report the number of passes and fails. # With EXIT_ON_FAIL set, we exit at the first fail. # +EXIT_ON_FAIL = True EXIT_ON_FAIL = False # # We skip randomized tests altogether if NO_RANDOM_TESTS is set. # -NO_RANDOM_TESTS = False #True +NO_RANDOM_TESTS = True +NO_RANDOM_TESTS = False # # If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. @@ -113,13 +115,23 @@ class Res(Structure): error_string = load(l.test_error_string, [c_byte], c_char_p) -add_two = load(l.test_add_two, [c_char_p, c_char_p], Res) -sub_two = load(l.test_sub_two, [c_char_p, c_char_p], Res) -mul_two = load(l.test_mul_two, [c_char_p, c_char_p], Res) -div_two = load(l.test_div_two, [c_char_p, c_char_p], Res) +add = load(l.test_add, [c_char_p, c_char_p], Res) +sub = load(l.test_sub, [c_char_p, c_char_p], Res) +mul = load(l.test_mul, [c_char_p, c_char_p], Res) +div = load(l.test_div, [c_char_p, c_char_p], Res) -int_log = load(l.test_log, [c_char_p, c_longlong], Res) -int_pow = load(l.test_pow, [c_char_p, c_longlong], Res) +# Powers and such +int_log = load(l.test_log, [c_char_p, c_longlong], Res) +int_pow = load(l.test_pow, [c_char_p, c_longlong], Res) +int_sqrt = load(l.test_sqrt, [c_char_p], Res) + +# Logical operations + +int_shl_digit = load(l.test_shl_digit, [c_char_p, c_longlong], Res) +int_shr_digit = load(l.test_shr_digit, [c_char_p, c_longlong], Res) +int_shl = load(l.test_shl, [c_char_p, c_longlong], Res) +int_shr = load(l.test_shr, [c_char_p, c_longlong], Res) +int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res) def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = ""): passed = True @@ -156,38 +168,38 @@ def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expecte return passed -def test_add_two(a = 0, b = 0, expected_error = Error.Okay): +def test_add(a = 0, b = 0, expected_error = Error.Okay): args = [str(a), str(b)] sa_c, sb_c = args[0].encode('utf-8'), args[1].encode('utf-8') - res = add_two(sa_c, sb_c) + res = add(sa_c, sb_c) expected_result = None if expected_error == Error.Okay: expected_result = a + b - return test("test_add_two", res, args, expected_error, expected_result) + return test("test_add", res, args, expected_error, expected_result) -def test_sub_two(a = 0, b = 0, expected_error = Error.Okay): +def test_sub(a = 0, b = 0, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') - res = sub_two(sa_c, sb_c) + res = sub(sa_c, sb_c) expected_result = None if expected_error == Error.Okay: expected_result = a - b - return test("test_sub_two", res, [sa_c, sb_c], expected_error, expected_result) + return test("test_sub", res, [sa_c, sb_c], expected_error, expected_result) -def test_mul_two(a = 0, b = 0, expected_error = Error.Okay): +def test_mul(a = 0, b = 0, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') - res = mul_two(sa_c, sb_c) + res = mul(sa_c, sb_c) expected_result = None if expected_error == Error.Okay: expected_result = a * b - return test("test_mul_two", res, [sa_c, sb_c], expected_error, expected_result) + return test("test_mul", res, [sa_c, sb_c], expected_error, expected_result) -def test_div_two(a = 0, b = 0, expected_error = Error.Okay): +def test_div(a = 0, b = 0, expected_error = Error.Okay): sa, sb = str(a), str(b) sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') try: - res = div_two(sa_c, sb_c) + res = div(sa_c, sb_c) except: print("Exception with arguments:", a, b) return False @@ -197,12 +209,12 @@ def test_div_two(a = 0, b = 0, expected_error = Error.Okay): # We don't round the division results, so if one component is negative, we're off by one. # if a < 0 and b > 0: - expected_result = int(-(abs(a) / b)) + expected_result = int(-(abs(a) // b)) elif b < 0 and a > 0: - expected_result = int(-(a / abs((b)))) + expected_result = int(-(a // abs((b)))) else: expected_result = a // b if b != 0 else None - return test("test_div_two", res, [sa_c, sb_c], expected_error, expected_result) + return test("test_div", res, [sa_c, sb_c], expected_error, expected_result) def test_log(a = 0, base = 0, expected_error = Error.Okay): @@ -212,7 +224,7 @@ def test_log(a = 0, base = 0, expected_error = Error.Okay): expected_result = None if expected_error == Error.Okay: - expected_result = int(log(a, base)) + expected_result = int(math.log(a, base)) return test("test_log", res, args, expected_error, expected_result) def test_pow(base = 0, power = 0, expected_error = Error.Okay): @@ -225,9 +237,108 @@ def test_pow(base = 0, power = 0, expected_error = Error.Okay): if power < 0: expected_result = 0 else: - expected_result = int(base**power) + # NOTE(Jeroen): Don't use `math.pow`, it's a floating point approximation. + # Use built-in `pow` or `a**b` instead. + expected_result = pow(base, power) return test("test_pow", res, args, expected_error, expected_result) +def test_sqrt(number = 0, expected_error = Error.Okay): + args = [str(number)] + sa_c = args[0].encode('utf-8') + try: + res = int_sqrt(sa_c) + except: + print("sqrt:", number) + + expected_result = None + if expected_error == Error.Okay: + if number < 0: + expected_result = 0 + else: + expected_result = int(math.isqrt(number)) + return test("test_sqrt", res, args, expected_error, expected_result) + +def root_n(number, root): + u, s = number, number + 1 + while u < s: + s = u + t = (root-1) * s + number // pow(s, root - 1) + u = t // root + return s + +def test_shl_digit(a = 0, digits = 0, expected_error = Error.Okay): + args = [str(a), digits] + sa_c = args[0].encode('utf-8') + res = int_shl_digit(sa_c, digits) + + expected_result = None + if expected_error == Error.Okay: + expected_result = a << (digits * 60) + return test("test_shl_digit", res, args, expected_error, expected_result) + +def test_shr_digit(a = 0, digits = 0, expected_error = Error.Okay): + args = [str(a), digits] + sa_c = args[0].encode('utf-8') + try: + res = int_shr_digit(sa_c, digits) + except: + print("int_shr_digit", a, digits) + exit() + + expected_result = None + if expected_error == Error.Okay: + if a < 0: + # Don't pass negative numbers. We have a shr_signed. + return False + else: + expected_result = a >> (digits * 60) + + return test("test_shr_digit", res, args, expected_error, expected_result) + +def test_shl(a = 0, bits = 0, expected_error = Error.Okay): + args = [str(a), bits] + sa_c = args[0].encode('utf-8') + res = int_shl(sa_c, bits) + + expected_result = None + if expected_error == Error.Okay: + expected_result = a << bits + return test("test_shl", res, args, expected_error, expected_result) + +def test_shr(a = 0, bits = 0, expected_error = Error.Okay): + args = [str(a), bits] + sa_c = args[0].encode('utf-8') + try: + res = int_shr(sa_c, bits) + except: + print("int_shr", a, bits) + exit() + + expected_result = None + if expected_error == Error.Okay: + if a < 0: + # Don't pass negative numbers. We have a shr_signed. + return False + else: + expected_result = a >> bits + + return test("test_shr", res, args, expected_error, expected_result) + +def test_shr_signed(a = 0, bits = 0, expected_error = Error.Okay): + args = [str(a), bits] + sa_c = args[0].encode('utf-8') + try: + res = int_shr_signed(sa_c, bits) + except: + print("int_shr_signed", a, bits) + exit() + + expected_result = None + if expected_error == Error.Okay: + expected_result = a >> bits + + return test("test_shr_signed", res, args, expected_error, expected_result) + # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # # The last two arguments in tests are the expected error and expected result. @@ -237,19 +348,20 @@ def test_pow(base = 0, power = 0, expected_error = Error.Okay): # You can override that by supplying an expected result as the last argument instead. TESTS = { - test_add_two: [ + test_add: [ [ 1234, 5432], ], - test_sub_two: [ + test_sub: [ [ 1234, 5432], ], - test_mul_two: [ + test_mul: [ [ 1234, 5432], [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7] ], - test_div_two: [ + test_div: [ [ 54321, 12345], [ 55431, 0, Error.Division_by_Zero], + [ 12980742146337069150589594264770969721, 4611686018427387904 ], ], test_log: [ [ 3192, 1, Error.Invalid_Argument], @@ -260,29 +372,87 @@ TESTS = { test_pow: [ [ 0, -1, Error.Math_Domain_Error ], # Math [ 0, 0 ], # 1 - [ 0, 2 ], # 0 - [ 42, -1,], # 0 - [ 42, 1 ], # 1 - [ 42, 0 ], # 42 - [ 42, 2 ], # 42*42 - - + [ 0, 2 ], # 0 + [ 42, -1,], # 0 + [ 42, 1 ], # 1 + [ 42, 0 ], # 42 + [ 42, 2 ], # 42*42 ], + test_sqrt: [ + [ -1, Error.Invalid_Argument, ], + [ 42, Error.Okay, ], + [ 12345678901234567890, Error.Okay, ], + [ 1298074214633706907132624082305024, Error.Okay, ], + ], + test_shl_digit: [ + [ 3192, 1 ], + [ 1298074214633706907132624082305024, 2 ], + [ 1024, 3 ], + ], + test_shr_digit: [ + [ 3680125442705055547392, 1 ], + [ 1725436586697640946858688965569256363112777243042596638790631055949824, 2 ], + [ 219504133884436710204395031992179571, 2 ], + ], + test_shl: [ + [ 3192, 1 ], + [ 1298074214633706907132624082305024, 2 ], + [ 1024, 3 ], + ], + test_shr: [ + [ 3680125442705055547392, 1 ], + [ 1725436586697640946858688965569256363112777243042596638790631055949824, 2 ], + [ 219504133884436710204395031992179571, 2 ], + ], + test_shr_signed: [ + [ -611105530635358368578155082258244262, 12 ], + [ -149195686190273039203651143129455, 12 ], + [ 611105530635358368578155082258244262, 12 ], + [ 149195686190273039203651143129455, 12 ], + ] } total_passes = 0 total_failures = 0 +# +# test_shr_signed also tests shr, so we're not going to test shr randomly. +# RANDOM_TESTS = [ - test_add_two, test_sub_two, test_mul_two, test_div_two, - test_log, test_pow, + test_add, test_sub, test_mul, test_div, + test_log, test_pow, test_sqrt, + test_shl_digit, test_shr_digit, test_shl, test_shr_signed, ] +SKIP_LARGE = [test_pow] +SKIP_LARGEST = [] # Untimed warmup. for test_proc in TESTS: for t in TESTS[test_proc]: res = test_proc(*t) + +def isqrt(x): + n = int(x) + a, b = divmod(n.bit_length(), 2) + print("isqrt({}), a: {}, b: {}". format(n, a, b)) + x = 2**(a+b) + print("initial: {}".format(x)) + i = 0 + while True: + # y = (x + n//x)//2 + t1 = n // x + t2 = x + t1 + t3 = t2 // 2 + y = (x + n//x)//2 + + i += 1 + print("iter {}\n\t x: {}\n\t y: {}\n\tt1: {}\n\tt2: {}\n\tsrc: {}".format(i, x, y, t1, t2, n)); + + if y >= x: + return x + x = y + if __name__ == '__main__': print("---- math/big tests ----") print() @@ -317,7 +487,8 @@ if __name__ == '__main__': print() for test_proc in RANDOM_TESTS: - if test_proc == test_pow and BITS > 1_200: continue + if BITS > 1_200 and test_proc in SKIP_LARGE: continue + if BITS > 4_096 and test_proc in SKIP_LARGEST: continue count_pass = 0 count_fail = 0 @@ -331,8 +502,10 @@ if __name__ == '__main__': a = randint(-(1 << BITS), 1 << BITS) b = randint(-(1 << BITS), 1 << BITS) - if test_proc == test_div_two: + if test_proc == test_div: # We've already tested division by zero above. + bits = int(BITS * 0.6) + b = randint(-(1 << bits), 1 << bits) if b == 0: b == 42 elif test_proc == test_log: @@ -341,6 +514,18 @@ if __name__ == '__main__': b = randint(2, 1 << 60) elif test_proc == test_pow: b = randint(1, 10) + elif test_proc == test_sqrt: + a = randint(1, 1 << BITS) + b = Error.Okay + elif test_proc == test_shl_digit: + b = randint(0, 10); + elif test_proc == test_shr_digit: + a = abs(a) + b = randint(0, 10); + elif test_proc == test_shl: + b = randint(0, min(BITS, 120)); + elif test_proc == test_shr_signed: + b = randint(0, min(BITS, 120)); else: b = randint(0, 1 << BITS) From db0196abc74b1ddf3ad7fa39d2143de2460b3500 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 31 Jul 2021 18:58:46 +0200 Subject: [PATCH 058/105] big: Test `root_n`. --- core/math/big/common.odin | 2 ++ core/math/big/exp_log.odin | 33 +++++++++------------- core/math/big/test.odin | 18 ++++++++++++ core/math/big/test.py | 58 ++++++++++++++++++++------------------ 4 files changed, 64 insertions(+), 47 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index f030f111a..32d7b938f 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -35,6 +35,8 @@ _DEFAULT_SQR_KARATSUBA_CUTOFF :: 120; _DEFAULT_MUL_TOOM_CUTOFF :: 350; _DEFAULT_SQR_TOOM_CUTOFF :: 400; +_MAX_ITERATIONS_ROOT_N :: 500; + Sign :: enum u8 { Zero_or_Positive = 0, Negative = 1, diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index 840aec8df..746470956 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -233,15 +233,9 @@ int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { if count, err = count_bits(src); err != .None { return err; } a, b := count >> 1, count & 1; - err = power_of_two(x, a+b); + if err = power_of_two(x, a+b); err != .None { return err; } - iter := 0; for { - iter += 1; - if iter > 100 { - swap(dest, x); - return .Max_Iterations_Reached; - } /* y = (x + n//x)//2 */ @@ -274,7 +268,7 @@ sqrt :: proc { int_sqrt, }; */ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { /* Fast path for n == 2 */ - // if n == 2 { return sqrt(dest, src); } + if n == 2 { return sqrt(dest, src); } /* Initialize dest + src if needed. */ if err = clear_if_uninitialized(dest); err != .None { return err; } @@ -366,7 +360,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } if c, err = cmp(t1, t2); c == 0 { break; } iterations += 1; - if iterations == 101 { + if iterations == _MAX_ITERATIONS_ROOT_N { return .Max_Iterations_Reached; } } @@ -376,12 +370,6 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { iterations = 0; for { - if iterations == 101 { - return .Max_Iterations_Reached; - } - //fmt.printf("root_n iteration: %v\n", iterations); - iterations += 1; - if err = pow(t2, t1, n); err != .None { return err; } c, err = cmp(t2, a); @@ -393,16 +381,16 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } else { break; } + + iterations += 1; + if iterations == _MAX_ITERATIONS_ROOT_N { + return .Max_Iterations_Reached; + } } iterations = 0; /* Correct overshoot from above or from recurrence. */ for { - if iterations == 101 { - return .Max_Iterations_Reached; - } - iterations += 1; - if err = pow(t2, t1, n); err != .None { return err; } c, err = cmp(t2, a); @@ -411,6 +399,11 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } else { break; } + + iterations += 1; + if iterations == _MAX_ITERATIONS_ROOT_N { + return .Max_Iterations_Reached; + } } /* Set the result. */ diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 04635295d..ed95669f5 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -162,6 +162,24 @@ PyRes :: struct { return PyRes{res = r, err = .None}; } +/* + dest = root_n(src, power) +*/ +@export test_root_n :: proc "c" (source: cstring, power: int) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + src := &Int{}; + defer destroy(src); + + if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":root_n:atoi(src):", err=err}; } + if err = root_n(src, src, power); err != .None { return PyRes{res=":root_n:root_n(src):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(src, 10, context.temp_allocator); + if err != .None { return PyRes{res=":root_n:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} /* dest = shr_digit(src, digits) diff --git a/core/math/big/test.py b/core/math/big/test.py index 956d75e1c..645383b0f 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -121,9 +121,10 @@ mul = load(l.test_mul, [c_char_p, c_char_p], Res) div = load(l.test_div, [c_char_p, c_char_p], Res) # Powers and such -int_log = load(l.test_log, [c_char_p, c_longlong], Res) -int_pow = load(l.test_pow, [c_char_p, c_longlong], Res) -int_sqrt = load(l.test_sqrt, [c_char_p], Res) +int_log = load(l.test_log, [c_char_p, c_longlong], Res) +int_pow = load(l.test_pow, [c_char_p, c_longlong], Res) +int_sqrt = load(l.test_sqrt, [c_char_p], Res) +int_root_n = load(l.test_root_n, [c_char_p, c_longlong], Res) # Logical operations @@ -266,6 +267,23 @@ def root_n(number, root): u = t // root return s +def test_root_n(number = 0, root = 0, expected_error = Error.Okay): + args = [str(number), root] + sa_c = args[0].encode('utf-8') + try: + res = int_root_n(sa_c, root) + except: + print("root_n:", number, root) + + expected_result = None + if expected_error == Error.Okay: + if number < 0: + expected_result = 0 + else: + expected_result = root_n(number, root) + + return test("test_root_n", res, args, expected_error, expected_result) + def test_shl_digit(a = 0, digits = 0, expected_error = Error.Okay): args = [str(a), digits] sa_c = args[0].encode('utf-8') @@ -384,6 +402,9 @@ TESTS = { [ 12345678901234567890, Error.Okay, ], [ 1298074214633706907132624082305024, Error.Okay, ], ], + test_root_n: [ + [ 1298074214633706907132624082305024, 2, Error.Okay, ], + ], test_shl_digit: [ [ 3192, 1 ], [ 1298074214633706907132624082305024, 2 ], @@ -420,10 +441,12 @@ total_failures = 0 # RANDOM_TESTS = [ test_add, test_sub, test_mul, test_div, - test_log, test_pow, test_sqrt, + test_log, test_pow, test_sqrt, test_root_n, test_shl_digit, test_shr_digit, test_shl, test_shr_signed, ] -SKIP_LARGE = [test_pow] +SKIP_LARGE = [ + test_pow, test_root_n, +] SKIP_LARGEST = [] # Untimed warmup. @@ -431,28 +454,6 @@ for test_proc in TESTS: for t in TESTS[test_proc]: res = test_proc(*t) - -def isqrt(x): - n = int(x) - a, b = divmod(n.bit_length(), 2) - print("isqrt({}), a: {}, b: {}". format(n, a, b)) - x = 2**(a+b) - print("initial: {}".format(x)) - i = 0 - while True: - # y = (x + n//x)//2 - t1 = n // x - t2 = x + t1 - t3 = t2 // 2 - y = (x + n//x)//2 - - i += 1 - print("iter {}\n\t x: {}\n\t y: {}\n\tt1: {}\n\tt2: {}\n\tsrc: {}".format(i, x, y, t1, t2, n)); - - if y >= x: - return x - x = y - if __name__ == '__main__': print("---- math/big tests ----") print() @@ -517,6 +518,9 @@ if __name__ == '__main__': elif test_proc == test_sqrt: a = randint(1, 1 << BITS) b = Error.Okay + elif test_proc == test_root_n: + a = randint(1, 1 << BITS) + b = randint(1, 10); elif test_proc == test_shl_digit: b = randint(0, 10); elif test_proc == test_shr_digit: From e80ac183246a1481926f838b5f1e5588410c2248 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 31 Jul 2021 23:25:59 +0200 Subject: [PATCH 059/105] big: Add `factorial`, have tests use hex strings. --- core/math/big/basic.odin | 91 +++++++++++++++++++++++++- core/math/big/build.bat | 4 +- core/math/big/common.odin | 6 ++ core/math/big/example.odin | 41 +++++++----- core/math/big/radix.odin | 4 +- core/math/big/test.odin | 60 ++++++++--------- core/math/big/test.py | 130 ++++++++++++++----------------------- 7 files changed, 201 insertions(+), 135 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index fde11eba6..1ba5807f4 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -749,6 +749,30 @@ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { } sqrmod :: proc { int_sqrmod, }; + +int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { + if n < 0 { return .Invalid_Argument; } + + i := DIGIT(len(_factorial_table)); + if n < i { + return set(res, _factorial_table[n]); + } + + a := &Int{}; + defer destroy(a); + + if err = set( a, i - 1); err != .None { return err; } + if err = set(res, _factorial_table[i - 1]); err != .None { return err; } + + for { + if err = mul(res, res, DIGIT(i)); err != .None || i == n { return err; } + i += 1; + } + + return .None; +} +factorial :: proc { int_factorial, }; + /* ========================== Low-level routines @@ -1237,4 +1261,69 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain } destroy(q); return remainder, .None; -} \ No newline at end of file +} + + +when size_of(rawptr) == 8 { + _factorial_table := [35]_WORD{ +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, +/* f(21): */ 51_090_942_171_709_440_000, +/* f(22): */ 1_124_000_727_777_607_680_000, +/* f(23): */ 25_852_016_738_884_976_640_000, +/* f(24): */ 620_448_401_733_239_439_360_000, +/* f(25): */ 15_511_210_043_330_985_984_000_000, +/* f(26): */ 403_291_461_126_605_635_584_000_000, +/* f(27): */ 10_888_869_450_418_352_160_768_000_000, +/* f(28): */ 304_888_344_611_713_860_501_504_000_000, +/* f(29): */ 8_841_761_993_739_701_954_543_616_000_000, +/* f(30): */ 265_252_859_812_191_058_636_308_480_000_000, +/* f(31): */ 8_222_838_654_177_922_817_725_562_880_000_000, +/* f(32): */ 263_130_836_933_693_530_167_218_012_160_000_000, +/* f(33): */ 8_683_317_618_811_886_495_518_194_401_280_000_000, +/* f(34): */ 295_232_799_039_604_140_847_618_609_643_520_000_000, + }; +} else { + _factorial_table := [21]_WORD{ +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, + }; +}; \ No newline at end of file diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 69dfe7995..2a21e4f98 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,7 +1,7 @@ @echo off -:odin run . -vet +odin run . -vet :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules -odin build . -build-mode:shared -show-timings -o:size -use-separate-modules +:odin build . -build-mode:shared -show-timings -o:size -use-separate-modules :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules python test.py diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 32d7b938f..5bdc0139f 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -11,6 +11,11 @@ package big import "core:intrinsics" +/* + TODO: Make the tunables runtime adjustable where practical. + This allows to benchmark and/or setting optimized values for a certain CPU without recompiling. +*/ + /* Tunables */ @@ -21,6 +26,7 @@ when _LOW_MEMORY { _DEFAULT_DIGIT_COUNT :: 32; } + _MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); _SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); _MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 0c8ef35e1..ce3a0f468 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -45,6 +45,7 @@ _SQR_TOOM_CUTOFF, Category :: enum { itoa, atoi, + factorial, }; Event :: struct { t: time.Duration, @@ -52,14 +53,21 @@ Event :: struct { } Timings := [Category]Event{}; -print :: proc(name: string, a: ^Int, base := i8(10)) { +print :: proc(name: string, a: ^Int, base := i8(10), print_extra_info := false, print_name := false) { s := time.tick_now(); as, err := itoa(a, base); Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; defer delete(as); cb, _ := count_bits(a); - fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); + if print_name { + fmt.printf("%v ", name); + } + if print_extra_info { + fmt.printf("(base: %v, bits used: %v): %v\n", base, cb, as); + } else { + fmt.printf("%v\n", as); + } if err != .None { fmt.printf("%v (error: %v | %v)\n", name, err, a); } @@ -70,22 +78,21 @@ demo :: proc() { destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - err = atoi(source, "711456452774621215865929644892071691538299606591173717356248653735056872543694196490784640730887936656406546625676792022", 10); - print("src ", source); + a := "4d2"; + b := "1538"; - fmt.println("sqrt should be 843478780275248664696797599030708027195155136953848512749494"); - - fmt.println(); - err = sqrt(destination, source); - fmt.printf("sqrt returned: %v\n", err); - print("sqrt ", destination); - - err = atoi(denominator, "711456452774621215865929644892071691538299606591173717356248653735056872543694196490784640730887936656406546625676792022", 10); - err = root_n(quotient, denominator, 2); - fmt.printf("root_n(2) returned: %v\n", err); - print("root_n(2)", quotient); - - // fmt.println(); + if err = atoi(destination, a, 16); err != .None { + fmt.printf("atoi(a) returned %v\n", err); + return; + } + if err = atoi(source, b, 16); err != .None { + fmt.printf("atoi(b) returned %v\n", err); + return; + } + if err = add(destination, destination, source); err != .None { + fmt.printf("add(a, b) returned %v\n", err); + return; + } } main :: proc() { diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 572adec6a..ef5b0079e 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -240,6 +240,7 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { /* Make sure the radix is ok. */ + if radix < 2 || radix > 64 { return .Invalid_Argument; } @@ -278,7 +279,7 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { ch = rune(input[0]); if radix <= 36 && ch >= 'a' && ch <= 'z' { - ch += 'a' - 'A'; + ch -= 32; // 'a' - 'A' } pos := ch - '+'; @@ -299,7 +300,6 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { input = input[1:]; } - /* If an illegal character was found, fail. */ diff --git a/core/math/big/test.odin b/core/math/big/test.odin index ed95669f5..b8643813c 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -33,12 +33,12 @@ PyRes :: struct { aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":add:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":add:atoi(b):", err=err}; } + if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":add:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":add:atoi(b):", err=err}; } if err = add(sum, aa, bb); err != .None { return PyRes{res=":add:add(sum,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(sum, 10, context.temp_allocator); + r, err = int_itoa_cstring(sum, 16, context.temp_allocator); if err != .None { return PyRes{res=":add:itoa(sum):", err=err}; } return PyRes{res = r, err = .None}; } @@ -50,12 +50,12 @@ PyRes :: struct { aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":sub:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":sub:atoi(b):", err=err}; } + if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":sub:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":sub:atoi(b):", err=err}; } if err = sub(sum, aa, bb); err != .None { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(sum, 10, context.temp_allocator); + r, err = int_itoa_cstring(sum, 16, context.temp_allocator); if err != .None { return PyRes{res=":sub:itoa(sum):", err=err}; } return PyRes{res = r, err = .None}; } @@ -67,12 +67,12 @@ PyRes :: struct { aa, bb, product := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, product); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":mul:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":mul:atoi(b):", err=err}; } + if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":mul:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":mul:atoi(b):", err=err}; } if err = mul(product, aa, bb); err != .None { return PyRes{res=":mul:mul(product,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(product, 10, context.temp_allocator); + r, err = int_itoa_cstring(product, 16, context.temp_allocator); if err != .None { return PyRes{res=":mul:itoa(product):", err=err}; } return PyRes{res = r, err = .None}; } @@ -87,12 +87,12 @@ PyRes :: struct { aa, bb, quotient := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, quotient); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":div:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 10); err != .None { return PyRes{res=":div:atoi(b):", err=err}; } + if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":div:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":div:atoi(b):", err=err}; } if err = div(quotient, aa, bb); err != .None { return PyRes{res=":div:div(quotient,a,b):", err=err}; } r: cstring; - r, err = int_itoa_cstring(quotient, 10, context.temp_allocator); + r, err = int_itoa_cstring(quotient, 16, context.temp_allocator); if err != .None { return PyRes{res=":div:itoa(quotient):", err=err}; } return PyRes{res = r, err = .None}; } @@ -109,7 +109,7 @@ PyRes :: struct { aa := &Int{}; defer destroy(aa); - if err = atoi(aa, string(a), 10); err != .None { return PyRes{res=":log:atoi(a):", err=err}; } + if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":log:atoi(a):", err=err}; } if l, err = log(aa, base); err != .None { return PyRes{res=":log:log(a, base):", err=err}; } zero(aa); @@ -119,7 +119,7 @@ PyRes :: struct { clamp(aa); r: cstring; - r, err = int_itoa_cstring(aa, 10, context.temp_allocator); + r, err = int_itoa_cstring(aa, 16, context.temp_allocator); if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -134,11 +134,11 @@ PyRes :: struct { dest, bb := &Int{}, &Int{}; defer destroy(dest, bb); - if err = atoi(bb, string(base), 10); err != .None { return PyRes{res=":pow:atoi(base):", err=err}; } + if err = atoi(bb, string(base), 16); err != .None { return PyRes{res=":pow:atoi(base):", err=err}; } if err = pow(dest, bb, power); err != .None { return PyRes{res=":pow:pow(dest, base, power):", err=err}; } r: cstring; - r, err = int_itoa_cstring(dest, 10, context.temp_allocator); + r, err = int_itoa_cstring(dest, 16, context.temp_allocator); if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -153,11 +153,11 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":sqrt:atoi(src):", err=err}; } + if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":sqrt:atoi(src):", err=err}; } if err = sqrt(src, src); err != .None { return PyRes{res=":sqrt:sqrt(src):", err=err}; } r: cstring; - r, err = int_itoa_cstring(src, 10, context.temp_allocator); + r, err = int_itoa_cstring(src, 16, context.temp_allocator); if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -172,11 +172,11 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":root_n:atoi(src):", err=err}; } + if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":root_n:atoi(src):", err=err}; } if err = root_n(src, src, power); err != .None { return PyRes{res=":root_n:root_n(src):", err=err}; } r: cstring; - r, err = int_itoa_cstring(src, 10, context.temp_allocator); + r, err = int_itoa_cstring(src, 16, context.temp_allocator); if err != .None { return PyRes{res=":root_n:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -191,11 +191,11 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shr_digit:atoi(src):", err=err}; } + if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shr_digit:atoi(src):", err=err}; } if err = shr_digit(src, digits); err != .None { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; } r: cstring; - r, err = int_itoa_cstring(src, 10, context.temp_allocator); + r, err = int_itoa_cstring(src, 16, context.temp_allocator); if err != .None { return PyRes{res=":shr_digit:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -210,11 +210,11 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shl_digit:atoi(src):", err=err}; } + if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shl_digit:atoi(src):", err=err}; } if err = shl_digit(src, digits); err != .None { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; } r: cstring; - r, err = int_itoa_cstring(src, 10, context.temp_allocator); + r, err = int_itoa_cstring(src, 16, context.temp_allocator); if err != .None { return PyRes{res=":shl_digit:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -229,11 +229,11 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shr:atoi(src):", err=err}; } + if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shr:atoi(src):", err=err}; } if err = shr(src, src, bits); err != .None { return PyRes{res=":shr:shr(src, bits):", err=err}; } r: cstring; - r, err = int_itoa_cstring(src, 10, context.temp_allocator); + r, err = int_itoa_cstring(src, 16, context.temp_allocator); if err != .None { return PyRes{res=":shr:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -248,11 +248,11 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shr_signed:atoi(src):", err=err}; } + if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shr_signed:atoi(src):", err=err}; } if err = shr_signed(src, src, bits); err != .None { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; } r: cstring; - r, err = int_itoa_cstring(src, 10, context.temp_allocator); + r, err = int_itoa_cstring(src, 16, context.temp_allocator); if err != .None { return PyRes{res=":shr_signed:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } @@ -267,11 +267,11 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 10); err != .None { return PyRes{res=":shl:atoi(src):", err=err}; } + if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shl:atoi(src):", err=err}; } if err = shl(src, src, bits); err != .None { return PyRes{res=":shl:shl(src, bits):", err=err}; } r: cstring; - r, err = int_itoa_cstring(src, 10, context.temp_allocator); + r, err = int_itoa_cstring(src, 16, context.temp_allocator); if err != .None { return PyRes{res=":shl:itoa(res):", err=err}; } return PyRes{res = r, err = .None}; } diff --git a/core/math/big/test.py b/core/math/big/test.py index 645383b0f..abfa42336 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -134,7 +134,7 @@ int_shl = load(l.test_shl, [c_char_p, c_longlong], Res) int_shr = load(l.test_shr, [c_char_p, c_longlong], Res) int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res) -def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = ""): +def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16): passed = True r = None err = Error(res.err) @@ -152,7 +152,7 @@ def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expecte r = None try: r = res.res.decode('utf-8') - r = int(res.res, 10) + r = int(res.res, radix) except: pass @@ -168,42 +168,40 @@ def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expecte return passed +def arg_to_odin(a): + if a >= 0: + s = hex(a)[2:] + else: + s = '-' + hex(a)[3:] + return s.encode('utf-8') def test_add(a = 0, b = 0, expected_error = Error.Okay): - args = [str(a), str(b)] - sa_c, sb_c = args[0].encode('utf-8'), args[1].encode('utf-8') - res = add(sa_c, sb_c) + args = [arg_to_odin(a), arg_to_odin(b)] + res = add(*args) expected_result = None if expected_error == Error.Okay: expected_result = a + b - return test("test_add", res, args, expected_error, expected_result) + return test("test_add", res, [a, b], expected_error, expected_result) def test_sub(a = 0, b = 0, expected_error = Error.Okay): - sa, sb = str(a), str(b) - sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') - res = sub(sa_c, sb_c) + args = [arg_to_odin(a), arg_to_odin(b)] + res = sub(*args) expected_result = None if expected_error == Error.Okay: expected_result = a - b - return test("test_sub", res, [sa_c, sb_c], expected_error, expected_result) + return test("test_sub", res, [a, b], expected_error, expected_result) def test_mul(a = 0, b = 0, expected_error = Error.Okay): - sa, sb = str(a), str(b) - sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') - res = mul(sa_c, sb_c) + args = [arg_to_odin(a), arg_to_odin(b)] + res = mul(*args) expected_result = None if expected_error == Error.Okay: expected_result = a * b - return test("test_mul", res, [sa_c, sb_c], expected_error, expected_result) + return test("test_mul", res, [a, b], expected_error, expected_result) def test_div(a = 0, b = 0, expected_error = Error.Okay): - sa, sb = str(a), str(b) - sa_c, sb_c = sa.encode('utf-8'), sb.encode('utf-8') - try: - res = div(sa_c, sb_c) - except: - print("Exception with arguments:", a, b) - return False + args = [arg_to_odin(a), arg_to_odin(b)] + res = div(*args) expected_result = None if expected_error == Error.Okay: # @@ -215,23 +213,21 @@ def test_div(a = 0, b = 0, expected_error = Error.Okay): expected_result = int(-(a // abs((b)))) else: expected_result = a // b if b != 0 else None - return test("test_div", res, [sa_c, sb_c], expected_error, expected_result) + return test("test_div", res, [a, b], expected_error, expected_result) def test_log(a = 0, base = 0, expected_error = Error.Okay): - args = [str(a), base] - sa_c = args[0].encode('utf-8') - res = int_log(sa_c, base) + args = [arg_to_odin(a), base] + res = int_log(*args) expected_result = None if expected_error == Error.Okay: expected_result = int(math.log(a, base)) - return test("test_log", res, args, expected_error, expected_result) + return test("test_log", res, [a, base], expected_error, expected_result) def test_pow(base = 0, power = 0, expected_error = Error.Okay): - args = [str(base), power] - sa_c = args[0].encode('utf-8') - res = int_pow(sa_c, power) + args = [arg_to_odin(base), power] + res = int_pow(*args) expected_result = None if expected_error == Error.Okay: @@ -241,23 +237,18 @@ def test_pow(base = 0, power = 0, expected_error = Error.Okay): # NOTE(Jeroen): Don't use `math.pow`, it's a floating point approximation. # Use built-in `pow` or `a**b` instead. expected_result = pow(base, power) - return test("test_pow", res, args, expected_error, expected_result) + return test("test_pow", res, [base, power], expected_error, expected_result) def test_sqrt(number = 0, expected_error = Error.Okay): - args = [str(number)] - sa_c = args[0].encode('utf-8') - try: - res = int_sqrt(sa_c) - except: - print("sqrt:", number) - + args = [arg_to_odin(number)] + res = int_sqrt(*args) expected_result = None if expected_error == Error.Okay: if number < 0: expected_result = 0 else: expected_result = int(math.isqrt(number)) - return test("test_sqrt", res, args, expected_error, expected_result) + return test("test_sqrt", res, [number], expected_error, expected_result) def root_n(number, root): u, s = number, number + 1 @@ -268,13 +259,8 @@ def root_n(number, root): return s def test_root_n(number = 0, root = 0, expected_error = Error.Okay): - args = [str(number), root] - sa_c = args[0].encode('utf-8') - try: - res = int_root_n(sa_c, root) - except: - print("root_n:", number, root) - + args = [arg_to_odin(number), root] + res = int_root_n(*args) expected_result = None if expected_error == Error.Okay: if number < 0: @@ -282,27 +268,19 @@ def test_root_n(number = 0, root = 0, expected_error = Error.Okay): else: expected_result = root_n(number, root) - return test("test_root_n", res, args, expected_error, expected_result) + return test("test_root_n", res, [number, root], expected_error, expected_result) def test_shl_digit(a = 0, digits = 0, expected_error = Error.Okay): - args = [str(a), digits] - sa_c = args[0].encode('utf-8') - res = int_shl_digit(sa_c, digits) - + args = [arg_to_odin(a), digits] + res = int_shl_digit(*args) expected_result = None if expected_error == Error.Okay: expected_result = a << (digits * 60) - return test("test_shl_digit", res, args, expected_error, expected_result) + return test("test_shl_digit", res, [a, digits], expected_error, expected_result) def test_shr_digit(a = 0, digits = 0, expected_error = Error.Okay): - args = [str(a), digits] - sa_c = args[0].encode('utf-8') - try: - res = int_shr_digit(sa_c, digits) - except: - print("int_shr_digit", a, digits) - exit() - + args = [arg_to_odin(a), digits] + res = int_shr_digit(*args) expected_result = None if expected_error == Error.Okay: if a < 0: @@ -311,27 +289,19 @@ def test_shr_digit(a = 0, digits = 0, expected_error = Error.Okay): else: expected_result = a >> (digits * 60) - return test("test_shr_digit", res, args, expected_error, expected_result) + return test("test_shr_digit", res, [a, digits], expected_error, expected_result) def test_shl(a = 0, bits = 0, expected_error = Error.Okay): - args = [str(a), bits] - sa_c = args[0].encode('utf-8') - res = int_shl(sa_c, bits) - + args = [arg_to_odin(a), bits] + res = int_shl(*args) expected_result = None if expected_error == Error.Okay: expected_result = a << bits - return test("test_shl", res, args, expected_error, expected_result) + return test("test_shl", res, [a, bits], expected_error, expected_result) def test_shr(a = 0, bits = 0, expected_error = Error.Okay): - args = [str(a), bits] - sa_c = args[0].encode('utf-8') - try: - res = int_shr(sa_c, bits) - except: - print("int_shr", a, bits) - exit() - + args = [arg_to_odin(a), bits] + res = int_shr(*args) expected_result = None if expected_error == Error.Okay: if a < 0: @@ -340,22 +310,16 @@ def test_shr(a = 0, bits = 0, expected_error = Error.Okay): else: expected_result = a >> bits - return test("test_shr", res, args, expected_error, expected_result) + return test("test_shr", res, [a, bits], expected_error, expected_result) def test_shr_signed(a = 0, bits = 0, expected_error = Error.Okay): - args = [str(a), bits] - sa_c = args[0].encode('utf-8') - try: - res = int_shr_signed(sa_c, bits) - except: - print("int_shr_signed", a, bits) - exit() - + args = [arg_to_odin(a), bits] + res = int_shr_signed(*args) expected_result = None if expected_error == Error.Okay: expected_result = a >> bits - return test("test_shr_signed", res, args, expected_error, expected_result) + return test("test_shr_signed", res, [a, bits], expected_error, expected_result) # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # From 50feeaa285b651c8661e25984af4ce58f88c9340 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 31 Jul 2021 23:47:52 +0200 Subject: [PATCH 060/105] big: Add test for `factorial`. --- core/math/big/build.bat | 4 ++-- core/math/big/test.odin | 18 ++++++++++++++++++ core/math/big/test.py | 17 ++++++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 2a21e4f98..69dfe7995 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,7 +1,7 @@ @echo off -odin run . -vet +:odin run . -vet :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules -:odin build . -build-mode:shared -show-timings -o:size -use-separate-modules +odin build . -build-mode:shared -show-timings -o:size -use-separate-modules :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules python test.py diff --git a/core/math/big/test.odin b/core/math/big/test.odin index b8643813c..a03a7e879 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -276,3 +276,21 @@ PyRes :: struct { return PyRes{res = r, err = .None}; } +/* + dest = factorial(n) +*/ +@export test_factorial :: proc "c" (n: DIGIT) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + dest := &Int{}; + defer destroy(dest); + + if err = factorial(dest, n); err != .None { return PyRes{res=":factorial:factorial(n):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(dest, 16, context.temp_allocator); + if err != .None { return PyRes{res=":factorial:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + diff --git a/core/math/big/test.py b/core/math/big/test.py index abfa42336..75152821f 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -134,6 +134,8 @@ int_shl = load(l.test_shl, [c_char_p, c_longlong], Res) int_shr = load(l.test_shr, [c_char_p, c_longlong], Res) int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res) +int_factorial = load(l.test_factorial, [c_uint64], Res) + def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16): passed = True r = None @@ -321,6 +323,16 @@ def test_shr_signed(a = 0, bits = 0, expected_error = Error.Okay): return test("test_shr_signed", res, [a, bits], expected_error, expected_result) +def test_factorial(n = 0, expected_error = Error.Okay): + args = [n] + res = int_factorial(*args) + expected_result = None + if expected_error == Error.Okay: + expected_result = math.factorial(n) + + return test("test_factorial", res, [n], expected_error, expected_result) + + # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # # The last two arguments in tests are the expected error and expected result. @@ -394,7 +406,10 @@ TESTS = { [ -149195686190273039203651143129455, 12 ], [ 611105530635358368578155082258244262, 12 ], [ 149195686190273039203651143129455, 12 ], - ] + ], + test_factorial: [ + [ 12_345 ], + ], } total_passes = 0 From b15ee059ade2db0ce4048bf09c32577137063464 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 1 Aug 2021 19:36:23 +0200 Subject: [PATCH 061/105] big: Add `gcd`. --- core/math/big/basic.odin | 93 +++++++++++++++++++++++++++++++++++++- core/math/big/helpers.odin | 68 ++++++++++++++-------------- core/math/big/test.py | 3 ++ 3 files changed, 127 insertions(+), 37 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 1ba5807f4..751e2aedf 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -25,8 +25,8 @@ import "core:intrinsics" */ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if err = clear_if_uninitialized(a); err != .None { return err; } - if err = clear_if_uninitialized(b); err != .None { return err; } + if err = clear_if_uninitialized(x); err != .None { return err; } + if err = clear_if_uninitialized(y); err != .None { return err; } if err = clear_if_uninitialized(dest); err != .None { return err; } /* All parameters have been initialized. @@ -773,6 +773,9 @@ int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { } factorial :: proc { int_factorial, }; + + + /* ========================== Low-level routines @@ -1264,6 +1267,92 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain } +/* + Greatest Common Divisor using the binary method. + + TODO(Jeroen): + - Maybe combine with LCM and have an `_int_gcd_lcm` proc that can return both with work shared. +*/ +int_gcd :: proc(res, a, b: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(a, b, res); err != .None { return err; } + + /* + If either `a` or `b`, return the other one. + */ + if z, _ := is_zero(a); z { return abs(res, b); } + if z, _ := is_zero(b); z { return abs(res, a); } + + /* + Get copies of `a` and `b` we can modify. + */ + u, v := &Int{}, &Int{}; + defer destroy(u, v); + if err = copy(u, a); err != .None { return err; } + if err = copy(v, b); err != .None { return err; } + + /* + Must be positive for the remainder of the algorithm. + */ + u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive; + + /* + B1. Find the common power of two for `u` and `v`. + */ + u_lsb, _ := count_lsb(u); + v_lsb, _ := count_lsb(v); + k := min(u_lsb, v_lsb); + + if k > 0 { + /* + Divide the power of two out. + */ + if err = shr(u, u, k); err != .None { return err; } + if err = shr(v, v, k); err != .None { return err; } + } + + /* + Divide any remaining factors of two out. + */ + if u_lsb != k { + if err = shr(u, u, u_lsb - k); err != .None { return err; } + } + if v_lsb != k { + if err = shr(v, v, v_lsb - k); err != .None { return err; } + } + + for v.used != 0 { + /* + Make sure `v` is the largest. + */ + if c, _ := cmp_mag(u, v); c == 1 { + /* + Swap `u` and `v` to make sure `v` is >= `u`. + */ + swap(u, v); + } + + /* + Subtract smallest from largest. + */ + if err = sub(v, v, u); err != .None { return err; } + + /* + Divide out all factors of two. + */ + b, _ := count_lsb(v); + if err = shr(v, v, b); err != .None { return err; } + } + + /* + Multiply by 2**k which we divided out at the beginning. + */ + if err = shl(res, u, k); err != .None { return err; } + res.sign = .Zero_or_Positive; + return err; +} +gcd :: proc { int_gcd, }; + + when size_of(rawptr) == 8 { _factorial_table := [35]_WORD{ /* f(00): */ 1, diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index d5cf16b33..dc846b4b9 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -504,48 +504,36 @@ count_bits :: proc(a: ^Int) -> (count: int, err: Error) { } /* - Counts the number of LSBs which are zero before the first zero bit + Returns the number of trailing zeroes before the first one. + Differs from regular `ctz` in that 0 returns 0. */ -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; +int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { + if err = clear_if_uninitialized(a); err != .None { return -1, err; } + _ctz :: intrinsics.count_trailing_zeros; /* - Early out for zero. + Easy out. */ - if z, _ := is_zero(a); z { - return 0, .None; - } + 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; + x: int; + for x = 0; x < a.used && a.digit[x] == 0; x += 1 {} - /* - 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; + q := a.digit[x]; + x *= _DIGIT_BITS; + return x + count_lsb(q), .None; } +platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) + where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) { + return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0; +} + +count_lsb :: proc { int_count_lsb, platform_count_lsb, }; + int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { when _DIGIT_BITS == 60 { // DIGIT = u64 return DIGIT(rnd.uint64(r)) & _MASK; @@ -602,14 +590,24 @@ _zero_unused :: proc(a: ^Int) { } } -clear_if_uninitialized :: proc(dest: ^Int, minimize := false) -> (err: Error) { - if !is_initialized(dest) { - return grow(dest, _MIN_DIGIT_COUNT if minimize else _DEFAULT_DIGIT_COUNT); +clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) { + if !is_initialized(arg) { + return grow(arg, _DEFAULT_DIGIT_COUNT); } - return .None; + return err; } +clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { + for i in args { + if i != nil && !is_initialized(i) { + e := grow(i, _DEFAULT_DIGIT_COUNT); + if e != .None { err = e; } + } + } + return err; +} +clear_if_uninitialized :: proc {clear_if_uninitialized_single, clear_if_uninitialized_multi, }; /* Allocates several `Int`s at once. diff --git a/core/math/big/test.py b/core/math/big/test.py index 75152821f..96e0be227 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -475,6 +475,9 @@ if __name__ == '__main__': TIMINGS = {} UNTIL_ITERS = ITERATIONS + if test_proc == test_root_n and BITS == 1_200: + UNTIL_ITERS /= 10 + UNTIL_TIME = TOTAL_TIME + BITS / TIMED_BITS_PER_SECOND # We run each test for a second per 20k bits From 0028cb02585f85d575156260d60c7ee8d4188568 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 1 Aug 2021 20:39:51 +0200 Subject: [PATCH 062/105] big: Test `gcd`. --- core/math/big/build.bat | 6 ++- core/math/big/example.odin | 83 ++++++++++++++++++++++++-------------- core/math/big/test.odin | 20 +++++++++ core/math/big/test.py | 18 ++++++++- 4 files changed, 94 insertions(+), 33 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 69dfe7995..6a067018e 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,7 +1,9 @@ @echo off -:odin run . -vet +:odin run . -vet :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules -odin build . -build-mode:shared -show-timings -o:size -use-separate-modules +:odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:size -use-separate-modules +:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules python test.py diff --git a/core/math/big/example.odin b/core/math/big/example.odin index ce3a0f468..056cddb90 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -13,7 +13,6 @@ package big import "core:fmt" import "core:mem" import "core:time" -// import rnd "core:math/rand" print_configation :: proc() { fmt.printf( @@ -42,10 +41,43 @@ _SQR_TOOM_CUTOFF, ); } +print_timings :: proc() { + fmt.printf("\nTimings:\n"); + for v, i in Timings { + if v.c > 0 { + avg := time.Duration(f64(v.t) / f64(v.c)); + + avg_s: string; + switch { + case avg < time.Microsecond: + avg_s = fmt.tprintf("%v ns", time.duration_nanoseconds(avg)); + case avg < time.Millisecond: + avg_s = fmt.tprintf("%v µs", time.duration_microseconds(avg)); + case: + avg_s = fmt.tprintf("%v", time.duration_milliseconds(avg)); + } + + total_s: string; + switch { + case v.t < time.Microsecond: + total_s = fmt.tprintf("%v ns", time.duration_nanoseconds(v.t)); + case v.t < time.Millisecond: + total_s = fmt.tprintf("%v µs", time.duration_microseconds(v.t)); + case: + total_s = fmt.tprintf("%v", time.duration_milliseconds(v.t)); + } + + fmt.printf("\t%v: %s (avg), %s (total, %v calls)\n", i, avg_s, total_s, v.c); + } + } +} + Category :: enum { itoa, atoi, factorial, + lsb, + ctz, }; Event :: struct { t: time.Duration, @@ -53,7 +85,7 @@ Event :: struct { } Timings := [Category]Event{}; -print :: proc(name: string, a: ^Int, base := i8(10), print_extra_info := false, print_name := false) { +print :: proc(name: string, a: ^Int, base := i8(10), print_extra_info := false, print_name := false, newline := true) { s := time.tick_now(); as, err := itoa(a, base); Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; @@ -64,35 +96,33 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_extra_info := false, fmt.printf("%v ", name); } if print_extra_info { - fmt.printf("(base: %v, bits used: %v): %v\n", base, cb, as); + fmt.printf("(base: %v, bits used: %v): %v", base, cb, as); } else { - fmt.printf("%v\n", as); + fmt.printf("%v", as); } if err != .None { - fmt.printf("%v (error: %v | %v)\n", name, err, a); + fmt.printf("%v (error: %v | %v)", name, err, a); + } + if newline { + fmt.println(); } } demo :: proc() { - err: Error; - destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(destination, source, quotient, remainder, numerator, denominator); - a := "4d2"; - b := "1538"; + // err: Error; + // a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + // defer destroy(a, b, c, d, e, f); - if err = atoi(destination, a, 16); err != .None { - fmt.printf("atoi(a) returned %v\n", err); - return; - } - if err = atoi(source, b, 16); err != .None { - fmt.printf("atoi(b) returned %v\n", err); - return; - } - if err = add(destination, destination, source); err != .None { - fmt.printf("add(a, b) returned %v\n", err); - return; - } + // set(a, 25); + // set(b, 15); + + // err = gcd(c, a, b); + // fmt.printf("gcd("); + // print("a =", a, 10, false, true, false); + // print(", b =", b, 10, false, true, false); + // print(") =", c, 10, false, true, false); + // fmt.printf(" (err = %v)\n", err); } main :: proc() { @@ -102,15 +132,8 @@ main :: proc() { // print_configation(); demo(); + print_timings(); - fmt.printf("\nTimings:\n"); - for v, i in Timings { - if v.c > 0 { - avg := time.duration_milliseconds(time.Duration(f64(v.t) / f64(v.c))); - total := time.duration_milliseconds(time.Duration(v.t)); - fmt.printf("%v: %.3f ms (avg), %.3f ms (total, %v calls)\n", i, avg, total, v.c); - } - } if len(ta.allocation_map) > 0 { for _, v in ta.allocation_map { diff --git a/core/math/big/test.odin b/core/math/big/test.odin index a03a7e879..0b5cdd7e9 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -294,3 +294,23 @@ PyRes :: struct { return PyRes{res = r, err = .None}; } +/* + dest = gcd(a, b) +*/ +@export test_gcd :: proc "c" (a, b: cstring) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + ai, bi, dest := &Int{}, &Int{}, &Int{}; + defer destroy(ai, bi, dest); + + if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":gcd:atoi(a):", err=err}; } + if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":gcd:atoi(b):", err=err}; } + if err = gcd(dest, ai, bi); err != .None { return PyRes{res=":gcd:gcd(a, b):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(dest, 16, context.temp_allocator); + if err != .None { return PyRes{res=":gcd:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + diff --git a/core/math/big/test.py b/core/math/big/test.py index 96e0be227..efdbab607 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -135,6 +135,8 @@ int_shr = load(l.test_shr, [c_char_p, c_longlong], Res) int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res) int_factorial = load(l.test_factorial, [c_uint64], Res) +int_gcd = load(l.test_gcd, [c_char_p, c_char_p], Res) + def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16): passed = True @@ -332,6 +334,15 @@ def test_factorial(n = 0, expected_error = Error.Okay): return test("test_factorial", res, [n], expected_error, expected_result) +def test_gcd(a = 0, b = 0, expected_error = Error.Okay): + args = [arg_to_odin(a), arg_to_odin(b)] + res = int_gcd(*args) + expected_result = None + if expected_error == Error.Okay: + expected_result = math.gcd(a, b) + + return test("test_gcd", res, [a, b], expected_error, expected_result) + # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # @@ -410,6 +421,10 @@ TESTS = { test_factorial: [ [ 12_345 ], ], + test_gcd: [ + [ 123, 25, ], + [ 125, 25, ], + ], } total_passes = 0 @@ -422,9 +437,10 @@ RANDOM_TESTS = [ test_add, test_sub, test_mul, test_div, test_log, test_pow, test_sqrt, test_root_n, test_shl_digit, test_shr_digit, test_shl, test_shr_signed, + test_gcd, ] SKIP_LARGE = [ - test_pow, test_root_n, + test_pow, test_root_n, # test_gcd, ] SKIP_LARGEST = [] From 8b1d8c8453cb9427e7e11d7986cf3ee8e2cd9afb Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 1 Aug 2021 21:28:19 +0200 Subject: [PATCH 063/105] big: Add `lcm` and its test. --- core/math/big/basic.odin | 43 ++++++++++++++++++++++++++++++++++++++ core/math/big/example.odin | 24 ++++++++++----------- core/math/big/test.odin | 20 ++++++++++++++++++ core/math/big/test.py | 16 ++++++++++++-- 4 files changed, 89 insertions(+), 14 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 751e2aedf..b449ee543 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -1353,6 +1353,49 @@ int_gcd :: proc(res, a, b: ^Int) -> (err: Error) { gcd :: proc { int_gcd, }; +/* + Least Common Multiple. + Computes least common multiple as `|a*b|/(a, b)` + + TODO(Jeroen): + - Maybe combine with GCD and have an `_int_gcd_lcm` proc that can return both with work shared. +*/ +int_lcm :: proc(res, a, b: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(a, b, res); err != .None { return err; } + + t1, t2 := &Int{}, &Int{}; + defer destroy(t1, t2); + + /* + t1 = get the GCD of the two inputs. + */ + if err = gcd(t1, a, b); err != .None { return err; } + + /* + Divide the smallest by the GCD. + */ + if c, _ := cmp_mag(a, b); c == -1 { + /* + Store quotient in `t2` such that `t2 * b` is the LCM. + */ + if err = div(t2, a, t1); err != .None { return err; } + err = mul(res, t2, b); + } else { + /* + Store quotient in `t2` such that `t2 * a` is the LCM. + */ + if err = div(t2, a, t1); err != .None { return err; } + err = mul(res, t2, b); + } + + /* + Fix the sign to positive and return. + */ + res.sign = .Zero_or_Positive; + return err; +} +lcm :: proc { int_lcm, }; + when size_of(rawptr) == 8 { _factorial_table := [35]_WORD{ /* f(00): */ 1, diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 056cddb90..7e61aeea9 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -110,19 +110,20 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_extra_info := false, demo :: proc() { - // err: Error; - // a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - // defer destroy(a, b, c, d, e, f); + err: Error; + a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(a, b, c, d, e, f); - // set(a, 25); - // set(b, 15); + set(a, 25); + set(b, 15); + + err = gcd(c, a, b); + fmt.printf("gcd("); + print("a =", a, 10, false, true, false); + print(", b =", b, 10, false, true, false); + print(") =", c, 10, false, true, false); + fmt.printf(" (err = %v)\n", err); - // err = gcd(c, a, b); - // fmt.printf("gcd("); - // print("a =", a, 10, false, true, false); - // print(", b =", b, 10, false, true, false); - // print(") =", c, 10, false, true, false); - // fmt.printf(" (err = %v)\n", err); } main :: proc() { @@ -134,7 +135,6 @@ main :: proc() { demo(); print_timings(); - if len(ta.allocation_map) > 0 { for _, v in ta.allocation_map { fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location); diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 0b5cdd7e9..bbe045912 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -314,3 +314,23 @@ PyRes :: struct { return PyRes{res = r, err = .None}; } +/* + dest = lcm(a, b) +*/ +@export test_lcm :: proc "c" (a, b: cstring) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + ai, bi, dest := &Int{}, &Int{}, &Int{}; + defer destroy(ai, bi, dest); + + if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":gcd:atoi(a):", err=err}; } + if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":gcd:atoi(b):", err=err}; } + if err = lcm(dest, ai, bi); err != .None { return PyRes{res=":lcm:lcm(a, b):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(dest, 16, context.temp_allocator); + if err != .None { return PyRes{res=":lcm:itoa(res):", err=err}; } + return PyRes{res = r, err = .None}; +} + diff --git a/core/math/big/test.py b/core/math/big/test.py index efdbab607..d1e40feac 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -136,7 +136,7 @@ int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res) int_factorial = load(l.test_factorial, [c_uint64], Res) int_gcd = load(l.test_gcd, [c_char_p, c_char_p], Res) - +int_lcm = load(l.test_lcm, [c_char_p, c_char_p], Res) def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16): passed = True @@ -343,6 +343,14 @@ def test_gcd(a = 0, b = 0, expected_error = Error.Okay): return test("test_gcd", res, [a, b], expected_error, expected_result) +def test_lcm(a = 0, b = 0, expected_error = Error.Okay): + args = [arg_to_odin(a), arg_to_odin(b)] + res = int_lcm(*args) + expected_result = None + if expected_error == Error.Okay: + expected_result = math.lcm(a, b) + + return test("test_lcm", res, [a, b], expected_error, expected_result) # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on. # @@ -425,6 +433,10 @@ TESTS = { [ 123, 25, ], [ 125, 25, ], ], + test_lcm: [ + [ 123, 25, ], + [ 125, 25, ], + ], } total_passes = 0 @@ -437,7 +449,7 @@ RANDOM_TESTS = [ test_add, test_sub, test_mul, test_div, test_log, test_pow, test_sqrt, test_root_n, test_shl_digit, test_shr_digit, test_shl, test_shr_signed, - test_gcd, + test_gcd, test_lcm, ] SKIP_LARGE = [ test_pow, test_root_n, # test_gcd, From 06f5a6c7855ce2edd88bb192fa62f58c9c8911a4 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 1 Aug 2021 22:05:20 +0200 Subject: [PATCH 064/105] big: Special case `gcd(0,0)` + `lcm(0,0)`. --- core/math/big/basic.odin | 19 +++++++++++++++++-- core/math/big/test.py | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index b449ee543..c2f983bc4 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -1277,10 +1277,18 @@ int_gcd :: proc(res, a, b: ^Int) -> (err: Error) { if err = clear_if_uninitialized(a, b, res); err != .None { return err; } /* + If both `a` and `b` are zero, return zero. If either `a` or `b`, return the other one. */ - if z, _ := is_zero(a); z { return abs(res, b); } - if z, _ := is_zero(b); z { return abs(res, a); } + az, _ := is_zero(a); + bz, _ := is_zero(b); + if az && bz { + return zero(res); + } else if az { + return abs(res, b); + } else if bz { + return abs(res, a); + } /* Get copies of `a` and `b` we can modify. @@ -1366,6 +1374,13 @@ int_lcm :: proc(res, a, b: ^Int) -> (err: Error) { t1, t2 := &Int{}, &Int{}; defer destroy(t1, t2); + /* + Special case: lcm(0, 0) is defined as zero. + */ + az, _ := is_zero(a); + bz, _ := is_zero(b); + if az && bz { return zero(res); } + /* t1 = get the GCD of the two inputs. */ diff --git a/core/math/big/test.py b/core/math/big/test.py index d1e40feac..acb6df197 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -432,10 +432,14 @@ TESTS = { test_gcd: [ [ 123, 25, ], [ 125, 25, ], + [ 125, 0, ], + [ 0, 0, ], ], test_lcm: [ [ 123, 25, ], [ 125, 25, ], + [ 125, 0, ], + [ 0, 0, ], ], } From 6424a5a8dd8ae3b8bc4b6adafdaf2e8605b5584e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 1 Aug 2021 23:40:33 +0200 Subject: [PATCH 065/105] big: Refactored `gcm` and `lcm` to use a common function. --- core/math/big/basic.odin | 152 ++++++++++++++++++++++++++----------- core/math/big/example.odin | 20 ++++- core/math/big/test.py | 6 +- 3 files changed, 130 insertions(+), 48 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index c2f983bc4..c501dd72e 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -1266,31 +1266,110 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain return remainder, .None; } +/* + Function computing both GCD and (if target isn't `nil`) also LCM. +*/ +int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != .None { return err; } + return #force_inline _int_gcd_lcm(res_gcd, res_lcm, a, b); +} +gcd_lcm :: proc { int_gcd_lcm, }; /* Greatest Common Divisor using the binary method. - - TODO(Jeroen): - - Maybe combine with LCM and have an `_int_gcd_lcm` proc that can return both with work shared. */ int_gcd :: proc(res, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a, b, res); err != .None { return err; } + if err = clear_if_uninitialized(res, a, b); err != .None { return err; } /* If both `a` and `b` are zero, return zero. If either `a` or `b`, return the other one. */ - az, _ := is_zero(a); - bz, _ := is_zero(b); + az, _ := is_zero(a); bz, _ := is_zero(b); + if az && bz { return zero(res); } + else if az { return abs(res, b); } + else if bz { return abs(res, a); } + + return #force_inline _int_gcd_lcm(res, nil, a, b); +} +gcd :: proc { int_gcd, }; + +/* + Least Common Multiple. +*/ +int_lcm :: proc(res, a, b: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(res, a, b); err != .None { return err; } + + /* + If both `a` and `b` are zero, return zero. + */ + az, _ := is_zero(a); bz, _ := is_zero(b); + if az || bz { return zero(res); } + + return #force_inline _int_gcd_lcm(nil, res, a, b); +} +lcm :: proc { int_lcm, }; + +/* + Internal function computing both GCD and (if target isn't `nil`) also LCM. + Expects the arguments to have been initialized. +*/ +_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { + /* + If both `a` and `b` are zero, return zero. + If either `a` or `b`, return the other one. + + The `gcd` and `lcm` wrappers have already done this test, + but `gcd_lcm` wouldn't have, so we still need to perform it. + + If neither result is wanted, we have nothing to do. + */ + if res_gcd == nil && res_lcm == nil { return .None; } + + /* + We need a temporary because `res_gcd` is allowed to be `nil`. + */ + az, _ := is_zero(a); bz, _ := is_zero(b); if az && bz { - return zero(res); + /* + GCD(0, 0) and LCM(0, 0) are both 0. + */ + if res_gcd != nil { + if err = zero(res_gcd); err != .None { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != .None { return err; } + } + return .None; } else if az { - return abs(res, b); + /* + We can early out with GCD = B and LCM = 0 + */ + if res_gcd != nil { + if err = abs(res_gcd, b); err != .None { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != .None { return err; } + } + return .None; } else if bz { - return abs(res, a); + /* + We can early out with GCD = A and LCM = 0 + */ + if res_gcd != nil { + if err = abs(res_gcd, a); err != .None { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != .None { return err; } + } + return .None; } - /* + temp_gcd_res := &Int{}; + defer destroy(temp_gcd_res); + + /* + If neither `a` or `b` was zero, we need to compute `gcd`. Get copies of `a` and `b` we can modify. */ u, v := &Int{}, &Int{}; @@ -1354,62 +1433,47 @@ int_gcd :: proc(res, a, b: ^Int) -> (err: Error) { /* Multiply by 2**k which we divided out at the beginning. */ - if err = shl(res, u, k); err != .None { return err; } - res.sign = .Zero_or_Positive; - return err; -} -gcd :: proc { int_gcd, }; - - -/* - Least Common Multiple. - Computes least common multiple as `|a*b|/(a, b)` - - TODO(Jeroen): - - Maybe combine with GCD and have an `_int_gcd_lcm` proc that can return both with work shared. -*/ -int_lcm :: proc(res, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a, b, res); err != .None { return err; } - - t1, t2 := &Int{}, &Int{}; - defer destroy(t1, t2); + if err = shl(temp_gcd_res, u, k); err != .None { return err; } + temp_gcd_res.sign = .Zero_or_Positive; /* - Special case: lcm(0, 0) is defined as zero. + We've computed `gcd`, either the long way, or because one of the inputs was zero. + If we don't want `lcm`, we're done. */ - az, _ := is_zero(a); - bz, _ := is_zero(b); - if az && bz { return zero(res); } - - /* - t1 = get the GCD of the two inputs. - */ - if err = gcd(t1, a, b); err != .None { return err; } + if res_lcm == nil { + swap(temp_gcd_res, res_gcd); + return .None; + } /* + Computes least common multiple as `|a*b|/gcd(a,b)` Divide the smallest by the GCD. */ if c, _ := cmp_mag(a, b); c == -1 { /* Store quotient in `t2` such that `t2 * b` is the LCM. */ - if err = div(t2, a, t1); err != .None { return err; } - err = mul(res, t2, b); + if err = div(res_lcm, a, temp_gcd_res); err != .None { return err; } + err = mul(res_lcm, res_lcm, b); } else { /* Store quotient in `t2` such that `t2 * a` is the LCM. */ - if err = div(t2, a, t1); err != .None { return err; } - err = mul(res, t2, b); + if err = div(res_lcm, a, temp_gcd_res); err != .None { return err; } + err = mul(res_lcm, res_lcm, b); + } + + if res_gcd != nil { + swap(temp_gcd_res, res_gcd); } /* Fix the sign to positive and return. */ - res.sign = .Zero_or_Positive; + res_lcm.sign = .Zero_or_Positive; return err; } -lcm :: proc { int_lcm, }; + when size_of(rawptr) == 8 { _factorial_table := [35]_WORD{ diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 7e61aeea9..ba7190c59 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -114,8 +114,16 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - set(a, 25); - set(b, 15); + set(a, 125); + set(b, 0); + + err = gcd_lcm(c, d, a, b); + fmt.printf("gcd_lcm("); + print("a =", a, 10, false, true, false); + print(", b =", b, 10, false, true, false); + print("), gcd =", c, 10, false, true, false); + print(", lcm =", d, 10, false, true, false); + fmt.printf(" (err = %v)\n", err); err = gcd(c, a, b); fmt.printf("gcd("); @@ -124,6 +132,14 @@ demo :: proc() { print(") =", c, 10, false, true, false); fmt.printf(" (err = %v)\n", err); + err = lcm(c, a, b); + fmt.printf("lcm("); + print("a =", a, 10, false, true, false); + print(", b =", b, 10, false, true, false); + print(") =", c, 10, false, true, false); + fmt.printf(" (err = %v)\n", err); + + } main :: proc() { diff --git a/core/math/big/test.py b/core/math/big/test.py index acb6df197..b8e91c003 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -430,16 +430,18 @@ TESTS = { [ 12_345 ], ], test_gcd: [ - [ 123, 25, ], + [ 23, 25, ], [ 125, 25, ], [ 125, 0, ], [ 0, 0, ], + [ 0, 125,], ], test_lcm: [ - [ 123, 25, ], + [ 23, 25,], [ 125, 25, ], [ 125, 0, ], [ 0, 0, ], + [ 0, 125,], ], } From 320387c4ee07a3e38c4d2b80c37a57e8b1436e12 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 2 Aug 2021 01:11:44 +0200 Subject: [PATCH 066/105] big: Add `gcd_lcm` fast path in wrapper. --- core/math/big/basic.odin | 58 +++++++++++++++++++++++--------------- core/math/big/build.bat | 5 ++-- core/math/big/example.odin | 4 +-- core/math/big/test.odin | 4 +-- 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index c501dd72e..5d0a50218 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -1271,26 +1271,45 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain */ int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != .None { return err; } + + az, _ := is_zero(a); bz, _ := is_zero(b); + if az && bz { + if res_gcd != nil { + if err = zero(res_gcd); err != .None { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != .None { return err; } + } + return .None; + } + else if az { + if res_gcd != nil { + if err = abs(res_gcd, b); err != .None { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != .None { return err; } + } + return .None; + } + else if bz { + if res_gcd != nil { + if err = abs(res_gcd, a); err != .None { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != .None { return err; } + } + return .None; + } + return #force_inline _int_gcd_lcm(res_gcd, res_lcm, a, b); } gcd_lcm :: proc { int_gcd_lcm, }; /* - Greatest Common Divisor using the binary method. + Greatest Common Divisor. */ int_gcd :: proc(res, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(res, a, b); err != .None { return err; } - - /* - If both `a` and `b` are zero, return zero. - If either `a` or `b`, return the other one. - */ - az, _ := is_zero(a); bz, _ := is_zero(b); - if az && bz { return zero(res); } - else if az { return abs(res, b); } - else if bz { return abs(res, a); } - - return #force_inline _int_gcd_lcm(res, nil, a, b); + return #force_inline int_gcd_lcm(res, nil, a, b); } gcd :: proc { int_gcd, }; @@ -1298,20 +1317,13 @@ gcd :: proc { int_gcd, }; Least Common Multiple. */ int_lcm :: proc(res, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(res, a, b); err != .None { return err; } - - /* - If both `a` and `b` are zero, return zero. - */ - az, _ := is_zero(a); bz, _ := is_zero(b); - if az || bz { return zero(res); } - - return #force_inline _int_gcd_lcm(nil, res, a, b); + return #force_inline int_gcd_lcm(nil, res, a, b); } lcm :: proc { int_lcm, }; /* - Internal function computing both GCD and (if target isn't `nil`) also LCM. + Internal function computing both GCD using the binary method, + and, if target isn't `nil`, also LCM. Expects the arguments to have been initialized. */ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 6a067018e..8e69389e9 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,9 @@ @echo off -:odin run . -vet +odin run . -vet :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -python test.py -:del big.* \ No newline at end of file +:python test.py \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index ba7190c59..5452dd711 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -115,7 +115,7 @@ demo :: proc() { defer destroy(a, b, c, d, e, f); set(a, 125); - set(b, 0); + set(b, 75); err = gcd_lcm(c, d, a, b); fmt.printf("gcd_lcm("); @@ -138,8 +138,6 @@ demo :: proc() { print(", b =", b, 10, false, true, false); print(") =", c, 10, false, true, false); fmt.printf(" (err = %v)\n", err); - - } main :: proc() { diff --git a/core/math/big/test.odin b/core/math/big/test.odin index bbe045912..52c9eaa3e 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -324,8 +324,8 @@ PyRes :: struct { ai, bi, dest := &Int{}, &Int{}, &Int{}; defer destroy(ai, bi, dest); - if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":gcd:atoi(a):", err=err}; } - if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":gcd:atoi(b):", err=err}; } + if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":lcm:atoi(a):", err=err}; } + if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":lcm:atoi(b):", err=err}; } if err = lcm(dest, ai, bi); err != .None { return PyRes{res=":lcm:lcm(a, b):", err=err}; } r: cstring; From cd0ce7b76e800ac308d6df02ddad4da221fbb90a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 2 Aug 2021 02:07:14 +0200 Subject: [PATCH 067/105] big: Add `choose`. --- core/math/big/basic.odin | 35 +++++++++++++++++++++++++++++++++++ core/math/big/example.odin | 34 ++++++++-------------------------- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 5d0a50218..4129f44d5 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -773,8 +773,43 @@ int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { } factorial :: proc { int_factorial, }; +/* + Number of ways to choose `k` items from `n` items. + Also known as the binomial coefficient. + TODO: Speed up. + Could be done faster by reusing code from factorial and reusing the common "prefix" results for n!, k! and n-k! + We know that n >= k, otherwise we early out with res = 0. + + So: + n-k, keep result + n, start from previous result + k, start from previous result + +*/ +int_choose_digit :: proc(res: ^Int, n, k: DIGIT) -> (err: Error) { + if res == nil { return .Invalid_Pointer; } + if err = clear_if_uninitialized(res); err != .None { return err; } + + if k > n { return zero(res); } + + /* + res = n! / (k! * (n - k)!) + */ + n_fac, k_fac, n_minus_k_fac := &Int{}, &Int{}, &Int{}; + defer destroy(n_fac, k_fac, n_minus_k_fac); + + if err = factorial(n_minus_k_fac, n - k); err != .None { return err; } + if err = factorial(k_fac, k); err != .None { return err; } + if err = mul(k_fac, k_fac, n_minus_k_fac); err != .None { return err; } + + if err = factorial(n_fac, n); err != .None { return err; } + if err = div(res, n_fac, k_fac); err != .None { return err; } + + return err; +} +choose :: proc { int_choose_digit, }; /* ========================== diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 5452dd711..14531a184 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -54,7 +54,7 @@ print_timings :: proc() { case avg < time.Millisecond: avg_s = fmt.tprintf("%v µs", time.duration_microseconds(avg)); case: - avg_s = fmt.tprintf("%v", time.duration_milliseconds(avg)); + avg_s = fmt.tprintf("%v ms", time.duration_milliseconds(avg)); } total_s: string; @@ -64,7 +64,7 @@ print_timings :: proc() { case v.t < time.Millisecond: total_s = fmt.tprintf("%v µs", time.duration_microseconds(v.t)); case: - total_s = fmt.tprintf("%v", time.duration_milliseconds(v.t)); + total_s = fmt.tprintf("%v ms", time.duration_milliseconds(v.t)); } fmt.printf("\t%v: %s (avg), %s (total, %v calls)\n", i, avg_s, total_s, v.c); @@ -76,6 +76,7 @@ Category :: enum { itoa, atoi, factorial, + choose, lsb, ctz, }; @@ -114,30 +115,11 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - set(a, 125); - set(b, 75); - - err = gcd_lcm(c, d, a, b); - fmt.printf("gcd_lcm("); - print("a =", a, 10, false, true, false); - print(", b =", b, 10, false, true, false); - print("), gcd =", c, 10, false, true, false); - print(", lcm =", d, 10, false, true, false); - fmt.printf(" (err = %v)\n", err); - - err = gcd(c, a, b); - fmt.printf("gcd("); - print("a =", a, 10, false, true, false); - print(", b =", b, 10, false, true, false); - print(") =", c, 10, false, true, false); - fmt.printf(" (err = %v)\n", err); - - err = lcm(c, a, b); - fmt.printf("lcm("); - print("a =", a, 10, false, true, false); - print(", b =", b, 10, false, true, false); - print(") =", c, 10, false, true, false); - fmt.printf(" (err = %v)\n", err); + s := time.tick_now(); + err = choose(a, 65535, 255); + Timings[.choose].t += time.tick_since(s); Timings[.choose].c += 1; + print("choose", a); + fmt.println(err); } main :: proc() { From 491e4ecc740a361bd726f8caf92f8eed078ed333 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 2 Aug 2021 17:47:07 +0200 Subject: [PATCH 068/105] big: Add binary split factorial. --- core/math/big/basic.odin | 57 ++++++++++++++++++++++++++++++++++- core/math/big/build.bat | 1 + core/math/big/common.odin | 12 +++++++- core/math/big/example.odin | 10 ++++--- core/math/big/helpers.odin | 16 +++++----- core/math/big/logical.odin | 61 +++++++++++++------------------------- 6 files changed, 103 insertions(+), 54 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 4129f44d5..f403d853f 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -751,12 +751,15 @@ sqrmod :: proc { int_sqrmod, }; int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { - if n < 0 { return .Invalid_Argument; } + if n < 0 || n > _FACTORIAL_MAX_N || res == nil { return .Invalid_Argument; } i := DIGIT(len(_factorial_table)); if n < i { return set(res, _factorial_table[n]); } + if n >= _FACTORIAL_BINARY_SPLIT_CUTOFF { + return int_factorial_binary_split(res, n); + } a := &Int{}; defer destroy(a); @@ -771,6 +774,58 @@ int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { return .None; } + +_int_recursive_product :: proc(res: ^Int, start, stop: DIGIT, level := int(0)) -> (err: Error) { + t1, t2 := &Int{}, &Int{}; + defer destroy(t1, t2); + + if level > _FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } + + num_factors := (stop - start) >> 1; + if num_factors == 2 { + if err = set(t1, start); err != .None { return err; } + if err = add(t2, t1, 2); err != .None { return err; } + return mul(res, t1, t2); + } + + if num_factors > 1 { + mid := (start + num_factors) | 1; + if err = _int_recursive_product(t1, start, mid, level + 1); err != .None { return err; } + if err = _int_recursive_product(t2, mid, stop, level + 1); err != .None { return err; } + return mul(res, t1, t2); + } + + if num_factors == 1 { return set(res, start); } + + return one(res); +} + +/* + Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html +*/ +int_factorial_binary_split :: proc(res: ^Int, n: DIGIT) -> (err: Error) { + if n < 0 || n > _FACTORIAL_MAX_N || res == nil { return .Invalid_Argument; } + + inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(inner, outer, start, stop, temp); + + if err = one(inner); err != .None { return err; } + if err = one(outer); err != .None { return err; } + + bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); + + for i := bits_used; i >= 0; i -= 1 { + start := (n >> (uint(i) + 1)) + 1 | 1; + stop := (n >> uint(i)) + 1 | 1; + if err = _int_recursive_product(temp, start, stop); err != .None { return err; } + if err = mul(inner, inner, temp); err != .None { return err; } + if err = mul(outer, outer, inner); err != .None { return err; } + } + shift := n - intrinsics.count_ones(n); + + return shl(res, outer, int(shift)); +} + factorial :: proc { int_factorial, }; /* diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 8e69389e9..2c1edfcec 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,5 +1,6 @@ @echo off odin run . -vet +: -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 5bdc0139f..bcda658b0 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -26,7 +26,6 @@ when _LOW_MEMORY { _DEFAULT_DIGIT_COUNT :: 32; } - _MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); _SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); _MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); @@ -43,6 +42,17 @@ _DEFAULT_SQR_TOOM_CUTOFF :: 400; _MAX_ITERATIONS_ROOT_N :: 500; +/* + Largest `N` for which we'll compute `N!` +*/ +_FACTORIAL_MAX_N :: 100_000; + +/* + Cutoff to switch to int_factorial_binary_split, and its max recursion level. +*/ +_FACTORIAL_BINARY_SPLIT_CUTOFF :: 6100; +_FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS :: 100; + Sign :: enum u8 { Zero_or_Positive = 0, Negative = 1, diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 14531a184..f7ba61034 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -42,7 +42,7 @@ _SQR_TOOM_CUTOFF, } print_timings :: proc() { - fmt.printf("\nTimings:\n"); + fmt.printf("Timings:\n"); for v, i in Timings { if v.c > 0 { avg := time.Duration(f64(v.t) / f64(v.c)); @@ -76,6 +76,7 @@ Category :: enum { itoa, atoi, factorial, + factorial_bin, choose, lsb, ctz, @@ -116,10 +117,11 @@ demo :: proc() { defer destroy(a, b, c, d, e, f); s := time.tick_now(); - err = choose(a, 65535, 255); + err = choose(a, 1024, 255); Timings[.choose].t += time.tick_since(s); Timings[.choose].c += 1; - print("choose", a); - fmt.println(err); + + print("1024 choose 255", a, 10, true, true, true); + fmt.printf("Error: %v\n", err); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index dc846b4b9..a3b4f13ba 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -337,9 +337,7 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != .None { - return err; - } + if err = clear(a, minimize, allocator); err != .None { return err; } a.used = 1; a.digit[0] = 1; @@ -429,9 +427,7 @@ get_i32 :: proc { int_get_i32, }; and maybe return max(T), .Integer_Overflow if not? */ int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { - if err = clear_if_uninitialized(a); err != .None { - return 0, err; - } + if err = clear_if_uninitialized(a); err != .None { return 0, err; } size_in_bits := int(size_of(T) * 8); i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS); @@ -656,4 +652,10 @@ clamp :: proc(a: ^Int) -> (err: Error) { a.sign = .Zero_or_Positive; } return .None; -} \ No newline at end of file +} + + +_STATIC_ZERO := &Int{ + used = 0, + sign = .Zero_or_Positive, +}; diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index e721db603..1e11f1a35 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -16,25 +16,21 @@ import "core:mem" /* The `and`, `or` and `xor` binops differ in two lines only. We could handle those with a switch, but that adds overhead. + + TODO: Implement versions that take a DIGIT immediate. */ /* 2's complement `and`, returns `dest = a & b;` */ -and :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a); err != .None { - return err; - } - if err = clear_if_uninitialized(b); err != .None { - return err; - } +int_and :: proc(dest, a, b: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(a, b); err != .None { return err; } + used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .None { - return err; - } + if err = grow(dest, used); err != .None { return err; } neg_a, _ := is_neg(a); neg_b, _ := is_neg(b); @@ -83,24 +79,18 @@ and :: proc(dest, a, b: ^Int) -> (err: Error) { dest.sign = .Negative if neg else .Zero_or_Positive; return clamp(dest); } +and :: proc { int_add, }; /* 2's complement `or`, returns `dest = a | b;` */ -or :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a); err != .None { - return err; - } - if err = clear_if_uninitialized(b); err != .None { - return err; - } +int_or :: proc(dest, a, b: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(a, b); err != .None { return err; } used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .None { - return err; - } + if err = grow(dest, used); err != .None { return err; } neg_a, _ := is_neg(a); neg_b, _ := is_neg(b); @@ -149,24 +139,19 @@ or :: proc(dest, a, b: ^Int) -> (err: Error) { dest.sign = .Negative if neg else .Zero_or_Positive; return clamp(dest); } +or :: proc { int_or, }; /* 2's complement `xor`, returns `dest = a ~ b;` */ -xor :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a); err != .None { - return err; - } - if err = clear_if_uninitialized(b); err != .None { - return err; - } +int_xor :: proc(dest, a, b: ^Int) -> (err: Error) { + if err = clear_if_uninitialized(a, b); err != .None { return err; } + used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .None { - return err; - } + if err = grow(dest, used); err != .None { return err; } neg_a, _ := is_neg(a); neg_b, _ := is_neg(b); @@ -215,20 +200,16 @@ xor :: proc(dest, a, b: ^Int) -> (err: Error) { dest.sign = .Negative if neg else .Zero_or_Positive; return clamp(dest); } +xor :: proc { int_xor, }; /* dest = ~src */ int_complement :: proc(dest, src: ^Int) -> (err: Error) { /* - Check that src and dest are usable. + Check that src is usable. Dest will get checked by `sub`. */ - if err = clear_if_uninitialized(src); err != .None { - return err; - } - if err = clear_if_uninitialized(dest); err != .None { - return err; - } + if err = clear_if_uninitialized(src); err != .None { return err; } /* Temporarily fix sign. @@ -254,8 +235,7 @@ complement :: proc { int_complement, }; */ int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int) -> (err: Error) { bits := bits; - if err = clear_if_uninitialized(quotient); err != .None { return err; } - if err = clear_if_uninitialized(numerator); err != .None { return err; } + if err = clear_if_uninitialized(quotient, numerator); err != .None { return err; } if bits < 0 { return .Invalid_Argument; } @@ -371,8 +351,7 @@ shr_signed :: proc { int_shr_signed, }; */ int_shl :: proc(dest, src: ^Int, bits: int) -> (err: Error) { bits := bits; - if err = clear_if_uninitialized(src); err != .None { return err; } - if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src, dest); err != .None { return err; } if bits < 0 { return .Invalid_Argument; From a27612ec6a69b6a0ee9bd039683551daf87e5cd3 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 2 Aug 2021 21:01:46 +0200 Subject: [PATCH 069/105] Add `_mul_comba` path. --- core/math/big/basic.odin | 131 ++++++++++++++++++++++++++++++++----- core/math/big/build.bat | 6 +- core/math/big/example.odin | 6 +- 3 files changed, 120 insertions(+), 23 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index f403d853f..226ddc068 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -499,8 +499,7 @@ mod_bits :: proc { int_mod_bits, }; Multiply by a DIGIT. */ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { - if err = clear_if_uninitialized(src ); err != .None { return err; } - if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src, dest); err != .None { return err; } if multiplier == 0 { return zero(dest); @@ -576,9 +575,7 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { High level multiplication (handles sign). */ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(src); err != .None { return err; } - if err = clear_if_uninitialized(dest); err != .None { return err; } - if err = clear_if_uninitialized(multiplier); err != .None { return err; } + if err = clear_if_uninitialized(dest, src, multiplier); err != .None { return err; } /* Early out for `multiplier` is zero; Set `dest` to zero. @@ -587,11 +584,6 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { return zero(dest); } - min_used := min(src.used, multiplier.used); - max_used := max(src.used, multiplier.used); - digits := src.used + multiplier.used + 1; - neg := src.sign != multiplier.sign; - if src == multiplier { /* Do we need to square? @@ -619,6 +611,11 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { * Using it to cut the input into slices small enough for _mul_comba * was actually slower on the author's machine, but YMMV. */ + + min_used := min(src.used, multiplier.used); + max_used := max(src.used, multiplier.used); + digits := src.used + multiplier.used + 1; + if false && min_used >= _MUL_KARATSUBA_CUTOFF && max_used / 2 >= _MUL_KARATSUBA_CUTOFF && /* @@ -630,18 +627,19 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { // err = s_mp_mul_toom(a, b, c); } else if false && min_used >= _MUL_KARATSUBA_CUTOFF { // err = s_mp_mul_karatsuba(a, b, c); - } else if false && digits < _WARRAY && min_used <= _MAX_COMBA { + } else if digits < _WARRAY && min_used <= _MAX_COMBA { /* Can we use the fast multiplier? * The fast multiplier can be used if the output will * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ - // err = s_mp_mul_comba(a, b, c, digs); + err = _int_mul_comba(dest, src, multiplier, digits); } else { err = _int_mul(dest, src, multiplier, digits); } } + neg := src.sign != multiplier.sign; dest.sign = .Negative if dest.used > 0 && neg else .Zero_or_Positive; return err; } @@ -1033,14 +1031,11 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { many digits of output are created. */ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { - /* Can we use the fast multiplier? */ - when false { // Have Comba? - if digits < _WARRAY && min(a.used, b.used) < _MAX_COMBA { - return _int_mul_comba(dest, a, b, digits); - } + if digits < _WARRAY && min(a.used, b.used) < _MAX_COMBA { + return _int_mul_comba(dest, a, b, digits); } /* @@ -1095,6 +1090,108 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { return clamp(dest); } +/* + Fast (comba) multiplier + + This is the fast column-array [comba] multiplier. It is + designed to compute the columns of the product first + then handle the carries afterwards. This has the effect + of making the nested loops that compute the columns very + simple and schedulable on super-scalar processors. + + This has been modified to produce a variable number of + digits of output so if say only a half-product is required + you don't have to compute the upper half (a feature + required for fast Barrett reduction). + + Based on Algorithm 14.12 on pp.595 of HAC. +*/ +_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { + /* + Set up array. + */ + W: [_WARRAY]DIGIT = ---; + + /* + Grow the destination as required. + */ + if err = grow(dest, digits); err != .None { return err; } + + /* + Number of output digits to produce. + */ + pa := min(digits, a.used + b.used); + + /* + Clear the carry + */ + _W := _WORD(0); + + ix: int; + for ix = 0; ix < pa; ix += 1 { + tx, ty, iy, iz: int; + + /* + Get offsets into the two bignums. + */ + ty = min(b.used - 1, ix); + tx = ix - ty; + + /* + This is the number of times the loop will iterate, essentially. + while (tx++ < a->used && ty-- >= 0) { ... } + */ + + iy = min(a.used - tx, ty + 1); + + /* + Execute loop. + */ + for iz = 0; iz < iy; iz += 1 { + _W += _WORD(a.digit[tx + iz]) * _WORD(b.digit[ty - iz]); + } + + /* + Store term. + */ + W[ix] = DIGIT(_W) & _MASK; + + /* + Make next carry. + */ + _W = _W >> _WORD(_DIGIT_BITS); + } + + /* + Setup dest. + */ + old_used := dest.used; + dest.used = pa; + + for ix = 0; ix < pa; ix += 1 { + /* + Now extract the previous digit [below the carry]. + */ + dest.digit[ix] = W[ix]; + } + + /* + Clear unused digits [that existed in the old copy of dest]. + */ + 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. + */ + + return clamp(dest); +} + /* Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 2c1edfcec..d454fef4d 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -odin run . -vet +:odin run . -vet : -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check +odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -:python test.py \ No newline at end of file +python test.py \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index f7ba61034..10a14507f 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -87,7 +87,7 @@ Event :: struct { } Timings := [Category]Event{}; -print :: proc(name: string, a: ^Int, base := i8(10), print_extra_info := false, print_name := false, newline := true) { +print :: proc(name: string, a: ^Int, base := i8(10), print_name := false, newline := true, print_extra_info := false) { s := time.tick_now(); as, err := itoa(a, base); Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; @@ -117,10 +117,10 @@ demo :: proc() { defer destroy(a, b, c, d, e, f); s := time.tick_now(); - err = choose(a, 1024, 255); + err = choose(a, 65535, 255); Timings[.choose].t += time.tick_since(s); Timings[.choose].c += 1; - print("1024 choose 255", a, 10, true, true, true); + print("65535 choose 255", a, 10, true, true, true); fmt.printf("Error: %v\n", err); } From 627872db979585d70c0e19811345e440e253ce88 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 2 Aug 2021 22:23:39 +0200 Subject: [PATCH 070/105] big: Timed `factorial`. --- core/math/big/basic.odin | 26 ++++++++++++-------------- core/math/big/build.bat | 6 +++--- core/math/big/common.odin | 2 +- core/math/big/example.odin | 25 +++++++++++++++++++------ 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 226ddc068..cf641c029 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -580,9 +580,8 @@ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { /* Early out for `multiplier` is zero; Set `dest` to zero. */ - if z, _ := is_zero(multiplier); z { - return zero(dest); - } + if z, _ := is_zero(multiplier); z { return zero(dest); } + if z, _ := is_zero(src); z { return zero(dest); } if src == multiplier { /* @@ -748,6 +747,10 @@ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { sqrmod :: proc { int_sqrmod, }; +/* + TODO: Use Sterling's Approximation to estimate log2(N!) to size the result. + This way we'll have to reallocate less, possibly not at all. +*/ int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { if n < 0 || n > _FACTORIAL_MAX_N || res == nil { return .Invalid_Argument; } @@ -759,12 +762,7 @@ int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { return int_factorial_binary_split(res, n); } - a := &Int{}; - defer destroy(a); - - if err = set( a, i - 1); err != .None { return err; } if err = set(res, _factorial_table[i - 1]); err != .None { return err; } - for { if err = mul(res, res, DIGIT(i)); err != .None || i == n { return err; } i += 1; @@ -1168,12 +1166,12 @@ _int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { old_used := dest.used; dest.used = pa; - for ix = 0; ix < pa; ix += 1 { - /* - Now extract the previous digit [below the carry]. - */ - dest.digit[ix] = W[ix]; - } + /* + Now extract the previous digit [below the carry]. + */ + // for ix = 0; ix < pa; ix += 1 { dest.digit[ix] = W[ix]; } + + copy_slice(dest.digit[0:], W[:pa]); /* Clear unused digits [that existed in the old copy of dest]. diff --git a/core/math/big/build.bat b/core/math/big/build.bat index d454fef4d..2c1edfcec 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -:odin run . -vet +odin run . -vet : -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -python test.py \ No newline at end of file +:python test.py \ No newline at end of file diff --git a/core/math/big/common.odin b/core/math/big/common.odin index bcda658b0..5a51c7405 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -45,7 +45,7 @@ _MAX_ITERATIONS_ROOT_N :: 500; /* Largest `N` for which we'll compute `N!` */ -_FACTORIAL_MAX_N :: 100_000; +_FACTORIAL_MAX_N :: 1_000_000; /* Cutoff to switch to int_factorial_binary_split, and its max recursion level. diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 10a14507f..7c2df9303 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -111,17 +111,30 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := false, newlin } demo :: proc() { - err: Error; + as: string; + defer delete(as); + a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - s := time.tick_now(); - err = choose(a, 65535, 255); - Timings[.choose].t += time.tick_since(s); Timings[.choose].c += 1; + N :: 5_000; - print("65535 choose 255", a, 10, true, true, true); - fmt.printf("Error: %v\n", err); + s := time.tick_now(); + err = factorial(a, N); + Timings[.factorial].t += time.tick_since(s); Timings[.factorial].c += 1; + if err != .None { + fmt.printf("factorial(%v) returned %v\n", N, err); + } + + s = time.tick_now(); + as, err = itoa(a, 16); + Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; + if err != .None { + fmt.printf("itoa(factorial(%v), 16) returned %v\n", N, err); + } + + fmt.printf("factorial(%v): %v (first 10 hex digits)\n", N, as[:10]); } main :: proc() { From 97d80d03f9902872bcd12e460822dce70e6d3fd1 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 3 Aug 2021 17:07:25 +0200 Subject: [PATCH 071/105] big: `Error.None` -> `nil` --- core/math/big/basic.odin | 216 ++++++++++++++++++------------------- core/math/big/common.odin | 10 +- core/math/big/compare.odin | 70 ++++++------ core/math/big/example.odin | 11 +- core/math/big/exp_log.odin | 90 ++++++++-------- core/math/big/helpers.odin | 90 ++++++++-------- core/math/big/logical.odin | 52 ++++----- core/math/big/radix.odin | 50 ++++----- core/math/big/test.odin | 138 ++++++++++++------------ 9 files changed, 368 insertions(+), 359 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index cf641c029..5470bf917 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -25,9 +25,9 @@ import "core:intrinsics" */ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest := dest; x := a; y := b; - if err = clear_if_uninitialized(x); err != .None { return err; } - if err = clear_if_uninitialized(y); err != .None { return err; } - if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(x); err != nil { return err; } + if err = clear_if_uninitialized(y); err != nil { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } /* All parameters have been initialized. We can now safely ignore errors from comparison routines. @@ -62,13 +62,13 @@ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. */ - if err = grow(dest, a.used + 1); err != .None { + if err = grow(dest, a.used + 1); err != nil { return err; } @@ -110,7 +110,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* dest = |a| - digit */ - if err = sub(dest, a, digit); err != .None { + if err = sub(dest, a, digit); err != nil { /* Restore a's sign. */ @@ -186,13 +186,13 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - if err = clear_if_uninitialized(dest); err != .None { + if err = clear_if_uninitialized(dest); err != nil { return err; } - if err = clear_if_uninitialized(x); err != .None { + if err = clear_if_uninitialized(x); err != nil { return err; } - if err = clear_if_uninitialized(y); err != .None { + if err = clear_if_uninitialized(y); err != nil { return err; } /* @@ -242,14 +242,14 @@ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { dest := dest; digit := digit; - if err = clear_if_uninitialized(dest); err != .None { + if err = clear_if_uninitialized(dest); err != nil { return err; } /* Grow destination as required. */ if dest != a { - if err = grow(dest, a.used + 1); err != .None { + if err = grow(dest, a.used + 1); err != nil { return err; } } @@ -267,14 +267,14 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ if n, _ := is_neg(dest); n && (dest.digit[0] + digit < _DIGIT_MAX) { dest.digit[0] += digit; - return .None; + return nil; } /* Can be subtracted from dest.digit[0] without underflow. */ if p, _ := is_pos(a); p && (dest.digit[0] > digit) { dest.digit[0] -= digit; - return .None; + return nil; } } @@ -339,14 +339,14 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ int_halve :: proc(dest, src: ^Int) -> (err: Error) { dest := dest; src := src; - if err = clear_if_uninitialized(dest); err != .None { + if err = clear_if_uninitialized(dest); err != nil { return err; } /* Grow destination as required. */ if dest != src { - if err = grow(dest, src.used); err != .None { + if err = grow(dest, src.used); err != nil { return err; } } @@ -397,14 +397,14 @@ shr1 :: halve; */ int_double :: proc(dest, src: ^Int) -> (err: Error) { dest := dest; src := src; - if err = clear_if_uninitialized(dest); err != .None { + if err = clear_if_uninitialized(dest); err != nil { return err; } /* Grow destination as required. */ if dest != src { - if err = grow(dest, src.used + 1); err != .None { + if err = grow(dest, src.used + 1); err != nil { return err; } } @@ -462,8 +462,8 @@ shl1 :: double; remainder = numerator % (1 << bits) */ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { - if err = clear_if_uninitialized(remainder); err != .None { return err; } - if err = clear_if_uninitialized(numerator); err != .None { return err; } + if err = clear_if_uninitialized(remainder); err != nil { return err; } + if err = clear_if_uninitialized(numerator); err != nil { return err; } if bits < 0 { return .Invalid_Argument; } if bits == 0 { return zero(remainder); } @@ -472,7 +472,7 @@ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { If the modulus is larger than the value, return the value. */ err = copy(remainder, numerator); - if bits >= (numerator.used * _DIGIT_BITS) || err != .None { + if bits >= (numerator.used * _DIGIT_BITS) || err != nil { return; } @@ -499,7 +499,7 @@ mod_bits :: proc { int_mod_bits, }; Multiply by a DIGIT. */ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { - if err = clear_if_uninitialized(src, dest); err != .None { return err; } + if err = clear_if_uninitialized(src, dest); err != nil { return err; } if multiplier == 0 { return zero(dest); @@ -516,14 +516,14 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { } if is_power_of_two(int(multiplier)) { ix: int; - if ix, err = log(multiplier, 2); err != .None { return err; } + if ix, err = log(multiplier, 2); err != nil { return err; } return shl(dest, src, ix); } /* Ensure `dest` is big enough to hold `src` * `multiplier`. */ - if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } + if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } /* Save the original used count. @@ -575,7 +575,7 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { High level multiplication (handles sign). */ int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(dest, src, multiplier); err != .None { return err; } + if err = clear_if_uninitialized(dest, src, multiplier); err != nil { return err; } /* Early out for `multiplier` is zero; Set `dest` to zero. @@ -657,10 +657,10 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E /* Early out if neither of the results is wanted. */ - if quotient == nil && remainder == nil { return .None; } + if quotient == nil && remainder == nil { return nil; } - if err = clear_if_uninitialized(numerator); err != .None { return err; } - if err = clear_if_uninitialized(denominator); err != .None { return err; } + if err = clear_if_uninitialized(numerator); err != nil { return err; } + if err = clear_if_uninitialized(denominator); err != nil { return err; } z: bool; if z, err = is_zero(denominator); z { return .Division_by_Zero; } @@ -671,12 +671,12 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E c: int; if c, err = cmp_mag(numerator, denominator); c == -1 { if remainder != nil { - if err = copy(remainder, numerator); err != .None { return err; } + if err = copy(remainder, numerator); err != nil { return err; } } if quotient != nil { zero(quotient); } - return .None; + return nil; } if false && (denominator.used > 2 * _MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) { @@ -702,10 +702,10 @@ div :: proc { int_div, }; denominator < remainder <= 0 if denominator < 0 */ int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { - if err = divmod(nil, remainder, numerator, denominator); err != .None { return err; } + if err = divmod(nil, remainder, numerator, denominator); err != nil { return err; } z: bool; - if z, err = is_zero(remainder); z || denominator.sign == remainder.sign { return .None; } + if z, err = is_zero(remainder); z || denominator.sign == remainder.sign { return nil; } return add(remainder, remainder, numerator); } mod :: proc { int_mod, }; @@ -714,7 +714,7 @@ mod :: proc { int_mod, }; remainder = (number + addend) % modulus. */ int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { - if err = add(remainder, number, addend); err != .None { return err; } + if err = add(remainder, number, addend); err != nil { return err; } return mod(remainder, remainder, modulus); } addmod :: proc { int_addmod, }; @@ -723,7 +723,7 @@ addmod :: proc { int_addmod, }; remainder = (number - decrease) % modulus. */ int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { - if err = add(remainder, number, decrease); err != .None { return err; } + if err = add(remainder, number, decrease); err != nil { return err; } return mod(remainder, remainder, modulus); } submod :: proc { int_submod, }; @@ -732,7 +732,7 @@ submod :: proc { int_submod, }; remainder = (number * multiplicand) % modulus. */ int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { - if err = mul(remainder, number, multiplicand); err != .None { return err; } + if err = mul(remainder, number, multiplicand); err != nil { return err; } return mod(remainder, remainder, modulus); } mulmod :: proc { int_mulmod, }; @@ -741,7 +741,7 @@ mulmod :: proc { int_mulmod, }; remainder = (number * number) % modulus. */ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { - if err = sqr(remainder, number); err != .None { return err; } + if err = sqr(remainder, number); err != nil { return err; } return mod(remainder, remainder, modulus); } sqrmod :: proc { int_sqrmod, }; @@ -762,13 +762,13 @@ int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { return int_factorial_binary_split(res, n); } - if err = set(res, _factorial_table[i - 1]); err != .None { return err; } + if err = set(res, _factorial_table[i - 1]); err != nil { return err; } for { - if err = mul(res, res, DIGIT(i)); err != .None || i == n { return err; } + if err = mul(res, res, DIGIT(i)); err != nil || i == n { return err; } i += 1; } - return .None; + return nil; } _int_recursive_product :: proc(res: ^Int, start, stop: DIGIT, level := int(0)) -> (err: Error) { @@ -779,15 +779,15 @@ _int_recursive_product :: proc(res: ^Int, start, stop: DIGIT, level := int(0)) - num_factors := (stop - start) >> 1; if num_factors == 2 { - if err = set(t1, start); err != .None { return err; } - if err = add(t2, t1, 2); err != .None { return err; } + if err = set(t1, start); err != nil { return err; } + if err = add(t2, t1, 2); err != nil { return err; } return mul(res, t1, t2); } if num_factors > 1 { mid := (start + num_factors) | 1; - if err = _int_recursive_product(t1, start, mid, level + 1); err != .None { return err; } - if err = _int_recursive_product(t2, mid, stop, level + 1); err != .None { return err; } + if err = _int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } + if err = _int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } return mul(res, t1, t2); } @@ -805,17 +805,17 @@ int_factorial_binary_split :: proc(res: ^Int, n: DIGIT) -> (err: Error) { inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(inner, outer, start, stop, temp); - if err = one(inner); err != .None { return err; } - if err = one(outer); err != .None { return err; } + if err = one(inner); err != nil { return err; } + if err = one(outer); err != nil { return err; } bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); for i := bits_used; i >= 0; i -= 1 { start := (n >> (uint(i) + 1)) + 1 | 1; stop := (n >> uint(i)) + 1 | 1; - if err = _int_recursive_product(temp, start, stop); err != .None { return err; } - if err = mul(inner, inner, temp); err != .None { return err; } - if err = mul(outer, outer, inner); err != .None { return err; } + if err = _int_recursive_product(temp, start, stop); err != nil { return err; } + if err = mul(inner, inner, temp); err != nil { return err; } + if err = mul(outer, outer, inner); err != nil { return err; } } shift := n - intrinsics.count_ones(n); @@ -841,7 +841,7 @@ factorial :: proc { int_factorial, }; */ int_choose_digit :: proc(res: ^Int, n, k: DIGIT) -> (err: Error) { if res == nil { return .Invalid_Pointer; } - if err = clear_if_uninitialized(res); err != .None { return err; } + if err = clear_if_uninitialized(res); err != nil { return err; } if k > n { return zero(res); } @@ -851,12 +851,12 @@ int_choose_digit :: proc(res: ^Int, n, k: DIGIT) -> (err: Error) { n_fac, k_fac, n_minus_k_fac := &Int{}, &Int{}, &Int{}; defer destroy(n_fac, k_fac, n_minus_k_fac); - if err = factorial(n_minus_k_fac, n - k); err != .None { return err; } - if err = factorial(k_fac, k); err != .None { return err; } - if err = mul(k_fac, k_fac, n_minus_k_fac); err != .None { return err; } + if err = factorial(n_minus_k_fac, n - k); err != nil { return err; } + if err = factorial(k_fac, k); err != nil { return err; } + if err = mul(k_fac, k_fac, n_minus_k_fac); err != nil { return err; } - if err = factorial(n_fac, n); err != .None { return err; } - if err = div(res, n_fac, k_fac); err != .None { return err; } + if err = factorial(n_fac, n); err != nil { return err; } + if err = div(res, n_fac, k_fac); err != nil { return err; } return err; } @@ -886,7 +886,7 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { max_used = x.used; old_used = dest.used; - if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != .None { + if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } dest.used = max_used + 1; @@ -954,10 +954,10 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { */ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest := dest; x := number; y := decrease; - if err = clear_if_uninitialized(x); err != .None { + if err = clear_if_uninitialized(x); err != nil { return err; } - if err = clear_if_uninitialized(y); err != .None { + if err = clear_if_uninitialized(y); err != nil { return err; } @@ -966,7 +966,7 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { max_used := x.used; i: int; - if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != .None { + if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } dest.used = max_used; @@ -1042,7 +1042,7 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { t := &Int{}; - if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } + if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } t.used = digits; /* @@ -1113,7 +1113,7 @@ _int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Grow the destination as required. */ - if err = grow(dest, digits); err != .None { return err; } + if err = grow(dest, digits); err != nil { return err; } /* Number of output digits to produce. @@ -1200,7 +1200,7 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { /* Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. */ - if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != .None { return err; } + if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } t.used = (2 * pa) + 1; for ix = 0; ix < pa; ix += 1 { @@ -1267,7 +1267,7 @@ _int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); q := &Int{}; - if err = grow(q, numerator.used); err != .None { return -1, err; } + if err = grow(q, numerator.used); err != nil { return -1, err; } q.used = numerator.used; q.sign = numerator.sign; @@ -1308,7 +1308,7 @@ _int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { swap(q, quotient); } destroy(q); - return remainder, .None; + return remainder, nil; } /* @@ -1320,26 +1320,26 @@ _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (er c: int; goto_end: for { - if err = one(tq); err != .None { break goto_end; } + if err = one(tq); err != nil { break goto_end; } num_bits, _ := count_bits(numerator); den_bits, _ := count_bits(denominator); n := num_bits - den_bits; - if err = abs(ta, numerator); err != .None { break goto_end; } - if err = abs(tb, denominator); err != .None { break goto_end; } - if err = shl(tb, tb, n); err != .None { break goto_end; } - if err = shl(tq, tq, n); err != .None { break goto_end; } + if err = abs(ta, numerator); err != nil { break goto_end; } + if err = abs(tb, denominator); err != nil { break goto_end; } + if err = shl(tb, tb, n); err != nil { break goto_end; } + if err = shl(tq, tq, n); err != nil { break goto_end; } for n >= 0 { if c, _ = cmp_mag(ta, tb); c == 0 || c == 1 { // ta -= tb - if err = sub(ta, ta, tb); err != .None { break goto_end; } + if err = sub(ta, ta, tb); err != nil { break goto_end; } // q += tq - if err = add( q, q, tq); err != .None { break goto_end; } + if err = add( q, q, tq); err != nil { break goto_end; } } - if err = shr1(tb, tb); err != .None { break goto_end; } - if err = shr1(tq, tq); err != .None { break goto_end; } + if err = shr1(tb, tb); err != nil { break goto_end; } + if err = shr1(tq, tq); err != nil { break goto_end; } n -= 1; } @@ -1383,7 +1383,7 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain Quick outs. */ if denominator == 1 || numerator.used == 0 { - err = .None; + err = nil; if quotient != nil { err = copy(quotient, numerator); } @@ -1397,7 +1397,7 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain remainder = 1; } if quotient == nil { - return remainder, .None; + return remainder, nil; } return remainder, shr(quotient, numerator, 1); } @@ -1409,7 +1409,7 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain } remainder = int(numerator.digit[0]) & ((1 << uint(ix)) - 1); if quotient == nil { - return remainder, .None; + return remainder, nil; } return remainder, shr(quotient, numerator, int(ix)); @@ -1425,7 +1425,7 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain /* No easy answer [c'est la vie]. Just division. */ - if err = grow(q, numerator.used); err != .None { return 0, err; } + if err = grow(q, numerator.used); err != nil { return 0, err; } q.used = numerator.used; q.sign = numerator.sign; @@ -1448,42 +1448,42 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain swap(q, quotient); } destroy(q); - return remainder, .None; + return remainder, nil; } /* Function computing both GCD and (if target isn't `nil`) also LCM. */ int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != .None { return err; } + if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != nil { return err; } az, _ := is_zero(a); bz, _ := is_zero(b); if az && bz { if res_gcd != nil { - if err = zero(res_gcd); err != .None { return err; } + if err = zero(res_gcd); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != .None { return err; } + if err = zero(res_lcm); err != nil { return err; } } - return .None; + return nil; } else if az { if res_gcd != nil { - if err = abs(res_gcd, b); err != .None { return err; } + if err = abs(res_gcd, b); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != .None { return err; } + if err = zero(res_lcm); err != nil { return err; } } - return .None; + return nil; } else if bz { if res_gcd != nil { - if err = abs(res_gcd, a); err != .None { return err; } + if err = abs(res_gcd, a); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != .None { return err; } + if err = zero(res_lcm); err != nil { return err; } } - return .None; + return nil; } return #force_inline _int_gcd_lcm(res_gcd, res_lcm, a, b); @@ -1521,7 +1521,7 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { If neither result is wanted, we have nothing to do. */ - if res_gcd == nil && res_lcm == nil { return .None; } + if res_gcd == nil && res_lcm == nil { return nil; } /* We need a temporary because `res_gcd` is allowed to be `nil`. @@ -1532,34 +1532,34 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { GCD(0, 0) and LCM(0, 0) are both 0. */ if res_gcd != nil { - if err = zero(res_gcd); err != .None { return err; } + if err = zero(res_gcd); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != .None { return err; } + if err = zero(res_lcm); err != nil { return err; } } - return .None; + return nil; } else if az { /* We can early out with GCD = B and LCM = 0 */ if res_gcd != nil { - if err = abs(res_gcd, b); err != .None { return err; } + if err = abs(res_gcd, b); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != .None { return err; } + if err = zero(res_lcm); err != nil { return err; } } - return .None; + return nil; } else if bz { /* We can early out with GCD = A and LCM = 0 */ if res_gcd != nil { - if err = abs(res_gcd, a); err != .None { return err; } + if err = abs(res_gcd, a); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != .None { return err; } + if err = zero(res_lcm); err != nil { return err; } } - return .None; + return nil; } temp_gcd_res := &Int{}; @@ -1571,8 +1571,8 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { */ u, v := &Int{}, &Int{}; defer destroy(u, v); - if err = copy(u, a); err != .None { return err; } - if err = copy(v, b); err != .None { return err; } + if err = copy(u, a); err != nil { return err; } + if err = copy(v, b); err != nil { return err; } /* Must be positive for the remainder of the algorithm. @@ -1590,18 +1590,18 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { /* Divide the power of two out. */ - if err = shr(u, u, k); err != .None { return err; } - if err = shr(v, v, k); err != .None { return err; } + if err = shr(u, u, k); err != nil { return err; } + if err = shr(v, v, k); err != nil { return err; } } /* Divide any remaining factors of two out. */ if u_lsb != k { - if err = shr(u, u, u_lsb - k); err != .None { return err; } + if err = shr(u, u, u_lsb - k); err != nil { return err; } } if v_lsb != k { - if err = shr(v, v, v_lsb - k); err != .None { return err; } + if err = shr(v, v, v_lsb - k); err != nil { return err; } } for v.used != 0 { @@ -1618,19 +1618,19 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { /* Subtract smallest from largest. */ - if err = sub(v, v, u); err != .None { return err; } + if err = sub(v, v, u); err != nil { return err; } /* Divide out all factors of two. */ b, _ := count_lsb(v); - if err = shr(v, v, b); err != .None { return err; } + if err = shr(v, v, b); err != nil { return err; } } /* Multiply by 2**k which we divided out at the beginning. */ - if err = shl(temp_gcd_res, u, k); err != .None { return err; } + if err = shl(temp_gcd_res, u, k); err != nil { return err; } temp_gcd_res.sign = .Zero_or_Positive; /* @@ -1639,7 +1639,7 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { */ if res_lcm == nil { swap(temp_gcd_res, res_gcd); - return .None; + return nil; } /* @@ -1650,13 +1650,13 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { /* Store quotient in `t2` such that `t2 * b` is the LCM. */ - if err = div(res_lcm, a, temp_gcd_res); err != .None { return err; } + if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } err = mul(res_lcm, res_lcm, b); } else { /* Store quotient in `t2` such that `t2 * a` is the LCM. */ - if err = div(res_lcm, a, temp_gcd_res); err != .None { return err; } + if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } err = mul(res_lcm, res_lcm, b); } diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 5a51c7405..cad6524e5 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -64,11 +64,18 @@ Int :: struct { sign: Sign, }; +Flag :: enum u8 { + NaN, + Inf, + Immutable, +}; + +Flags :: bit_set[Flag; u8]; + /* Errors are a strict superset of runtime.Allocation_Error. */ Error :: enum int { - None = 0, Out_Of_Memory = 1, Invalid_Pointer = 2, Invalid_Argument = 3, @@ -85,7 +92,6 @@ Error :: enum int { }; Error_String :: #partial [Error]string{ - .None = "None", .Out_Of_Memory = "Out of memory", .Invalid_Pointer = "Invalid pointer", .Invalid_Argument = "Invalid argument", diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index 43e872ce1..3d4f220fa 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -23,47 +23,47 @@ int_is_initialized :: proc(a: ^Int) -> bool { } int_is_zero :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return false, err; } - return a.used == 0, .None; + return a.used == 0, nil; } int_is_positive :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return false, err; } - return a.sign == .Zero_or_Positive, .None; + return a.sign == .Zero_or_Positive, nil; } int_is_negative :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return false, err; } - return a.sign == .Negative, .None; + return a.sign == .Negative, nil; } int_is_even :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return false, err; } res, err = is_zero(a); - if err != .None { + if err != nil { return false, err; } else if res == true { - return true, .None; + return true, nil; } res = false; if a.used > 0 && a.digit[0] & 1 == 0 { res = true; } - return res, .None; + return res, nil; } int_is_odd :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return false, err; } @@ -76,7 +76,7 @@ platform_int_is_power_of_two :: proc(a: int) -> bool { } int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return false, err; } @@ -84,21 +84,21 @@ int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { Early out for Int == 0. */ if a.used == 0 { - return false, .None; + return false, nil; } /* For an `Int` to be a power of two, its top limb has to be a power of two. */ if !platform_int_is_power_of_two(int(a.digit[a.used - 1])) { - return false, .None; + return false, nil; } /* That was the only limb, so it's a power of two. */ if a.used == 1 { - return true, .None; + return true, nil; } /* @@ -106,32 +106,32 @@ int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { */ for i := 1; i < a.used; i += 1 { if a.digit[i - 1] != 0 { - return false, .None; + return false, nil; } } - return true, .None; + return true, nil; } /* Compare two `Int`s, signed. */ int_compare :: proc(a, b: ^Int) -> (res: int, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } - if err = clear_if_uninitialized(b); err != .None { + if err = clear_if_uninitialized(b); err != nil { return 0, err; } neg: bool; - if neg, err = is_negative(a); err != .None { + if neg, err = is_negative(a); err != nil { return 0, err; } /* Compare based on sign */ if a.sign != b.sign { res = -1 if neg else +1; - return res, .None; + return res, nil; } /* If negative, compare in the opposite direction */ @@ -145,63 +145,63 @@ int_compare :: proc(a, b: ^Int) -> (res: int, err: Error) { Compare an `Int` to an unsigned number upto the size of the backing type. */ int_compare_digit :: proc(a: ^Int, u: DIGIT) -> (res: int, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } /* Compare based on sign */ neg: bool; - if neg, err = is_neg(a); err != .None { + if neg, err = is_neg(a); err != nil { return 0, err; } if neg { - return -1, .None; + return -1, nil; } /* Compare based on magnitude */ if a.used > 1 { - return +1, .None; + return +1, nil; } /* Compare the only digit in `a` to `u`. */ if a.digit[0] != u { if a.digit[0] > u { - return +1, .None; + return +1, nil; } - return -1, .None; + return -1, nil; } - return 0, .None; + return 0, nil; } /* Compare the magnitude of two `Int`s, unsigned. */ int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } - if err = clear_if_uninitialized(b); err != .None { + if err = clear_if_uninitialized(b); err != nil { return 0, err; } /* Compare based on used digits */ if a.used != b.used { if a.used > b.used { - return +1, .None; + return +1, nil; } - return -1, .None; + return -1, nil; } /* Same number of used digits, compare based on their value */ for n := a.used - 1; n >= 0; n -= 1 { if a.digit[n] != b.digit[n] { if a.digit[n] > b.digit[n] { - return +1, .None; + return +1, nil; } - return -1, .None; + return -1, nil; } } - return 0, .None; + return 0, nil; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 7c2df9303..7dc6329c6 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -102,7 +102,7 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := false, newlin } else { fmt.printf("%v", as); } - if err != .None { + if err != nil { fmt.printf("%v (error: %v | %v)", name, err, a); } if newline { @@ -118,19 +118,22 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - N :: 5_000; + factorial(a, 128); // Untimed warmup. + + N :: 128; s := time.tick_now(); err = factorial(a, N); Timings[.factorial].t += time.tick_since(s); Timings[.factorial].c += 1; - if err != .None { + + if err != nil { fmt.printf("factorial(%v) returned %v\n", N, err); } s = time.tick_now(); as, err = itoa(a, 16); Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; - if err != .None { + if err != nil { fmt.printf("itoa(factorial(%v), 16) returned %v\n", N, err); } diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index 746470956..8fba8cb81 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -13,7 +13,7 @@ int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { if base < 2 || DIGIT(base) > _DIGIT_MAX { return -1, .Invalid_Argument; } - if err = clear_if_uninitialized(a); err != .None { return -1, err; } + if err = clear_if_uninitialized(a); err != nil { return -1, err; } if n, _ := is_neg(a); n { return -1, .Math_Domain_Error; } if z, _ := is_zero(a); z { return -1, .Math_Domain_Error; } @@ -38,8 +38,8 @@ log :: proc { int_log, int_log_digit, }; */ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { power := power; - if err = clear_if_uninitialized(base); err != .None { return err; } - if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(base); err != nil { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } /* Early outs. */ @@ -48,7 +48,7 @@ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { A zero base is a special case. */ if power < 0 { - if err = zero(dest); err != .None { return err; } + if err = zero(dest); err != nil { return err; } return .Math_Domain_Error; } if power == 0 { return one(dest); } @@ -77,19 +77,19 @@ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { } g := &Int{}; - if err = copy(g, base); err != .None { return err; } + if err = copy(g, base); err != nil { return err; } /* Set initial result. */ - if err = set(dest, 1); err != .None { return err; } + if err = set(dest, 1); err != nil { return err; } loop: for power > 0 { /* If the bit is set, multiply. */ if power & 1 != 0 { - if err = mul(dest, g, dest); err != .None { + if err = mul(dest, g, dest); err != nil { break loop; } } @@ -97,7 +97,7 @@ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { Square. */ if power > 1 { - if err = sqr(g, g); err != .None { + if err = sqr(g, g); err != nil { break loop; } } @@ -117,7 +117,7 @@ int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { base_t := &Int{}; defer destroy(base_t); - if err = set(base_t, base); err != .None { return err; } + if err = set(base_t, base); err != nil { return err; } return int_pow(dest, base_t, power); } @@ -163,14 +163,14 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { Therefore, we return 0. */ if a < base { - return 0, .None; + return 0, nil; } /* If a number equals the base, the log is 1. */ if a == base { - return 1, .None; + return 1, nil; } N := _WORD(a); @@ -199,14 +199,14 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { bracket_low = bracket_mid; } if N == bracket_mid { - return mid, .None; + return mid, nil; } } if bracket_high == N { - return high, .None; + return high, nil; } else { - return low, .None; + return low, nil; } } @@ -216,8 +216,8 @@ int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { when true { - if err = clear_if_uninitialized(dest); err != .None { return err; } - if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } /* Must be positive. */ if src.sign == .Negative { return .Invalid_Argument; } @@ -230,10 +230,10 @@ int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { defer destroy(x, y, t1, t2); count: int; - if count, err = count_bits(src); err != .None { return err; } + if count, err = count_bits(src); err != nil { return err; } a, b := count >> 1, count & 1; - if err = power_of_two(x, a+b); err != .None { return err; } + if err = power_of_two(x, a+b); err != nil { return err; } for { /* @@ -245,7 +245,7 @@ int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { if c, _ := cmp(y, x); c == 0 || c == 1 { swap(dest, x); - return .None; + return nil; } swap(x, y); } @@ -271,8 +271,8 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { if n == 2 { return sqrt(dest, src); } /* Initialize dest + src if needed. */ - if err = clear_if_uninitialized(dest); err != .None { return err; } - if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } if n < 0 || n > int(_DIGIT_MAX) { return .Invalid_Argument; @@ -280,7 +280,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { neg: bool; if n & 1 == 0 { - if neg, err = is_neg(src); neg || err != .None { return .Invalid_Argument; } + if neg, err = is_neg(src); neg || err != nil { return .Invalid_Argument; } } /* Set up temporaries. */ @@ -323,33 +323,33 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { /* Start value must be larger than root. */ ilog2 += 2; - if err = power_of_two(t2, ilog2); err != .None { return err; } + if err = power_of_two(t2, ilog2); err != nil { return err; } c: int; iterations := 0; for { /* t1 = t2 */ - if err = copy(t1, t2); err != .None { return err; } + if err = copy(t1, t2); err != nil { return err; } /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ /* t3 = t1**(b-1) */ - if err = pow(t3, t1, n-1); err != .None { return err; } + if err = pow(t3, t1, n-1); err != nil { return err; } /* numerator */ /* t2 = t1**b */ - if err = mul(t2, t1, t3); err != .None { return err; } + if err = mul(t2, t1, t3); err != nil { return err; } /* t2 = t1**b - a */ - if err = sub(t2, t2, a); err != .None { return err; } + if err = sub(t2, t2, a); err != nil { return err; } /* denominator */ /* t3 = t1**(b-1) * b */ - if err = mul(t3, t3, DIGIT(n)); err != .None { return err; } + if err = mul(t3, t3, DIGIT(n)); err != nil { return err; } /* t3 = (t1**b - a)/(b * t1**(b-1)) */ - if err = div(t3, t2, t3); err != .None { return err; } - if err = sub(t2, t1, t3); err != .None { return err; } + if err = div(t3, t2, t3); err != nil { return err; } + if err = sub(t2, t1, t3); err != nil { return err; } /* Number of rounds is at most log_2(root). If it is more it @@ -370,14 +370,14 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { iterations = 0; for { - if err = pow(t2, t1, n); err != .None { return err; } + if err = pow(t2, t1, n); err != nil { return err; } c, err = cmp(t2, a); if c == 0 { swap(dest, t1); - return .None; + return nil; } else if c == -1 { - if err = add(t1, t1, DIGIT(1)); err != .None { return err; } + if err = add(t1, t1, DIGIT(1)); err != nil { return err; } } else { break; } @@ -391,11 +391,11 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { iterations = 0; /* Correct overshoot from above or from recurrence. */ for { - if err = pow(t2, t1, n); err != .None { return err; } + if err = pow(t2, t1, n); err != nil { return err; } c, err = cmp(t2, a); if c == 1 { - if err = sub(t1, t1, DIGIT(1)); err != .None { return err; } + if err = sub(t1, t1, DIGIT(1)); err != nil { return err; } } else { break; } @@ -424,13 +424,13 @@ _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { ic, _ := cmp(a, base); if ic == -1 || ic == 0 { - return 1 if ic == 0 else 0, .None; + return 1 if ic == 0 else 0, nil; } - if err = set(bi_base, base); err != .None { return -1, err; } - if err = init_multi(bracket_mid, t); err != .None { return -1, err; } - if err = one(bracket_low); err != .None { return -1, err; } - if err = set(bracket_high, base); err != .None { return -1, err; } + if err = set(bi_base, base); err != nil { return -1, err; } + if err = init_multi(bracket_mid, t); err != nil { return -1, err; } + if err = one(bracket_low); err != nil { return -1, err; } + if err = set(bracket_high, base); err != nil { return -1, err; } low := 0; high := 1; @@ -450,12 +450,12 @@ _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { } low = high; - if err = copy(bracket_low, bracket_high); err != .None { + if err = copy(bracket_low, bracket_high); err != nil { destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); return -1, err; } high <<= 1; - if err = sqr(bracket_high, bracket_high); err != .None { + if err = sqr(bracket_high, bracket_high); err != nil { destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); return -1, err; } @@ -464,12 +464,12 @@ _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { for (high - low) > 1 { mid := (high + low) >> 1; - if err = pow(t, bi_base, mid - low); err != .None { + if err = pow(t, bi_base, mid - low); err != nil { destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); return -1, err; } - if err = mul(bracket_mid, bracket_low, t); err != .None { + if err = mul(bracket_mid, bracket_low, t); err != nil { destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); return -1, err; } @@ -484,7 +484,7 @@ _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { } if mc == 0 { destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return mid, .None; + return mid, nil; } } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index a3b4f13ba..c14690279 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -35,7 +35,7 @@ int_destroy :: proc(integers: ..^Int) { int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) where intrinsics.type_is_integer(T) { src := src; - if err = clear_if_uninitialized(dest); err != .None { + if err = clear_if_uninitialized(dest); err != nil { return err; } dest.used = 0; @@ -48,7 +48,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : src >>= _DIGIT_BITS; } _zero_unused(dest); - return .None; + return nil; } set :: proc { int_set_from_integer, int_copy }; @@ -57,18 +57,18 @@ set :: proc { int_set_from_integer, int_copy }; Copy one `Int` to another. */ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { - if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } /* If dest == src, do nothing */ if (dest == src) { - return .None; + return nil; } /* Grow `dest` to fit `src`. If `dest` is not yet initialized, it will be using `allocator`. */ - if err = grow(dest, src.used, false, allocator); err != .None { + if err = grow(dest, src.used, false, allocator); err != nil { return err; } @@ -81,7 +81,7 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error dest.used = src.used; dest.sign = src.sign; _zero_unused(dest); - return .None; + return nil; } copy :: proc { int_copy, }; @@ -106,7 +106,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) /* Check that src is usable. */ - if err = clear_if_uninitialized(src); err != .None { + if err = clear_if_uninitialized(src); err != nil { return err; } /* @@ -114,13 +114,13 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) */ if (dest == src) { dest.sign = .Zero_or_Positive; - return .None; + return nil; } /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != .None { + if err = copy(dest, src, allocator); err != nil { return err; } @@ -128,7 +128,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) Fix sign. */ dest.sign = .Zero_or_Positive; - return .None; + return nil; } platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { @@ -143,7 +143,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Check that src is usable. */ - if err = clear_if_uninitialized(src); err != .None { + if err = clear_if_uninitialized(src); err != nil { return err; } /* @@ -158,12 +158,12 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { } if (dest == src) { dest.sign = sign; - return .None; + return nil; } /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != .None { + if err = copy(dest, src, allocator); err != nil { return err; } @@ -171,7 +171,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { Fix sign. */ dest.sign = sign; - return .None; + return nil; } /* @@ -181,7 +181,7 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { /* Check that `a`is usable. */ - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } @@ -192,7 +192,7 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); - return 1 if ((a.digit[limb] & i) != 0) else 0, .None; + return 1 if ((a.digit[limb] & i) != 0) else 0, nil; } /* @@ -202,7 +202,7 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: E /* Check that `a`is usable. */ - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } @@ -217,7 +217,7 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: E for shift := 0; shift < count; shift += 1 { o := offset + shift; v, e = extract_bit(a, o); - if e != .None { + if e != nil { break; } res = res + _WORD(v) << uint(shift); @@ -258,7 +258,7 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: E res |= _WORD(r); } - return res, .None; + return res, nil; } } @@ -276,7 +276,7 @@ shrink :: proc(a: ^Int) -> (err: Error) { if a.used != needed { return grow(a, needed); } - return .None; + return nil; } int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { @@ -309,7 +309,7 @@ int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := conte if len(a.digit) != needed { return .Out_Of_Memory; } - return .None; + return nil; } grow :: proc { int_grow, }; @@ -337,12 +337,12 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != .None { return err; } + if err = clear(a, minimize, allocator); err != nil { return err; } a.used = 1; a.digit[0] = 1; a.sign = .Zero_or_Positive; - return .None; + return nil; } one :: proc { int_one, }; @@ -350,14 +350,14 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != .None { + if err = clear(a, minimize, allocator); err != nil { return err; } a.used = 1; a.digit[0] = 1; a.sign = .Negative; - return .None; + return nil; } minus_one :: proc { int_minus_one, }; @@ -377,7 +377,7 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Grow to accomodate the single bit. */ a.used = (power / _DIGIT_BITS) + 1; - if err = grow(a, a.used); err != .None { + if err = grow(a, a.used); err != nil { return err; } /* @@ -389,7 +389,7 @@ power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Set the bit. */ a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS)); - return .None; + return nil; } int_get_u128 :: proc(a: ^Int) -> (res: u128, err: Error) { @@ -427,7 +427,7 @@ get_i32 :: proc { int_get_i32, }; and maybe return max(T), .Integer_Overflow if not? */ int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { - if err = clear_if_uninitialized(a); err != .None { return 0, err; } + if err = clear_if_uninitialized(a); err != nil { return 0, err; } size_in_bits := int(size_of(T) * 8); i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS); @@ -458,7 +458,7 @@ int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.ty get :: proc { int_get, }; int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } @@ -478,14 +478,14 @@ int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { Count bits in an `Int`. */ count_bits :: proc(a: ^Int) -> (count: int, err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } /* Fast path for zero. */ if z, _ := is_zero(a); z { - return 0, .None; + return 0, nil; } /* Get the number of DIGITs and use it. @@ -504,13 +504,13 @@ count_bits :: proc(a: ^Int) -> (count: int, err: Error) { Differs from regular `ctz` in that 0 returns 0. */ int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { - if err = clear_if_uninitialized(a); err != .None { return -1, err; } + if err = clear_if_uninitialized(a); err != nil { return -1, err; } _ctz :: intrinsics.count_trailing_zeros; /* Easy out. */ - if z, _ := is_zero(a); z { return 0, .None; } + if z, _ := is_zero(a); z { return 0, nil; } /* Scan lower digits until non-zero. @@ -520,7 +520,7 @@ int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { q := a.digit[x]; x *= _DIGIT_BITS; - return x + count_lsb(q), .None; + return x + count_lsb(q), nil; } platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) @@ -554,7 +554,7 @@ int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Error) { digits += 1; } - if err = grow(dest, digits); err != .None { return err; } + if err = grow(dest, digits); err != nil { return err; } for i := 0; i < digits; i += 1 { dest.digit[i] = int_random_digit(r) & _MASK; @@ -563,7 +563,7 @@ int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Error) { dest.digit[digits - 1] &= ((1 << uint(bits)) - 1); } dest.used = digits; - return .None; + return nil; } rand :: proc { int_rand, }; @@ -597,7 +597,7 @@ clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { for i in args { if i != nil && !is_initialized(i) { e := grow(i, _DEFAULT_DIGIT_COUNT); - if e != .None { err = e; } + if e != nil { err = e; } } } return err; @@ -611,27 +611,27 @@ clear_if_uninitialized :: proc {clear_if_uninitialized_single, clear_if_uninitia int_init_multi :: proc(integers: ..^Int) -> (err: Error) { integers := integers; for a in &integers { - if err = clear(a); err != .None { return err; } + if err = clear(a); err != nil { return err; } } - return .None; + return nil; } init_multi :: proc { int_init_multi, }; _copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { digits := digits; - if err = clear_if_uninitialized(src); err != .None { return err; } - if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } /* If dest == src, do nothing */ if (dest == src) { - return .None; + return nil; } digits = min(digits, len(src.digit), len(dest.digit)); mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); - return .None; + return nil; } /* @@ -641,7 +641,7 @@ _copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { Typically very fast. Also fixes the sign if there are no more leading digits. */ clamp :: proc(a: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return err; } for a.used > 0 && a.digit[a.used - 1] == 0 { @@ -651,7 +651,7 @@ clamp :: proc(a: ^Int) -> (err: Error) { if z, _ := is_zero(a); z { a.sign = .Zero_or_Positive; } - return .None; + return nil; } diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 1e11f1a35..3f3d9d6fe 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -24,13 +24,13 @@ import "core:mem" 2's complement `and`, returns `dest = a & b;` */ int_and :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a, b); err != .None { return err; } + if err = clear_if_uninitialized(a, b); err != nil { return err; } used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .None { return err; } + if err = grow(dest, used); err != nil { return err; } neg_a, _ := is_neg(a); neg_b, _ := is_neg(b); @@ -85,12 +85,12 @@ and :: proc { int_add, }; 2's complement `or`, returns `dest = a | b;` */ int_or :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a, b); err != .None { return err; } + if err = clear_if_uninitialized(a, b); err != nil { return err; } used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .None { return err; } + if err = grow(dest, used); err != nil { return err; } neg_a, _ := is_neg(a); neg_b, _ := is_neg(b); @@ -145,13 +145,13 @@ or :: proc { int_or, }; 2's complement `xor`, returns `dest = a ~ b;` */ int_xor :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a, b); err != .None { return err; } + if err = clear_if_uninitialized(a, b); err != nil { return err; } used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = grow(dest, used); err != .None { return err; } + if err = grow(dest, used); err != nil { return err; } neg_a, _ := is_neg(a); neg_b, _ := is_neg(b); @@ -209,7 +209,7 @@ int_complement :: proc(dest, src: ^Int) -> (err: Error) { /* Check that src is usable. Dest will get checked by `sub`. */ - if err = clear_if_uninitialized(src); err != .None { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } /* Temporarily fix sign. @@ -235,25 +235,25 @@ complement :: proc { int_complement, }; */ int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int) -> (err: Error) { bits := bits; - if err = clear_if_uninitialized(quotient, numerator); err != .None { return err; } + if err = clear_if_uninitialized(quotient, numerator); err != nil { return err; } if bits < 0 { return .Invalid_Argument; } - if err = copy(quotient, numerator); err != .None { return err; } + if err = copy(quotient, numerator); err != nil { return err; } /* Shift right by a certain bit count (store quotient and optional remainder.) `numerator` should not be used after this. */ if remainder != nil { - if err = mod_bits(remainder, numerator, bits); err != .None { return err; } + if err = mod_bits(remainder, numerator, bits); err != nil { return err; } } /* Shift by as many digits in the bit count. */ if bits >= _DIGIT_BITS { - if err = shr_digit(quotient, bits / _DIGIT_BITS); err != .None { return err; } + if err = shr_digit(quotient, bits / _DIGIT_BITS); err != nil { return err; } } /* @@ -299,9 +299,9 @@ int_shr_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { /* Check that `quotient` is usable. */ - if err = clear_if_uninitialized(quotient); err != .None { return err; } + if err = clear_if_uninitialized(quotient); err != nil { return err; } - if digits <= 0 { return .None; } + if digits <= 0 { return nil; } /* If digits > used simply zero and return. @@ -332,15 +332,15 @@ shr_digit :: proc { int_shr_digit, }; Shift right by a certain bit count with sign extension. */ int_shr_signed :: proc(dest, src: ^Int, bits: int) -> (err: Error) { - if err = clear_if_uninitialized(src); err != .None { return err; } - if err = clear_if_uninitialized(dest); err != .None { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } if src.sign == .Zero_or_Positive { return shr(dest, src, bits); } - if err = add(dest, src, DIGIT(1)); err != .None { return err; } + if err = add(dest, src, DIGIT(1)); err != nil { return err; } - if err = shr(dest, dest, bits); err != .None { return err; } + if err = shr(dest, dest, bits); err != nil { return err; } return sub(dest, src, DIGIT(1)); } @@ -351,25 +351,25 @@ shr_signed :: proc { int_shr_signed, }; */ int_shl :: proc(dest, src: ^Int, bits: int) -> (err: Error) { bits := bits; - if err = clear_if_uninitialized(src, dest); err != .None { return err; } + if err = clear_if_uninitialized(src, dest); err != nil { return err; } if bits < 0 { return .Invalid_Argument; } - if err = copy(dest, src); err != .None { return err; } + if err = copy(dest, src); err != nil { return err; } /* Grow `dest` to accommodate the additional bits. */ digits_needed := dest.used + (bits / _DIGIT_BITS) + 1; - if err = grow(dest, digits_needed); err != .None { return err; } + if err = grow(dest, digits_needed); err != nil { return err; } dest.used = digits_needed; /* Shift by as many digits in the bit count as we have. */ if bits >= _DIGIT_BITS { - if err = shl_digit(dest, bits / _DIGIT_BITS); err != .None { return err; } + if err = shl_digit(dest, bits / _DIGIT_BITS); err != nil { return err; } } /* @@ -407,20 +407,20 @@ int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { /* Check that `quotient` is usable. */ - if err = clear_if_uninitialized(quotient); err != .None { return err; } + if err = clear_if_uninitialized(quotient); err != nil { return err; } - if digits <= 0 { return .None; } + if digits <= 0 { return nil; } /* No need to shift a zero. */ z: bool; - if z, err = is_zero(quotient); z || err != .None { return err; } + if z, err = is_zero(quotient); z || err != nil { return err; } /* Resize `quotient` to accomodate extra digits. */ - if err = grow(quotient, quotient.used + digits); err != .None { return err; } + if err = grow(quotient, quotient.used + digits); err != nil { return err; } /* Increment the used by the shift amount then copy upwards. @@ -436,6 +436,6 @@ int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { quotient.used += digits; mem.zero_slice(quotient.digit[:digits]); - return .None; + return nil; } shl_digit :: proc { int_shl_digit, }; \ No newline at end of file diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index ef5b0079e..97c77f144 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -19,7 +19,7 @@ import "core:mem" */ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { a := a; radix := radix; - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return "", err; } /* @@ -39,7 +39,7 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc /* Exit if calculating the size returned an error. */ - if size, err = radix_size(a, radix, zero_terminate); err != .None { + if size, err = radix_size(a, radix, zero_terminate); err != nil { return "", err; } @@ -62,7 +62,7 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc */ int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) { a := a; radix := radix; - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return "", err; } /* @@ -97,7 +97,7 @@ int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocato */ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) { a := a; radix := radix; size := size; - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } /* @@ -112,7 +112,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter We weren't given a size. Let's compute it. */ if size == -1 { - if size, err = radix_size(a, radix, zero_terminate); err != .None { + if size, err = radix_size(a, radix, zero_terminate); err != nil { return 0, err; } } @@ -149,7 +149,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter diff := size - written; mem.copy(&buffer[0], &buffer[diff], written); } - return written, .None; + return written, nil; } /* @@ -182,7 +182,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter diff := size - written; mem.copy(&buffer[0], &buffer[diff], written); } - return written, .None; + return written, nil; } /* @@ -202,7 +202,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter for offset := 0; offset < count; offset += shift { bits_to_get := int(min(count - offset, shift)); - if digit, err = int_bitfield_extract(a, offset, bits_to_get); err != .None { + if digit, err = int_bitfield_extract(a, offset, bits_to_get); err != nil { return len(buffer) - available, .Invalid_Argument; } available -= 1; @@ -222,7 +222,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter diff := size - written; mem.copy(&buffer[0], &buffer[diff], written); } - return written, .None; + return written, nil; } return _itoa_raw_full(a, radix, buffer, zero_terminate); @@ -248,13 +248,13 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { /* Set the integer to the default of zero. */ - if err = zero(res); err != .None { return err; } + if err = zero(res); err != nil { return err; } /* We'll interpret an empty string as zero. */ if len(input) == 0 { - return .None; + return nil; } /* @@ -295,8 +295,8 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { break; } - if err = mul(res, res, DIGIT(radix)); err != .None { return err; } - if err = add(res, res, DIGIT(y)); err != .None { return err; } + if err = mul(res, res, DIGIT(radix)); err != nil { return err; } + if err = add(res, res, DIGIT(y)); err != nil { return err; } input = input[1:]; } @@ -313,7 +313,7 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { res.sign = sign; } - return .None; + return nil; } @@ -327,15 +327,15 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e if radix < 2 || radix > 64 { return -1, .Invalid_Argument; } - if err = clear_if_uninitialized(a); err != .None { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } if z, _ := is_zero(a); z { if zero_terminate { - return 2, .None; + return 2, nil; } - return 1, .None; + return 1, nil; } if pot, _ := is_power_of_two(a); pot { @@ -348,7 +348,7 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e digit = a.digit, }; - if size, err = log(t, DIGIT(radix)); err != .None { + if size, err = log(t, DIGIT(radix)); err != nil { return 0, err; } } else { @@ -364,8 +364,8 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e err = set(k, lb[radix]); /* n = floor((la * k) / 2^29) + 1 */ - if err = mul(k, la, k); err != .None { return 0, err; } - if err = shr(k, k, _RADIX_SIZE_SCALE); err != .None { return 0, err; } + if err = mul(k, la, k); err != nil { return 0, err; } + if err = shr(k, k, _RADIX_SIZE_SCALE); err != nil { return 0, err; } /* The "+1" here is the "+1" in "floor((la * k) / 2^29) + 1" */ /* n = n + 1 + EOS + sign */ @@ -378,7 +378,7 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e */ size += 2 if a.sign == .Negative else 1; size += 1 if zero_terminate else 0; - return size, .None; + return size, nil; } /* @@ -435,8 +435,8 @@ RADIX_TABLE_REVERSE_SIZE :: 80; _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (written: int, err: Error) { temp, denominator := &Int{}, &Int{}; - if err = copy(temp, a); err != .None { return 0, err; } - if err = set(denominator, radix); err != .None { return 0, err; } + if err = copy(temp, a); err != nil { return 0, err; } + if err = set(denominator, radix); err != nil { return 0, err; } available := len(buffer); if zero_terminate { @@ -450,7 +450,7 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false remainder: int; for { - if remainder, err = _int_div_digit(temp, temp, DIGIT(radix)); err != .None { + if remainder, err = _int_div_digit(temp, temp, DIGIT(radix)); err != nil { destroy(temp, denominator); return len(buffer) - available, err; } @@ -476,5 +476,5 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false diff := len(buffer) - written; mem.copy(&buffer[0], &buffer[diff], written); } - return written, .None; + return written, nil; } \ No newline at end of file diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 52c9eaa3e..fb0b0ea6f 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -33,14 +33,14 @@ PyRes :: struct { aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":add:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":add:atoi(b):", err=err}; } - if err = add(sum, aa, bb); err != .None { return PyRes{res=":add:add(sum,a,b):", err=err}; } + if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err}; } + if err = add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(sum, 16, context.temp_allocator); - if err != .None { return PyRes{res=":add:itoa(sum):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":add:itoa(sum):", err=err}; } + return PyRes{res = r, err = nil}; } @export test_sub :: proc "c" (a, b: cstring) -> (res: PyRes) { @@ -50,14 +50,14 @@ PyRes :: struct { aa, bb, sum := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, sum); - if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":sub:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":sub:atoi(b):", err=err}; } - if err = sub(sum, aa, bb); err != .None { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } + if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err}; } + if err = sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(sum, 16, context.temp_allocator); - if err != .None { return PyRes{res=":sub:itoa(sum):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":sub:itoa(sum):", err=err}; } + return PyRes{res = r, err = nil}; } @export test_mul :: proc "c" (a, b: cstring) -> (res: PyRes) { @@ -67,14 +67,14 @@ PyRes :: struct { aa, bb, product := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, product); - if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":mul:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":mul:atoi(b):", err=err}; } - if err = mul(product, aa, bb); err != .None { return PyRes{res=":mul:mul(product,a,b):", err=err}; } + if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err}; } + if err = mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(product, 16, context.temp_allocator); - if err != .None { return PyRes{res=":mul:itoa(product):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":mul:itoa(product):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -87,14 +87,14 @@ PyRes :: struct { aa, bb, quotient := &Int{}, &Int{}, &Int{}; defer destroy(aa, bb, quotient); - if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":div:atoi(a):", err=err}; } - if err = atoi(bb, string(b), 16); err != .None { return PyRes{res=":div:atoi(b):", err=err}; } - if err = div(quotient, aa, bb); err != .None { return PyRes{res=":div:div(quotient,a,b):", err=err}; } + if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err}; } + if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err}; } + if err = div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(quotient, 16, context.temp_allocator); - if err != .None { return PyRes{res=":div:itoa(quotient):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":div:itoa(quotient):", err=err}; } + return PyRes{res = r, err = nil}; } @@ -109,8 +109,8 @@ PyRes :: struct { aa := &Int{}; defer destroy(aa); - if err = atoi(aa, string(a), 16); err != .None { return PyRes{res=":log:atoi(a):", err=err}; } - if l, err = log(aa, base); err != .None { return PyRes{res=":log:log(a, base):", err=err}; } + if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err}; } + if l, err = log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err}; } zero(aa); aa.digit[0] = DIGIT(l) & _MASK; @@ -120,8 +120,8 @@ PyRes :: struct { r: cstring; r, err = int_itoa_cstring(aa, 16, context.temp_allocator); - if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":log:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -134,13 +134,13 @@ PyRes :: struct { dest, bb := &Int{}, &Int{}; defer destroy(dest, bb); - if err = atoi(bb, string(base), 16); err != .None { return PyRes{res=":pow:atoi(base):", err=err}; } - if err = pow(dest, bb, power); err != .None { return PyRes{res=":pow:pow(dest, base, power):", err=err}; } + if err = atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err}; } + if err = pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); - if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":log:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -153,13 +153,13 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":sqrt:atoi(src):", err=err}; } - if err = sqrt(src, src); err != .None { return PyRes{res=":sqrt:sqrt(src):", err=err}; } + if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err}; } + if err = sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); - if err != .None { return PyRes{res=":log:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":log:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -172,13 +172,13 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":root_n:atoi(src):", err=err}; } - if err = root_n(src, src, power); err != .None { return PyRes{res=":root_n:root_n(src):", err=err}; } + if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err}; } + if err = root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); - if err != .None { return PyRes{res=":root_n:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":root_n:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -191,13 +191,13 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shr_digit:atoi(src):", err=err}; } - if err = shr_digit(src, digits); err != .None { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; } + if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err}; } + if err = shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); - if err != .None { return PyRes{res=":shr_digit:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":shr_digit:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -210,13 +210,13 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shl_digit:atoi(src):", err=err}; } - if err = shl_digit(src, digits); err != .None { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; } + if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err}; } + if err = shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); - if err != .None { return PyRes{res=":shl_digit:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":shl_digit:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -229,13 +229,13 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shr:atoi(src):", err=err}; } - if err = shr(src, src, bits); err != .None { return PyRes{res=":shr:shr(src, bits):", err=err}; } + if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err}; } + if err = shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); - if err != .None { return PyRes{res=":shr:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":shr:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -248,13 +248,13 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shr_signed:atoi(src):", err=err}; } - if err = shr_signed(src, src, bits); err != .None { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; } + if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err}; } + if err = shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); - if err != .None { return PyRes{res=":shr_signed:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":shr_signed:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -267,13 +267,13 @@ PyRes :: struct { src := &Int{}; defer destroy(src); - if err = atoi(src, string(source), 16); err != .None { return PyRes{res=":shl:atoi(src):", err=err}; } - if err = shl(src, src, bits); err != .None { return PyRes{res=":shl:shl(src, bits):", err=err}; } + if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err}; } + if err = shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); - if err != .None { return PyRes{res=":shl:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":shl:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -286,12 +286,12 @@ PyRes :: struct { dest := &Int{}; defer destroy(dest); - if err = factorial(dest, n); err != .None { return PyRes{res=":factorial:factorial(n):", err=err}; } + if err = factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); - if err != .None { return PyRes{res=":factorial:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":factorial:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -304,14 +304,14 @@ PyRes :: struct { ai, bi, dest := &Int{}, &Int{}, &Int{}; defer destroy(ai, bi, dest); - if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":gcd:atoi(a):", err=err}; } - if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":gcd:atoi(b):", err=err}; } - if err = gcd(dest, ai, bi); err != .None { return PyRes{res=":gcd:gcd(a, b):", err=err}; } + if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err}; } + if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err}; } + if err = gcd(dest, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); - if err != .None { return PyRes{res=":gcd:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":gcd:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } /* @@ -324,13 +324,13 @@ PyRes :: struct { ai, bi, dest := &Int{}, &Int{}, &Int{}; defer destroy(ai, bi, dest); - if err = atoi(ai, string(a), 16); err != .None { return PyRes{res=":lcm:atoi(a):", err=err}; } - if err = atoi(bi, string(b), 16); err != .None { return PyRes{res=":lcm:atoi(b):", err=err}; } - if err = lcm(dest, ai, bi); err != .None { return PyRes{res=":lcm:lcm(a, b):", err=err}; } + if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err}; } + if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err}; } + if err = lcm(dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); - if err != .None { return PyRes{res=":lcm:itoa(res):", err=err}; } - return PyRes{res = r, err = .None}; + if err != nil { return PyRes{res=":lcm:itoa(res):", err=err}; } + return PyRes{res = r, err = nil}; } From fc0a92f8ace0e8a6f42d7666b8d4cb92cfb0df3e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 3 Aug 2021 19:52:14 +0200 Subject: [PATCH 072/105] big: Add constants. --- core/math/big/common.odin | 60 +++++++++++++----------- core/math/big/example.odin | 39 +++++++++++++--- core/math/big/helpers.odin | 93 ++++++++++++++++++++++++++++---------- 3 files changed, 133 insertions(+), 59 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index cad6524e5..7943c0e4b 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -26,10 +26,15 @@ when _LOW_MEMORY { _DEFAULT_DIGIT_COUNT :: 32; } -_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); -_SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); -_MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); -_SQR_TOOM_CUTOFF :: #config(SQR_TOOM_CUTOFF, _DEFAULT_SQR_TOOM_CUTOFF); +/* + `initialize_constants` returns `#config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF)` + and we initialize this cutoff that way so that the procedure is used and called, + because it handles initializing the constants ONE, ZERO, MINUS_ONE, NAN and INF. +*/ +_MUL_KARATSUBA_CUTOFF := initialize_constants(); +_SQR_KARATSUBA_CUTOFF := #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); +_MUL_TOOM_CUTOFF := #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); +_SQR_TOOM_CUTOFF := #config(SQR_TOOM_CUTOFF, _DEFAULT_SQR_TOOM_CUTOFF); /* These defaults were tuned on an AMD A8-6600K (64-bit) using libTomMath's `make tune`. @@ -59,9 +64,10 @@ Sign :: enum u8 { }; Int :: struct { - used: int, - digit: [dynamic]DIGIT, - sign: Sign, + used: int, + digit: [dynamic]DIGIT, + sign: Sign, + flags: Flags, }; Flag :: enum u8 { @@ -76,35 +82,35 @@ Flags :: bit_set[Flag; u8]; Errors are a strict superset of runtime.Allocation_Error. */ Error :: enum int { - Out_Of_Memory = 1, - Invalid_Pointer = 2, - Invalid_Argument = 3, + Out_Of_Memory = 1, + Invalid_Pointer = 2, + Invalid_Argument = 3, - Unknown_Error = 4, - Max_Iterations_Reached = 5, - Buffer_Overflow = 6, - Integer_Overflow = 7, + Assignment_To_Immutable = 4, + Max_Iterations_Reached = 5, + Buffer_Overflow = 6, + Integer_Overflow = 7, - Division_by_Zero = 8, - Math_Domain_Error = 9, + Division_by_Zero = 8, + Math_Domain_Error = 9, - Unimplemented = 127, + Unimplemented = 127, }; Error_String :: #partial [Error]string{ - .Out_Of_Memory = "Out of memory", - .Invalid_Pointer = "Invalid pointer", - .Invalid_Argument = "Invalid argument", + .Out_Of_Memory = "Out of memory", + .Invalid_Pointer = "Invalid pointer", + .Invalid_Argument = "Invalid argument", - .Unknown_Error = "Unknown error", - .Max_Iterations_Reached = "Max iterations reached", - .Buffer_Overflow = "Buffer overflow", - .Integer_Overflow = "Integer overflow", + .Assignment_To_Immutable = "Assignment to immutable", + .Max_Iterations_Reached = "Max iterations reached", + .Buffer_Overflow = "Buffer overflow", + .Integer_Overflow = "Integer overflow", - .Division_by_Zero = "Division by zero", - .Math_Domain_Error = "Math domain error", + .Division_by_Zero = "Division by zero", + .Math_Domain_Error = "Math domain error", - .Unimplemented = "Unimplemented", + .Unimplemented = "Unimplemented", }; Primality_Flag :: enum u8 { diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 7dc6329c6..96d036570 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -39,6 +39,7 @@ _SQR_KARATSUBA_CUTOFF, _MUL_TOOM_CUTOFF, _SQR_TOOM_CUTOFF, ); + } print_timings :: proc() { @@ -95,16 +96,15 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := false, newlin defer delete(as); cb, _ := count_bits(a); if print_name { - fmt.printf("%v ", name); - } - if print_extra_info { - fmt.printf("(base: %v, bits used: %v): %v", base, cb, as); - } else { - fmt.printf("%v", as); + fmt.printf("%v", name); } if err != nil { fmt.printf("%v (error: %v | %v)", name, err, a); } + fmt.printf("%v", as); + if print_extra_info { + fmt.printf(" (base: %v, bits used: %v, flags: %v)", base, cb, a.flags); + } if newline { fmt.println(); } @@ -118,6 +118,31 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); + fmt.println(); + print(" ONE: ", ONE, 10, true, true, true); + fmt.println(); + + one(a); + print(" one: ", a, 10, true, true, true); + fmt.println(); + + minus_one(a); + print("-one: ", a, 10, true, true, true); + fmt.println(); + + nan(a); + print(" nan: ", a, 10, true, true, true); + fmt.println(); + + inf(a); + print(" inf: ", a, 10, true, true, true); + fmt.println(); + + minus_inf(a); + print("-inf: ", a, 10, true, true, true); + fmt.println(); + + factorial(a, 128); // Untimed warmup. N :: 128; @@ -145,8 +170,8 @@ main :: proc() { mem.tracking_allocator_init(&ta, context.allocator); context.allocator = mem.tracking_allocator(&ta); - // print_configation(); demo(); + print_timings(); if len(ta.allocation_map) > 0 { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index c14690279..d4b5d0220 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -56,7 +56,8 @@ set :: proc { int_set_from_integer, int_copy }; /* Copy one `Int` to another. */ -int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { +int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + if err = error_if_immutable(dest); err != nil { return err; } if err = clear_if_uninitialized(src); err != nil { return err; } /* If dest == src, do nothing @@ -68,7 +69,7 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error Grow `dest` to fit `src`. If `dest` is not yet initialized, it will be using `allocator`. */ - if err = grow(dest, src.used, false, allocator); err != nil { + if err = grow(dest, src.used, minimize, allocator); err != nil { return err; } @@ -78,8 +79,10 @@ int_copy :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error for v, i in src.digit[:src.used] { dest.digit[i] = v; } - dest.used = src.used; - dest.sign = src.sign; + dest.used = src.used; + dest.sign = src.sign; + dest.flags = src.flags &~ {.Immutable}; + _zero_unused(dest); return nil; } @@ -120,7 +123,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != nil { + if err = copy(dest, src, false, allocator); err != nil { return err; } @@ -163,7 +166,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Copy `src` to `dest` */ - if err = copy(dest, src, allocator); err != nil { + if err = copy(dest, src, false, allocator); err != nil { return err; } @@ -337,12 +340,7 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != nil { return err; } - - a.used = 1; - a.digit[0] = 1; - a.sign = .Zero_or_Positive; - return nil; + return copy(a, ONE, minimize, allocator); } one :: proc { int_one, }; @@ -350,17 +348,34 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = clear(a, minimize, allocator); err != nil { - return err; - } - - a.used = 1; - a.digit[0] = 1; - a.sign = .Negative; - return nil; + return copy(a, MINUS_ONE, minimize, allocator); } minus_one :: proc { int_minus_one, }; +/* + Set the `Int` to Inf and optionally shrink it to the minimum backing size. +*/ +int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return copy(a, INF, minimize, allocator); +} +inf :: proc { int_inf, }; + +/* + Set the `Int` to -Inf and optionally shrink it to the minimum backing size. +*/ +int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return copy(a, MINUS_INF, minimize, allocator); +} +minus_inf :: proc { int_inf, }; + +/* + Set the `Int` to NaN and optionally shrink it to the minimum backing size. +*/ +int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return copy(a, NAN, minimize, allocator); +} +nan :: proc { int_nan, }; + power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { /* Check that `a` is usable. @@ -602,9 +617,21 @@ clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { } return err; } - clear_if_uninitialized :: proc {clear_if_uninitialized_single, clear_if_uninitialized_multi, }; +error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { + if arg != nil && .Immutable in arg.flags { return .Assignment_To_Immutable; } + return nil; +} + +error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { + for i in args { + if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable; } + } + return nil; +} +error_if_immutable :: proc {error_if_immutable_single, error_if_immutable_multi, }; + /* Allocates several `Int`s at once. */ @@ -655,7 +682,23 @@ clamp :: proc(a: ^Int) -> (err: Error) { } -_STATIC_ZERO := &Int{ - used = 0, - sign = .Zero_or_Positive, -}; +/* + Initialize constants. +*/ +ONE, ZERO, MINUS_ONE, INF, MINUS_INF, NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + +initialize_constants :: proc() -> (res: int) { + set( ZERO, 0); ZERO.flags = {.Immutable}; + set( ONE, 1); ONE.flags = {.Immutable}; + set(MINUS_ONE, -1); MINUS_ONE.flags = {.Immutable}; + set( INF, 0); INF.flags = {.Immutable, .Inf}; + set( INF, 0); MINUS_INF.flags = {.Immutable, .Inf}; MINUS_INF.sign = .Negative; + set( NAN, 0); NAN.flags = {.Immutable, .NaN}; + + return #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); +} + +destroy_constants :: proc() { + destroy(ONE, ZERO, MINUS_ONE, INF, NAN); +} + From 2323ca16225066187958a003301be6fd3bc181fe Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 4 Aug 2021 00:40:27 +0200 Subject: [PATCH 073/105] big: Add `MATH_BIG_FORCE_64/32_BIT` flags. --- core/math/big/basic.odin | 2 +- core/math/big/common.odin | 8 +++++++- core/math/big/helpers.odin | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 5470bf917..69520a76c 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -1672,7 +1672,7 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { } -when size_of(rawptr) == 8 { +when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { _factorial_table := [35]_WORD{ /* f(00): */ 1, /* f(01): */ 1, diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 7943c0e4b..1b76c9520 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -19,6 +19,12 @@ import "core:intrinsics" /* Tunables */ + +MATH_BIG_FORCE_64_BIT :: false; +MATH_BIG_FORCE_32_BIT :: false; +when (MATH_BIG_FORCE_32_BIT && MATH_BIG_FORCE_64_BIT) { #panic("Cannot force 32-bit and 64-bit big backend simultaneously."); }; + + _LOW_MEMORY :: #config(BIGINT_SMALL_MEMORY, false); when _LOW_MEMORY { _DEFAULT_DIGIT_COUNT :: 8; @@ -140,7 +146,7 @@ _MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS); _MAX_BIT_COUNT :: (max(int) - 2); _MAX_DIGIT_COUNT :: _MAX_BIT_COUNT / _DIGIT_BITS; -when size_of(rawptr) == 8 { +when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { /* We can use u128 as an intermediary. */ diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index d4b5d0220..a326b960b 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -549,7 +549,7 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { when _DIGIT_BITS == 60 { // DIGIT = u64 return DIGIT(rnd.uint64(r)) & _MASK; } else when _DIGIT_BITS == 28 { // DIGIT = u32 - return DIGIT(rand.uint32(r)) & _MASK; + return DIGIT(rnd.uint32(r)) & _MASK; } else { panic("Unsupported DIGIT size."); } From 47397a6a488ad9cae625e531e5236513b81660a0 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 4 Aug 2021 00:59:15 +0200 Subject: [PATCH 074/105] Add faster divison. --- core/math/big/basic.odin | 175 ++++++++++++++++++++++++++++++++++++- core/math/big/build.bat | 6 +- core/math/big/helpers.odin | 4 +- 3 files changed, 177 insertions(+), 8 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 69520a76c..28e339f26 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -681,10 +681,9 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E if false && (denominator.used > 2 * _MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) { // err = _int_div_recursive(quotient, remainder, numerator, denominator); - } else if false { - // err = _int_div_school(quotient, remainder, numerator, denominator); } else { - err = _int_div_small(quotient, remainder, numerator, denominator); + err = _int_div_school(quotient, remainder, numerator, denominator); + // err = _int_div_small(quotient, remainder, numerator, denominator); } return err; @@ -1311,6 +1310,176 @@ _int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { return remainder, nil; } +/* + Signed Integer Division + + c*b + d == a [i.e. a/b, c=quotient, d=remainder], HAC pp.598 Algorithm 14.20 + + Note that the description in HAC is horribly incomplete. + For example, it doesn't consider the case where digits are removed from 'x' in + the inner loop. + + It also doesn't consider the case that y has fewer than three digits, etc. + The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. +*/ +_int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { + if err = error_if_immutable(quotient, remainder); err != nil { return err; } + if err = clear_if_uninitialized(quotient, numerator, denominator); err != nil { return err; } + + q, x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(q, x, y, t1, t2); + + if err = grow(q, numerator.used + 2); err != nil { return err; } + q.used = numerator.used + 2; + + if err = init_multi(t1, t2); err != nil { return err; } + if err = copy(x, numerator); err != nil { return err; } + if err = copy(y, denominator); err != nil { return err; } + + /* + Fix the sign. + */ + neg := numerator.sign != denominator.sign; + x.sign = .Zero_or_Positive; + y.sign = .Zero_or_Positive; + + /* + Normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] + */ + norm, _ := count_bits(y); + norm %= _DIGIT_BITS; + + if norm < _DIGIT_BITS - 1 { + norm = (_DIGIT_BITS - 1) - norm; + if err = shl(x, x, norm); err != nil { return err; } + if err = shl(y, y, norm); err != nil { return err; } + } else { + norm = 0; + } + + /* + Note: HAC does 0 based, so if used==5 then it's 0,1,2,3,4, i.e. use 4 + */ + n := x.used - 1; + t := y.used - 1; + + /* + while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } + y = y*b**{n-t} + */ + + if err = shl_digit(y, n - t); err != nil { return err; } + + c, _ := cmp(x, y); + for c != -1 { + q.digit[n - t] += 1; + if err = sub(x, x, y); err != nil { return err; } + c, _ = cmp(x, y); + } + + /* + Reset y by shifting it back down. + */ + shr_digit(y, n - t); + + /* + Step 3. for i from n down to (t + 1). + */ + for i := n; i >= (t + 1); i -= 1 { + if (i > x.used) { continue; } + + /* + step 3.1 if xi == yt then set q{i-t-1} to b-1, otherwise set q{i-t-1} to (xi*b + x{i-1})/yt + */ + if x.digit[i] == y.digit[t] { + q.digit[(i - t) - 1] = 1 << (_DIGIT_BITS - 1); + } else { + + tmp := _WORD(x.digit[i]) << _DIGIT_BITS; + tmp |= _WORD(x.digit[i - 1]); + tmp /= _WORD(y.digit[t]); + if tmp > _WORD(_MASK) { + tmp = _WORD(_MASK); + } + q.digit[(i - t) - 1] = DIGIT(tmp & _WORD(_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + + iter := 0; + + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] + 1) & _MASK; + for { + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; + + /* + Find left hand. + */ + zero(t1); + t1.digit[0] = ((t - 1) < 0) ? 0 : y.digit[t - 1]; + t1.digit[1] = y.digit[t]; + t1.used = 2; + if err = mul(t1, t1, q.digit[(i - t) - 1]); err != nil { return err; } + + /* + Find right hand. + */ + t2.digit[0] = ((i - 2) < 0) ? 0 : x.digit[i - 2]; + t2.digit[1] = x.digit[i - 1]; /* i >= 1 always holds */ + t2.digit[2] = x.digit[i]; + t2.used = 3; + + if t1_t2, _ := cmp_mag(t1, t2); t1_t2 != 1 { + + break; + } + iter += 1; if iter > 100 { return .Max_Iterations_Reached; } + } + + /* + Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} + */ + if err = int_mul_digit(t1, y, q.digit[(i - t) - 1]); err != nil { return err; } + if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = sub(x, x, t1); err != nil { return err; } + + /* + if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } + */ + if x.sign == .Negative { + if err = copy(t1, y); err != nil { return err; } + if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = add(x, x, t1); err != nil { return err; } + + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; + } + } + + /* + Now q is the quotient and x is the remainder, [which we have to normalize] + Get sign before writing to c. + */ + z, _ := is_zero(x); + x.sign = .Zero_or_Positive if z else numerator.sign; + + if quotient != nil { + clamp(q); + swap(q, quotient); + quotient.sign = .Negative if neg else .Zero_or_Positive; + } + + if remainder != nil { + if err = shr(x, x, norm); err != nil { return err; } + swap(x, remainder); + } + + return nil; +} + /* Slower bit-bang division... also smaller. */ diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 2c1edfcec..d454fef4d 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -odin run . -vet +:odin run . -vet : -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check +odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -:python test.py \ No newline at end of file +python test.py \ No newline at end of file diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index a326b960b..f0eeea96b 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -340,7 +340,7 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return copy(a, ONE, minimize, allocator); + return set(a, 1); } one :: proc { int_one, }; @@ -348,7 +348,7 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return copy(a, MINUS_ONE, minimize, allocator); + return set(a, -1); } minus_one :: proc { int_minus_one, }; From 85a2a8815e2ae06d23ec23224e00084e143363e2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 4 Aug 2021 13:43:16 +0200 Subject: [PATCH 075/105] big: Some more work on constants. --- core/math/big/basic.odin | 31 ++++++++++++--------- core/math/big/build.bat | 6 ++-- core/math/big/example.odin | 20 +++----------- core/math/big/exp_log.odin | 4 +-- core/math/big/helpers.odin | 56 ++++++++++++++++++++++++++------------ 5 files changed, 65 insertions(+), 52 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 28e339f26..8715b5244 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -683,7 +683,11 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E // err = _int_div_recursive(quotient, remainder, numerator, denominator); } else { err = _int_div_school(quotient, remainder, numerator, denominator); - // err = _int_div_small(quotient, remainder, numerator, denominator); + /* + NOTE(Jeroen): We no longer need or use `_int_div_small`. + We'll keep it around for a bit. + err = _int_div_small(quotient, remainder, numerator, denominator); + */ } return err; @@ -792,7 +796,7 @@ _int_recursive_product :: proc(res: ^Int, start, stop: DIGIT, level := int(0)) - if num_factors == 1 { return set(res, start); } - return one(res); + return set(res, 1); } /* @@ -804,8 +808,8 @@ int_factorial_binary_split :: proc(res: ^Int, n: DIGIT) -> (err: Error) { inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(inner, outer, start, stop, temp); - if err = one(inner); err != nil { return err; } - if err = one(outer); err != nil { return err; } + if err = set(inner, 1); err != nil { return err; } + if err = set(outer, 1); err != nil { return err; } bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); @@ -896,7 +900,7 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { /* Zero the carry */ carry := DIGIT(0); - for i = 0; i < min_used; i += 1 { + #no_bounds_check for i = 0; i < min_used; i += 1 { /* Compute the sum one _DIGIT at a time. dest[i] = a[i] + b[i] + carry; @@ -918,7 +922,7 @@ _int_add :: proc(dest, a, b: ^Int) -> (err: Error) { 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 { + #no_bounds_check for ; i < max_used; i += 1 { dest.digit[i] = x.digit[i] + carry; /* Compute carry @@ -975,7 +979,7 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { borrow := DIGIT(0); - for i = 0; i < min_used; i += 1 { + #no_bounds_check for i = 0; i < min_used; i += 1 { dest.digit[i] = (x.digit[i] - y.digit[i] - borrow); /* borrow = carry bit of dest[i] @@ -993,7 +997,7 @@ _int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { /* Now copy higher words if any, e.g. if A has more digits than B */ - for ; i < max_used; i += 1 { + #no_bounds_check for ; i < max_used; i += 1 { dest.digit[i] = x.digit[i] - borrow; /* borrow = carry bit of dest[i] @@ -1058,7 +1062,7 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Compute the column of the output and propagate the carry. */ - for iy = 0; iy < pb; iy += 1 { + #no_bounds_check for iy = 0; iy < pb; iy += 1 { /* Compute the column as a _WORD. */ @@ -1144,7 +1148,7 @@ _int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Execute loop. */ - for iz = 0; iz < iy; iz += 1 { + #no_bounds_check for iz = 0; iz < iy; iz += 1 { _W += _WORD(a.digit[tx + iz]) * _WORD(b.digit[ty - iz]); } @@ -1202,7 +1206,7 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } t.used = (2 * pa) + 1; - for ix = 0; ix < pa; ix += 1 { + #no_bounds_check for ix = 0; ix < pa; ix += 1 { carry := DIGIT(0); /* First calculate the digit at 2*ix; calculate double precision result. @@ -1218,7 +1222,7 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { */ carry = DIGIT(r >> _DIGIT_BITS); - for iy = ix + 1; iy < pa; iy += 1 { + #no_bounds_check for iy = ix + 1; iy < pa; iy += 1 { /* First calculate the product. */ @@ -1242,7 +1246,7 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { /* Propagate upwards. */ - for carry != 0 { + #no_bounds_check for carry != 0 { r = _WORD(t.digit[ix+iy]) + _WORD(carry); t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); carry = DIGIT(r >> _WORD(_DIGIT_BITS)); @@ -1483,6 +1487,7 @@ _int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (e /* Slower bit-bang division... also smaller. */ +@(deprecated="Use `_int_div_school`, it's 3.5x faster.") _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}; diff --git a/core/math/big/build.bat b/core/math/big/build.bat index d454fef4d..2c1edfcec 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -:odin run . -vet +odin run . -vet : -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -python test.py \ No newline at end of file +:python test.py \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 96d036570..6fc21124a 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -88,7 +88,7 @@ Event :: struct { } Timings := [Category]Event{}; -print :: proc(name: string, a: ^Int, base := i8(10), print_name := false, newline := true, print_extra_info := false) { +print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) { s := time.tick_now(); as, err := itoa(a, base); Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; @@ -118,28 +118,16 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - fmt.println(); - print(" ONE: ", ONE, 10, true, true, true); - fmt.println(); - - one(a); - print(" one: ", a, 10, true, true, true); - fmt.println(); - - minus_one(a); - print("-one: ", a, 10, true, true, true); - fmt.println(); - nan(a); - print(" nan: ", a, 10, true, true, true); + print(" nan: ", a, 10, true, true, true); fmt.println(); inf(a); - print(" inf: ", a, 10, true, true, true); + print(" inf: ", a, 10, true, true, true); fmt.println(); minus_inf(a); - print("-inf: ", a, 10, true, true, true); + print("-inf: ", a, 10, true, true, true); fmt.println(); diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index 8fba8cb81..e0569c586 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -51,7 +51,7 @@ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { if err = zero(dest); err != nil { return err; } return .Math_Domain_Error; } - if power == 0 { return one(dest); } + if power == 0 { return set(dest, 1); } if power > 0 { return zero(dest); } } @@ -429,7 +429,7 @@ _int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { if err = set(bi_base, base); err != nil { return -1, err; } if err = init_multi(bracket_mid, t); err != nil { return -1, err; } - if err = one(bracket_low); err != nil { return -1, err; } + if err = set(bracket_low, 1); err != nil { return -1, err; } if err = set(bracket_high, base); err != nil { return -1, err; } low := 0; high := 1; diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index f0eeea96b..9217589db 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -13,6 +13,10 @@ import "core:mem" import "core:intrinsics" import rnd "core:math/rand" +/* + TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere. +*/ + /* Deallocates the backing memory of one or more `Int`s. */ @@ -35,10 +39,13 @@ int_destroy :: proc(integers: ..^Int) { int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) where intrinsics.type_is_integer(T) { src := src; - if err = clear_if_uninitialized(dest); err != nil { - return err; - } - dest.used = 0; + + if err = error_if_immutable(dest); err != nil { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } + + dest.flags = {}; // We're not -Inf, Inf, NaN or Immutable. + + dest.used = 0; dest.sign = .Zero_or_Positive if src >= 0 else .Negative; src = abs(src); @@ -57,19 +64,21 @@ set :: proc { int_set_from_integer, int_copy }; Copy one `Int` to another. */ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if err = error_if_immutable(dest); err != nil { return err; } - if err = clear_if_uninitialized(src); err != nil { return err; } /* If dest == src, do nothing */ - if (dest == src) { - return nil; - } + if (dest == src) { return nil; } + + if err = error_if_immutable(dest); err != nil { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } + /* Grow `dest` to fit `src`. If `dest` is not yet initialized, it will be using `allocator`. */ - if err = grow(dest, src.used, minimize, allocator); err != nil { + needed := src.used if minimize else max(src.used, _DEFAULT_DIGIT_COUNT); + + if err = grow(dest, needed, minimize, allocator); err != nil { return err; } @@ -340,7 +349,7 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return set(a, 1); + return set(a, 1, minimize, allocator); } one :: proc { int_one, }; @@ -348,7 +357,7 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return set(a, -1); + return set(a, -1, minimize, allocator); } minus_one :: proc { int_minus_one, }; @@ -356,7 +365,9 @@ minus_one :: proc { int_minus_one, }; Set the `Int` to Inf and optionally shrink it to the minimum backing size. */ int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return copy(a, INF, minimize, allocator); + err = set(a, 1, minimize, allocator); + a.flags |= { .Inf, }; + return err; } inf :: proc { int_inf, }; @@ -364,7 +375,9 @@ inf :: proc { int_inf, }; Set the `Int` to -Inf and optionally shrink it to the minimum backing size. */ int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return copy(a, MINUS_INF, minimize, allocator); + err = set(a, -1, minimize, allocator); + a.flags |= { .Inf, }; + return err; } minus_inf :: proc { int_inf, }; @@ -372,7 +385,9 @@ minus_inf :: proc { int_inf, }; Set the `Int` to NaN and optionally shrink it to the minimum backing size. */ int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return copy(a, NAN, minimize, allocator); + err = set(a, 1, minimize, allocator); + a.flags |= { .NaN, }; + return err; } nan :: proc { int_nan, }; @@ -691,9 +706,14 @@ initialize_constants :: proc() -> (res: int) { set( ZERO, 0); ZERO.flags = {.Immutable}; set( ONE, 1); ONE.flags = {.Immutable}; set(MINUS_ONE, -1); MINUS_ONE.flags = {.Immutable}; - set( INF, 0); INF.flags = {.Immutable, .Inf}; - set( INF, 0); MINUS_INF.flags = {.Immutable, .Inf}; MINUS_INF.sign = .Negative; - set( NAN, 0); NAN.flags = {.Immutable, .NaN}; + + /* + We set these special values to -1 or 1 so they don't get mistake for zero accidentally. + This allows for shortcut tests of is_zero as .used == 0. + */ + set( INF, 1); INF.flags = {.Immutable, .Inf}; + set( INF, -1); MINUS_INF.flags = {.Immutable, .Inf}; + set( NAN, 1); NAN.flags = {.Immutable, .NaN}; return #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); } From 463003e86a96cf5cc0b05fc96d01f5118272911f Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 4 Aug 2021 21:25:38 +0200 Subject: [PATCH 076/105] bit: Improved bitfield extraction. --- core/math/big/basic.odin | 20 ++++---- core/math/big/example.odin | 58 ++++++++++++----------- core/math/big/helpers.odin | 94 +++++++++++++------------------------- core/math/big/prime.odin | 68 +++++++++++++++++++++++++++ core/math/big/radix.odin | 6 ++- core/math/big/test.py | 14 ++++-- 6 files changed, 158 insertions(+), 102 deletions(-) create mode 100644 core/math/big/prime.odin diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 8715b5244..eaa1d5eb7 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -711,7 +711,12 @@ int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { if z, err = is_zero(remainder); z || denominator.sign == remainder.sign { return nil; } return add(remainder, remainder, numerator); } -mod :: proc { int_mod, }; + +int_mod_digit :: proc(numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { + return _int_div_digit(nil, numerator, denominator); +} + +mod :: proc { int_mod, int_mod_digit, }; /* remainder = (number + addend) % modulus. @@ -1263,14 +1268,14 @@ _int_sqr :: proc(dest, src: ^Int) -> (err: Error) { /* Divide by three (based on routine from MPI and the GMP manual). */ -_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { +_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: Error) { /* b = 2**MP_DIGIT_BIT / 3 */ b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); q := &Int{}; - if err = grow(q, numerator.used); err != nil { return -1, err; } + if err = grow(q, numerator.used); err != nil { return 0, err; } q.used = numerator.used; q.sign = numerator.sign; @@ -1300,8 +1305,7 @@ _int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: int, err: Error) { } q.digit[ix] = DIGIT(t); } - - remainder = int(w); + remainder = DIGIT(w); /* [optional] store the quotient. @@ -1542,7 +1546,7 @@ _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (er /* Single digit division (based on routine from MPI). */ -_int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: int, err: Error) { +_int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { q := &Int{}; ix: int; @@ -1581,7 +1585,7 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain for ix < _DIGIT_BITS && denominator != (1 << uint(ix)) { ix += 1; } - remainder = int(numerator.digit[0]) & ((1 << uint(ix)) - 1); + remainder = numerator.digit[0] & ((1 << uint(ix)) - 1); if quotient == nil { return remainder, nil; } @@ -1615,7 +1619,7 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain } q.digit[ix] = t; } - remainder = int(w); + remainder = DIGIT(w); if quotient != nil { clamp(q); diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 6fc21124a..cd264096b 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -81,6 +81,8 @@ Category :: enum { choose, lsb, ctz, + bitfield_extract_old, + bitfield_extract_new, }; Event :: struct { t: time.Duration, @@ -118,39 +120,41 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - nan(a); - print(" nan: ", a, 10, true, true, true); - fmt.println(); + err = factorial(a, 1224); + count, _ := count_bits(a); - inf(a); - print(" inf: ", a, 10, true, true, true); - fmt.println(); + bits := 101; + be1, be2: _WORD; - minus_inf(a); - print("-inf: ", a, 10, true, true, true); - fmt.println(); - - - factorial(a, 128); // Untimed warmup. - - N :: 128; - - s := time.tick_now(); - err = factorial(a, N); - Timings[.factorial].t += time.tick_since(s); Timings[.factorial].c += 1; - - if err != nil { - fmt.printf("factorial(%v) returned %v\n", N, err); + /* + Sanity check loop. + */ + for o := 0; o < count - bits; o += 1 { + be1, _ = int_bitfield_extract(a, o, bits); + be2, _ = int_bitfield_extract_fast(a, o, bits); + if be1 != be2 { + fmt.printf("Offset: %v | Expected: %v | Got: %v\n", o, be1, be2); + assert(false); + } } - s = time.tick_now(); - as, err = itoa(a, 16); - Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; - if err != nil { - fmt.printf("itoa(factorial(%v), 16) returned %v\n", N, err); + /* + Timing loop + */ + s_old := time.tick_now(); + for o := 0; o < count - bits; o += 1 { + be1, _ = int_bitfield_extract(a, o, bits); } + Timings[.bitfield_extract_old].t += time.tick_since(s_old); + Timings[.bitfield_extract_old].c += (count - bits); - fmt.printf("factorial(%v): %v (first 10 hex digits)\n", N, as[:10]); + s_new := time.tick_now(); + for o := 0; o < count - bits; o += 1 { + be2, _ = int_bitfield_extract_fast(a, o, bits); + } + Timings[.bitfield_extract_new].t += time.tick_since(s_new); + Timings[.bitfield_extract_new].c += (count - bits); + assert(be1 == be2); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 9217589db..f46509162 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -13,6 +13,8 @@ import "core:mem" import "core:intrinsics" import rnd "core:math/rand" +// import "core:fmt" + /* TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere. */ @@ -193,9 +195,7 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { /* Check that `a`is usable. */ - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } + if err = clear_if_uninitialized(a); err != nil { return 0, err; } limb := bit_offset / _DIGIT_BITS; if limb < 0 || limb >= a.used { @@ -207,72 +207,44 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { return 1 if ((a.digit[limb] & i) != 0) else 0, nil; } -/* - TODO: Optimize. -*/ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* - Check that `a`is usable. + Check that `a` is usable. */ - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } + if err = clear_if_uninitialized(a); err != nil { return 0, err; } + if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } - if count > _WORD_BITS || count < 1 { - return 0, .Invalid_Argument; - } + for shift := 0; shift < count; shift += 1 { + bit_offset := offset + shift; - when true { - v: DIGIT; - e: Error; + limb := bit_offset / _DIGIT_BITS; + mask := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); - for shift := 0; shift < count; shift += 1 { - o := offset + shift; - v, e = extract_bit(a, o); - if e != nil { - break; - } - res = res + _WORD(v) << uint(shift); + if (a.digit[limb] & mask) != 0 { + res += _WORD(1) << uint(shift); } - - return res, e; - } else { - limb_lo := offset / _DIGIT_BITS; - bits_lo := offset % _DIGIT_BITS; - limb_hi := (offset + count) / _DIGIT_BITS; - bits_hi := (offset + count) % _DIGIT_BITS; - - if limb_lo < 0 || limb_lo >= a.used || limb_hi < 0 || limb_hi >= a.used { - return 0, .Invalid_Argument; - } - - for i := limb_hi; i >= limb_lo; i -= 1 { - res <<= _DIGIT_BITS; - - /* - Determine which bits to extract from each DIGIT. The whole DIGIT's worth by default. - */ - bit_count := _DIGIT_BITS; - bit_offset := 0; - if i == limb_lo { - bit_count -= bits_lo; - bit_offset = _DIGIT_BITS - bit_count; - } else if i == limb_hi { - bit_count = bits_hi; - bit_offset = 0; - } - - d := a.digit[i]; - - v := (d >> uint(bit_offset)) & DIGIT(1 << uint(bit_count - 1)); - m := DIGIT(1 << uint(bit_count-1)); - r := v & m; - - res |= _WORD(r); - } - return res, nil; - } + return res, nil; +} + +int_bitfield_extract_fast :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { + /* + Check that `a` is usable. + */ + if err = clear_if_uninitialized(a); err != nil { return 0, err; } + if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } + + for shift := 0; shift < count; shift += 1 { + bit_offset := offset + shift; + + limb := bit_offset / _DIGIT_BITS; + mask := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); + + if (a.digit[limb] & mask) != 0 { + res += _WORD(1) << uint(shift); + } + } + return res, nil; } /* diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin new file mode 100644 index 000000000..a64215c62 --- /dev/null +++ b/core/math/big/prime.odin @@ -0,0 +1,68 @@ +package big + +/* + Copyright 2021 Jeroen van Rijn . + Made available under Odin's BSD-2 license. + + An arbitrary precision mathematics 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`, `sub`, `mul`, `div`, ... +*/ + +/* + Determines if an Integer is divisible by one of the _PRIME_TABLE primes. + Returns true if it is, false if not. +*/ +int_prime_is_divisible :: proc(a: ^Int) -> (res: bool, err: Error) { + + rem: DIGIT; + for prime in _PRIME_TABLE { + if rem, err = mod(a, prime); err != nil { return false, err; } + if rem == 0 { return true, nil; } + } + /* + Default to not divisible. + */ + return false, nil; +} + + +_PRIME_TABLE := []DIGIT{ + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653, +}; \ No newline at end of file diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 97c77f144..dadc5fdc8 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -202,7 +202,9 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter for offset := 0; offset < count; offset += shift { bits_to_get := int(min(count - offset, shift)); - if digit, err = int_bitfield_extract(a, offset, bits_to_get); err != nil { + + digit, err = int_bitfield_extract(a, offset, bits_to_get); + if err != nil { return len(buffer) - available, .Invalid_Argument; } available -= 1; @@ -448,7 +450,7 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false temp.sign = .Zero_or_Positive; } - remainder: int; + remainder: DIGIT; for { if remainder, err = _int_div_digit(temp, temp, DIGIT(radix)); err != nil { destroy(temp, denominator); diff --git a/core/math/big/test.py b/core/math/big/test.py index b8e91c003..8346f1386 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -11,13 +11,13 @@ from enum import Enum # With EXIT_ON_FAIL set, we exit at the first fail. # EXIT_ON_FAIL = True -EXIT_ON_FAIL = False +#EXIT_ON_FAIL = False # # We skip randomized tests altogether if NO_RANDOM_TESTS is set. # NO_RANDOM_TESTS = True -NO_RANDOM_TESTS = False +#NO_RANDOM_TESTS = False # # If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. @@ -197,7 +197,13 @@ def test_sub(a = 0, b = 0, expected_error = Error.Okay): def test_mul(a = 0, b = 0, expected_error = Error.Okay): args = [arg_to_odin(a), arg_to_odin(b)] - res = mul(*args) + try: + res = mul(*args) + except OSError as e: + print("{} while trying to multiply {} x {}.".format(e, a, b)) + if EXIT_ON_FAIL: exit(3) + return False + expected_result = None if expected_error == Error.Okay: expected_result = a * b @@ -369,7 +375,7 @@ TESTS = { ], test_mul: [ [ 1234, 5432], - [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7] + [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7], ], test_div: [ [ 54321, 12345], From 35d8976de4502e0ad23aa33585870673cfc3aa67 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Aug 2021 00:24:51 +0200 Subject: [PATCH 077/105] bit: Optimized `int_bitfield_extract`. --- core/math/big/example.odin | 32 +++--------- core/math/big/helpers.odin | 102 ++++++++++++++++++++++--------------- core/math/big/test.py | 4 +- 3 files changed, 70 insertions(+), 68 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index cd264096b..f0d70b0c4 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -81,8 +81,7 @@ Category :: enum { choose, lsb, ctz, - bitfield_extract_old, - bitfield_extract_new, + bitfield_extract, }; Event :: struct { t: time.Duration, @@ -123,20 +122,8 @@ demo :: proc() { err = factorial(a, 1224); count, _ := count_bits(a); - bits := 101; - be1, be2: _WORD; - - /* - Sanity check loop. - */ - for o := 0; o < count - bits; o += 1 { - be1, _ = int_bitfield_extract(a, o, bits); - be2, _ = int_bitfield_extract_fast(a, o, bits); - if be1 != be2 { - fmt.printf("Offset: %v | Expected: %v | Got: %v\n", o, be1, be2); - assert(false); - } - } + bits := 51; + be1: _WORD; /* Timing loop @@ -145,16 +132,9 @@ demo :: proc() { for o := 0; o < count - bits; o += 1 { be1, _ = int_bitfield_extract(a, o, bits); } - Timings[.bitfield_extract_old].t += time.tick_since(s_old); - Timings[.bitfield_extract_old].c += (count - bits); - - s_new := time.tick_now(); - for o := 0; o < count - bits; o += 1 { - be2, _ = int_bitfield_extract_fast(a, o, bits); - } - Timings[.bitfield_extract_new].t += time.tick_since(s_new); - Timings[.bitfield_extract_new].c += (count - bits); - assert(be1 == be2); + Timings[.bitfield_extract].t += time.tick_since(s_old); + Timings[.bitfield_extract].c += (count - bits); + fmt.printf("be1: %v\n", be1); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index f46509162..435d82fe0 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -191,20 +191,8 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Helpers to extract values from the `Int`. */ -extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { - /* - Check that `a`is usable. - */ - if err = clear_if_uninitialized(a); err != nil { return 0, err; } - - limb := bit_offset / _DIGIT_BITS; - if limb < 0 || limb >= a.used { - return 0, .Invalid_Argument; - } - - i := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); - - return 1 if ((a.digit[limb] & i) != 0) else 0, nil; +int_bitfield_extract_single :: proc(a: ^Int, offset: int) -> (bit: _WORD, err: Error) { + return int_bitfield_extract(a, offset, 1); } int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { @@ -212,38 +200,72 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: E Check that `a` is usable. */ if err = clear_if_uninitialized(a); err != nil { return 0, err; } - if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } - - for shift := 0; shift < count; shift += 1 { - bit_offset := offset + shift; - - limb := bit_offset / _DIGIT_BITS; - mask := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); - - if (a.digit[limb] & mask) != 0 { - res += _WORD(1) << uint(shift); - } - } - return res, nil; -} - -int_bitfield_extract_fast :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* - Check that `a` is usable. + Early out for single bit. */ - if err = clear_if_uninitialized(a); err != nil { return 0, err; } + if count == 1 { + limb := offset / _DIGIT_BITS; + if limb < 0 || limb >= a.used { return 0, .Invalid_Argument; } + i := _WORD(1 << _WORD((offset % _DIGIT_BITS))); + return 1 if ((_WORD(a.digit[limb]) & i) != 0) else 0, nil; + } + if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } - for shift := 0; shift < count; shift += 1 { - bit_offset := offset + shift; + /* + There are 3 possible cases. + - [offset:][:count] covers 1 DIGIT, + e.g. offset: 0, count: 60 = bits 0..59 + - [offset:][:count] covers 2 DIGITS, + e.g. offset: 5, count: 60 = bits 5..59, 0..4 + e.g. offset: 0, count: 120 = bits 0..59, 60..119 + - [offset:][:count] covers 3 DIGITS, + e.g. offset: 40, count: 100 = bits 40..59, 0..59, 0..19 + e.g. offset: 40, count: 120 = bits 40..59, 0..59, 0..39 + */ + + limb := offset / _DIGIT_BITS; + bits_left := count; + bits_offset := offset % _DIGIT_BITS; + + num_bits := min(bits_left, _DIGIT_BITS - bits_offset); + + // fmt.printf("offset: %v | count: %v\n\n", offset, count); + // fmt.printf("left: %v | bits_offset: %v | limb: %v | num: %v\n\n", bits_left, bits_offset, limb, num_bits); + + shift := offset % _DIGIT_BITS; + mask := (_WORD(1) << uint(num_bits)) - 1; + + // fmt.printf("shift: %v | mask: %v\n", shift, mask); + // fmt.printf("d: %v\n", a.digit[limb]); + + res = (_WORD(a.digit[limb]) >> uint(shift)) & mask; + + // fmt.printf("res: %v\n", res); + + bits_left -= num_bits; + if bits_left == 0 { return res, nil; } + + res_shift := num_bits; + + num_bits = min(bits_left, _DIGIT_BITS); + mask = (1 << uint(num_bits)) - 1; + + v := (_WORD(a.digit[limb + 1]) & mask) << uint(res_shift); + res |= v; + + bits_left -= num_bits; + if bits_left == 0 { return res, nil; } + + // fmt.printf("bits_left: %v | offset: %v | num: %v\n", bits_left, offset, num_bits); + + mask = (1 << uint(bits_left)) - 1; + res_shift += _DIGIT_BITS; + + v = (_WORD(a.digit[limb + 2]) & mask) << uint(res_shift); + res |= v; - limb := bit_offset / _DIGIT_BITS; - mask := DIGIT(1 << DIGIT((bit_offset % _DIGIT_BITS))); - if (a.digit[limb] & mask) != 0 { - res += _WORD(1) << uint(shift); - } - } return res, nil; } diff --git a/core/math/big/test.py b/core/math/big/test.py index 8346f1386..f7e341ce7 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -11,13 +11,13 @@ from enum import Enum # With EXIT_ON_FAIL set, we exit at the first fail. # EXIT_ON_FAIL = True -#EXIT_ON_FAIL = False +EXIT_ON_FAIL = False # # We skip randomized tests altogether if NO_RANDOM_TESTS is set. # NO_RANDOM_TESTS = True -#NO_RANDOM_TESTS = False +NO_RANDOM_TESTS = False # # If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. From 511057ca364bdadde4d6752769b93f33a799497e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Aug 2021 13:44:07 +0200 Subject: [PATCH 078/105] big: Improve timing code. --- core/math/big/example.odin | 64 +++++++++++++++++++++++++------------- core/math/big/helpers.odin | 37 +++++++--------------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index f0d70b0c4..d46ce5e9d 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -45,34 +45,53 @@ _SQR_TOOM_CUTOFF, print_timings :: proc() { fmt.printf("Timings:\n"); for v, i in Timings { - if v.c > 0 { - avg := time.Duration(f64(v.t) / f64(v.c)); + if v.count > 0 { + avg_ticks := time.Duration(f64(v.ticks) / f64(v.count)); + avg_cycles := f64(v.cycles) / f64(v.count); avg_s: string; switch { - case avg < time.Microsecond: - avg_s = fmt.tprintf("%v ns", time.duration_nanoseconds(avg)); - case avg < time.Millisecond: - avg_s = fmt.tprintf("%v µs", time.duration_microseconds(avg)); + case avg_ticks < time.Microsecond: + avg_s = fmt.tprintf("%v ns / %v cycles", time.duration_nanoseconds(avg_ticks), avg_cycles); + case avg_ticks < time.Millisecond: + avg_s = fmt.tprintf("%v µs / %v cycles", time.duration_microseconds(avg_ticks), avg_cycles); case: - avg_s = fmt.tprintf("%v ms", time.duration_milliseconds(avg)); + avg_s = fmt.tprintf("%v ms / %v cycles", time.duration_milliseconds(avg_ticks), avg_cycles); } total_s: string; switch { - case v.t < time.Microsecond: - total_s = fmt.tprintf("%v ns", time.duration_nanoseconds(v.t)); - case v.t < time.Millisecond: - total_s = fmt.tprintf("%v µs", time.duration_microseconds(v.t)); + case v.ticks < time.Microsecond: + total_s = fmt.tprintf("%v ns / %v cycles", time.duration_nanoseconds(v.ticks), v.cycles); + case v.ticks < time.Millisecond: + total_s = fmt.tprintf("%v µs / %v cycles", time.duration_microseconds(v.ticks), v.cycles); case: - total_s = fmt.tprintf("%v ms", time.duration_milliseconds(v.t)); + total_s = fmt.tprintf("%v ms / %v cycles", time.duration_milliseconds(v.ticks), v.cycles); } - fmt.printf("\t%v: %s (avg), %s (total, %v calls)\n", i, avg_s, total_s, v.c); + fmt.printf("\t%v: %s (avg), %s (total, %v calls)\n", i, avg_s, total_s, v.count); } } } +@(deferred_in_out=_SCOPE_END) +SCOPED_TIMING :: #force_inline proc(c: Category) -> (ticks: time.Tick, cycles: u64) { + cycles = time.read_cycle_counter(); + ticks = time.tick_now(); + return; +} +_SCOPE_END :: #force_inline proc(c: Category, ticks: time.Tick, cycles: u64) { + cycles_now := time.read_cycle_counter(); + ticks_now := time.tick_now(); + + Timings[c].ticks = time.tick_diff(ticks, ticks_now); + Timings[c].cycles = cycles_now - cycles; + Timings[c].count += 1; +} +SCOPED_COUNT_ADD :: #force_inline proc(c: Category, count: int) { + Timings[c].count += count; +} + Category :: enum { itoa, atoi, @@ -83,16 +102,16 @@ Category :: enum { ctz, bitfield_extract, }; + Event :: struct { - t: time.Duration, - c: int, + ticks: time.Duration, + count: int, + cycles: u64, } Timings := [Category]Event{}; print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) { - s := time.tick_now(); as, err := itoa(a, base); - Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; defer delete(as); cb, _ := count_bits(a); @@ -128,12 +147,13 @@ demo :: proc() { /* Timing loop */ - s_old := time.tick_now(); - for o := 0; o < count - bits; o += 1 { - be1, _ = int_bitfield_extract(a, o, bits); + { + SCOPED_TIMING(.bitfield_extract); + for o := 0; o < count - bits; o += 1 { + be1, _ = int_bitfield_extract(a, o, bits); + } } - Timings[.bitfield_extract].t += time.tick_since(s_old); - Timings[.bitfield_extract].c += (count - bits); + SCOPED_COUNT_ADD(.bitfield_extract, count - bits - 1); fmt.printf("be1: %v\n", be1); } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 435d82fe0..054b05abd 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -205,19 +205,19 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: E */ if count == 1 { limb := offset / _DIGIT_BITS; - if limb < 0 || limb >= a.used { return 0, .Invalid_Argument; } + if limb < 0 || limb >= a.used { return 0, .Invalid_Argument; } i := _WORD(1 << _WORD((offset % _DIGIT_BITS))); return 1 if ((_WORD(a.digit[limb]) & i) != 0) else 0, nil; } - if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } + if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } /* There are 3 possible cases. - [offset:][:count] covers 1 DIGIT, - e.g. offset: 0, count: 60 = bits 0..59 + e.g. offset: 0, count: 60 = bits 0..59 - [offset:][:count] covers 2 DIGITS, - e.g. offset: 5, count: 60 = bits 5..59, 0..4 + e.g. offset: 5, count: 60 = bits 5..59, 0..4 e.g. offset: 0, count: 120 = bits 0..59, 60..119 - [offset:][:count] covers 3 DIGITS, e.g. offset: 40, count: 100 = bits 40..59, 0..59, 0..19 @@ -230,41 +230,26 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: E num_bits := min(bits_left, _DIGIT_BITS - bits_offset); - // fmt.printf("offset: %v | count: %v\n\n", offset, count); - // fmt.printf("left: %v | bits_offset: %v | limb: %v | num: %v\n\n", bits_left, bits_offset, limb, num_bits); - - shift := offset % _DIGIT_BITS; - mask := (_WORD(1) << uint(num_bits)) - 1; - - // fmt.printf("shift: %v | mask: %v\n", shift, mask); - // fmt.printf("d: %v\n", a.digit[limb]); - - res = (_WORD(a.digit[limb]) >> uint(shift)) & mask; - - // fmt.printf("res: %v\n", res); + shift := offset % _DIGIT_BITS; + mask := (_WORD(1) << uint(num_bits)) - 1; + res = (_WORD(a.digit[limb]) >> uint(shift)) & mask; bits_left -= num_bits; if bits_left == 0 { return res, nil; } res_shift := num_bits; + num_bits = min(bits_left, _DIGIT_BITS); + mask = (1 << uint(num_bits)) - 1; - num_bits = min(bits_left, _DIGIT_BITS); - mask = (1 << uint(num_bits)) - 1; - - v := (_WORD(a.digit[limb + 1]) & mask) << uint(res_shift); - res |= v; + res |= (_WORD(a.digit[limb + 1]) & mask) << uint(res_shift); bits_left -= num_bits; if bits_left == 0 { return res, nil; } - // fmt.printf("bits_left: %v | offset: %v | num: %v\n", bits_left, offset, num_bits); - mask = (1 << uint(bits_left)) - 1; res_shift += _DIGIT_BITS; - v = (_WORD(a.digit[limb + 2]) & mask) << uint(res_shift); - res |= v; - + res |= (_WORD(a.digit[limb + 2]) & mask) << uint(res_shift); return res, nil; } From 9858989b1cd053342e68deb247e8fc475f97255a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Aug 2021 18:38:38 +0200 Subject: [PATCH 079/105] big: Split up `add` and `sub` into public and internal parts. --- core/math/big/basic.odin | 444 ++--------------------------------- core/math/big/build.bat | 14 +- core/math/big/example.odin | 69 ------ core/math/big/internal.odin | 446 ++++++++++++++++++++++++++++++++++++ core/math/big/test.odin | 18 +- core/math/big/tune.odin | 73 ++++++ 6 files changed, 556 insertions(+), 508 deletions(-) create mode 100644 core/math/big/internal.odin create mode 100644 core/math/big/tune.odin diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index eaa1d5eb7..86554748a 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -23,35 +23,12 @@ import "core:intrinsics" /* High-level addition. Handles sign. */ -int_add :: proc(dest, a, b: ^Int) -> (err: Error) { - dest := dest; x := a; y := b; - if err = clear_if_uninitialized(x); err != nil { return err; } - if err = clear_if_uninitialized(y); err != nil { return err; } - if err = clear_if_uninitialized(dest); err != nil { return err; } +int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + if err = clear_if_uninitialized(dest, a, b); err != nil { return err; } /* All parameters have been initialized. - We can now safely ignore errors from comparison routines. */ - - /* - Handle both negative or both positive. - */ - if x.sign == y.sign { - dest.sign = x.sign; - return _int_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 c, _ := cmp_mag(a, b); c == -1 { - x, y = y, x; - } - - dest.sign = x.sign; - return _int_sub(dest, x, y); + return #force_inline internal_int_add_signed(dest, a, b, allocator); } /* @@ -60,178 +37,28 @@ int_add :: proc(dest, a, b: ^Int) -> (err: Error) { dest = a + digit; */ -int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { - dest := dest; digit := digit; - if err = clear_if_uninitialized(a); err != nil { - return err; - } +int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { + if err = clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. */ - if err = grow(dest, a.used + 1); err != nil { - return err; - } + if err = grow(dest, a.used + 1, false, allocator); err != nil { return err; } /* All parameters have been initialized. - We can now safely ignore errors from comparison routines. */ - - /* - 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 p, _ := is_pos(dest); p && (dest.digit[0] + digit < _DIGIT_MAX) { - dest.digit[0] += digit; - dest.used += 1; - return clamp(dest); - } - /* - Can be subtracted from dest.digit[0] without underflow. - */ - if n, _ := is_neg(a); n && (dest.digit[0] > digit) { - dest.digit[0] -= digit; - dest.used += 1; - return clamp(dest); - } - } - - /* - If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit` - */ - if n, _ := is_neg(a); n && (a.used > 1 || a.digit[0] >= digit) { - /* - Temporarily fix `a`'s sign. - */ - a.sign = .Zero_or_Positive; - /* - dest = |a| - digit - */ - if err = sub(dest, a, digit); err != nil { - /* - Restore a's sign. - */ - a.sign = .Negative; - return err; - } - /* - Restore sign and set `dest` sign. - */ - a.sign = .Negative; - dest.sign = .Negative; - - return clamp(dest); - } - - /* - Remember the currently used number of digits in `dest`. - */ - old_used := dest.used; - - /* - If `a` is positive - */ - if p, _ := is_pos(a); p { - /* - 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. - */ - return clamp(dest); + return #force_inline internal_int_add_digit(dest, a, digit); } /* High-level subtraction, dest = number - decrease. Handles signs. */ -int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { - dest := dest; x := number; y := decrease; - if err = clear_if_uninitialized(dest); err != nil { - return err; - } - if err = clear_if_uninitialized(x); err != nil { - return err; - } - if err = clear_if_uninitialized(y); err != nil { - return err; - } +int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { + if err = clear_if_uninitialized(dest, number, decrease); err != nil { return err; } /* All parameters have been initialized. - We can now safely ignore errors from comparison routines. */ - - 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 _int_add(dest, x, y); - } - - /* - Subtract a positive from a positive, OR negative from a negative. - First, take the difference between their magnitudes, then... - */ - if c, _ := cmp_mag(x, y); c == -1 { - /* - The second has a larger magnitude. - The result has the *opposite* sign from the first number. - */ - if p, _ := is_pos(x); p { - dest.sign = .Negative; - } else { - dest.sign = .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 _int_sub(dest, x, y); + return #force_inline internal_int_sub_signed(dest, number, decrease, allocator); } /* @@ -240,99 +67,19 @@ int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { dest = a - digit; */ -int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { - dest := dest; digit := digit; - if err = clear_if_uninitialized(dest); err != nil { - return err; - } +int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { + if err = clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. */ - if dest != a { - if err = grow(dest, a.used + 1); err != nil { - return err; - } - } + if err = grow(dest, a.used + 1, false, allocator); err != nil { return err; } + /* All parameters have been initialized. - We can now safely ignore errors from comparison routines. */ - - /* - 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 n, _ := is_neg(dest); n && (dest.digit[0] + digit < _DIGIT_MAX) { - dest.digit[0] += digit; - return nil; - } - /* - Can be subtracted from dest.digit[0] without underflow. - */ - if p, _ := is_pos(a); p && (dest.digit[0] > digit) { - dest.digit[0] -= digit; - return nil; - } - } - - /* - If `a` is negative, just do an unsigned addition (with fudged signs). - */ - if n, _ := is_neg(a); n { - 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. - */ - z, _ := is_zero(a); - - if a.used == 1 && (a.digit[0] <= digit) || z { - 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. - */ - return clamp(dest); + return #force_inline internal_int_sub_digit(dest, a, digit); } - /* dest = src / 2 dest = src >> 1 @@ -870,167 +617,6 @@ int_choose_digit :: proc(res: ^Int, n, k: DIGIT) -> (err: Error) { } choose :: proc { int_choose_digit, }; -/* - ========================== - Low-level routines - ========================== -*/ - -/* - Low-level addition, unsigned. - Handbook of Applied Cryptography, algorithm 14.7. -*/ -_int_add :: proc(dest, a, b: ^Int) -> (err: Error) { - dest := dest; x := a; y := b; - - old_used, min_used, max_used, i: int; - - if x.used < y.used { - x, y = y, x; - assert(x.used >= y.used); - } - - min_used = y.used; - max_used = x.used; - old_used = dest.used; - - if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != nil { - return err; - } - dest.used = max_used + 1; - /* - All parameters have been initialized. - */ - - /* Zero the carry */ - carry := DIGIT(0); - - #no_bounds_check 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. - */ - #no_bounds_check 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. - */ - return clamp(dest); -} - -/* - Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|. - Handbook of Applied Cryptography, algorithm 14.9. -*/ -_int_sub :: proc(dest, number, decrease: ^Int) -> (err: Error) { - dest := dest; x := number; y := decrease; - if err = clear_if_uninitialized(x); err != nil { - return err; - } - if err = clear_if_uninitialized(y); err != nil { - return err; - } - - old_used := dest.used; - min_used := y.used; - max_used := x.used; - i: int; - - if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != nil { - return err; - } - dest.used = max_used; - /* - All parameters have been initialized. - */ - - borrow := DIGIT(0); - - #no_bounds_check 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 - */ - #no_bounds_check 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. - */ - return clamp(dest); -} - - /* Multiplies |a| * |b| and only computes upto digs digits of result. HAC pp. 595, Algorithm 14.12 Modified so you can control how diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 2c1edfcec..94c8c7144 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -odin run . -vet +:odin run . -vet-more : -o:size -no-bounds-check -:odin build . -build-mode:shared -show-timings -o:minimal -use-separate-modules -:odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -no-bounds-check -:odin build . -build-mode:shared -show-timings -o:size -use-separate-modules -:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules -no-bounds-check -:odin build . -build-mode:shared -show-timings -o:speed -use-separate-modules +:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:size +odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:speed -:python test.py \ No newline at end of file +python test.py \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index d46ce5e9d..806aafda6 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -12,7 +12,6 @@ package big import "core:fmt" import "core:mem" -import "core:time" print_configation :: proc() { fmt.printf( @@ -42,74 +41,6 @@ _SQR_TOOM_CUTOFF, } -print_timings :: proc() { - fmt.printf("Timings:\n"); - for v, i in Timings { - if v.count > 0 { - avg_ticks := time.Duration(f64(v.ticks) / f64(v.count)); - avg_cycles := f64(v.cycles) / f64(v.count); - - avg_s: string; - switch { - case avg_ticks < time.Microsecond: - avg_s = fmt.tprintf("%v ns / %v cycles", time.duration_nanoseconds(avg_ticks), avg_cycles); - case avg_ticks < time.Millisecond: - avg_s = fmt.tprintf("%v µs / %v cycles", time.duration_microseconds(avg_ticks), avg_cycles); - case: - avg_s = fmt.tprintf("%v ms / %v cycles", time.duration_milliseconds(avg_ticks), avg_cycles); - } - - total_s: string; - switch { - case v.ticks < time.Microsecond: - total_s = fmt.tprintf("%v ns / %v cycles", time.duration_nanoseconds(v.ticks), v.cycles); - case v.ticks < time.Millisecond: - total_s = fmt.tprintf("%v µs / %v cycles", time.duration_microseconds(v.ticks), v.cycles); - case: - total_s = fmt.tprintf("%v ms / %v cycles", time.duration_milliseconds(v.ticks), v.cycles); - } - - fmt.printf("\t%v: %s (avg), %s (total, %v calls)\n", i, avg_s, total_s, v.count); - } - } -} - -@(deferred_in_out=_SCOPE_END) -SCOPED_TIMING :: #force_inline proc(c: Category) -> (ticks: time.Tick, cycles: u64) { - cycles = time.read_cycle_counter(); - ticks = time.tick_now(); - return; -} -_SCOPE_END :: #force_inline proc(c: Category, ticks: time.Tick, cycles: u64) { - cycles_now := time.read_cycle_counter(); - ticks_now := time.tick_now(); - - Timings[c].ticks = time.tick_diff(ticks, ticks_now); - Timings[c].cycles = cycles_now - cycles; - Timings[c].count += 1; -} -SCOPED_COUNT_ADD :: #force_inline proc(c: Category, count: int) { - Timings[c].count += count; -} - -Category :: enum { - itoa, - atoi, - factorial, - factorial_bin, - choose, - lsb, - ctz, - bitfield_extract, -}; - -Event :: struct { - ticks: time.Duration, - count: int, - cycles: u64, -} -Timings := [Category]Event{}; - print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) { as, err := itoa(a, base); diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin new file mode 100644 index 000000000..7f9b2f7fe --- /dev/null +++ b/core/math/big/internal.odin @@ -0,0 +1,446 @@ +//+ignore +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. + + ========================== Low-level routines ========================== + + IMPORTANT: `internal_*` procedures make certain assumptions about their input. + + The public functions that call them are expected to satisfy their sanity check requirements. + This allows `internal_*` call `internal_*` without paying this overhead multiple times. + + Where errors can occur, they are of course still checked and returned as appropriate. + + When importing `math:core/big` to implement an involved algorithm of your own, you are welcome + to use these procedures instead of their public counterparts. + + Most inputs and outputs are expected to be passed an initialized `Int`, for example. + Exceptions include `quotient` and `remainder`, which are allowed to be `nil` when the calling code doesn't need them. + + Check the comments above each `internal_*` implementation to see what constraints it expects to have met. +*/ + +import "core:mem" + +/* + Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. + + Assumptions: + `dest`, `a` and `b` != `nil` and have been initalized. +*/ +internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + dest := dest; x := a; y := b; + + old_used, min_used, max_used, i: int; + + if x.used < y.used { + x, y = y, x; + assert(x.used >= y.used); + } + + min_used = y.used; + max_used = x.used; + old_used = dest.used; + + if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } + dest.used = max_used + 1; + /* + All parameters have been initialized. + */ + + /* Zero the carry */ + carry := DIGIT(0); + + #no_bounds_check 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. + */ + #no_bounds_check 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. + */ + return clamp(dest); +} + +/* + Low-level addition, signed. Handbook of Applied Cryptography, algorithm 14.7. + + Assumptions: + `dest`, `a` and `b` != `nil` and have been initalized. +*/ +internal_int_add_signed :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + x := a; y := b; + /* + Handle both negative or both positive. + */ + if x.sign == y.sign { + dest.sign = x.sign; + return #force_inline internal_int_add_unsigned(dest, x, y, allocator); + } + + /* + 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 c, _ := #force_inline cmp_mag(a, b); c == -1 { + x, y = y, x; + } + + dest.sign = x.sign; + return #force_inline internal_int_sub_unsigned(dest, x, y, allocator); +} + +/* + Low-level addition Int+DIGIT, signed. Handbook of Applied Cryptography, algorithm 14.7. + + Assumptions: + `dest` and `a` != `nil` and have been initalized. + `dest` is large enough (a.used + 1) to fit result. +*/ +internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { + /* + 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 dest.sign == .Zero_or_Positive && (dest.digit[0] + digit < _DIGIT_MAX) { + dest.digit[0] += digit; + dest.used += 1; + return clamp(dest); + } + /* + Can be subtracted from dest.digit[0] without underflow. + */ + if a.sign == .Negative && (dest.digit[0] > digit) { + dest.digit[0] -= digit; + dest.used += 1; + return clamp(dest); + } + } + + /* + If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit` + */ + if a.sign == .Negative && (a.used > 1 || a.digit[0] >= digit) { + /* + Temporarily fix `a`'s sign. + */ + a.sign = .Zero_or_Positive; + /* + dest = |a| - digit + */ + if err = #force_inline internal_int_add_digit(dest, a, digit); err != nil { + /* + Restore a's sign. + */ + a.sign = .Negative; + return err; + } + /* + Restore sign and set `dest` sign. + */ + a.sign = .Negative; + dest.sign = .Negative; + + return clamp(dest); + } + + /* + Remember the currently used number of digits in `dest`. + */ + old_used := dest.used; + + /* + If `a` is positive + */ + if a.sign == .Zero_or_Positive { + /* + Add digits, use `carry`. + */ + i: int; + carry := digit; + #no_bounds_check 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. + */ + return clamp(dest); +} + +internal_add :: proc { internal_int_add_signed, internal_int_add_digit, }; + +/* + Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|. + Handbook of Applied Cryptography, algorithm 14.9. + + Assumptions: + `dest`, `number` and `decrease` != `nil` and have been initalized. +*/ +internal_int_sub_unsigned :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { + dest := dest; x := number; y := decrease; + old_used := dest.used; + min_used := y.used; + max_used := x.used; + i: int; + + if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } + dest.used = max_used; + /* + All parameters have been initialized. + */ + + borrow := DIGIT(0); + + #no_bounds_check 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 + */ + #no_bounds_check 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. + */ + return clamp(dest); +} + +/* + Low-level subtraction, signed. Handbook of Applied Cryptography, algorithm 14.9. + dest = number - decrease. Assumes |number| > |decrease|. + + Assumptions: + `dest`, `number` and `decrease` != `nil` and have been initalized. +*/ +internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { + number := number; decrease := decrease; + if number.sign != decrease.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 = number.sign; + return #force_inline internal_int_add_unsigned(dest, number, decrease, allocator); + } + + /* + Subtract a positive from a positive, OR negative from a negative. + First, take the difference between their magnitudes, then... + */ + if c, _ := #force_inline cmp_mag(number, decrease); c == -1 { + /* + The second has a larger magnitude. + The result has the *opposite* sign from the first number. + */ + dest.sign = .Negative if number.sign == .Zero_or_Positive else .Zero_or_Positive; + number, decrease = decrease, number; + } else { + /* + The first has a larger or equal magnitude. + Copy the sign from the first. + */ + dest.sign = number.sign; + } + return #force_inline internal_int_sub_unsigned(dest, number, decrease, allocator); +} + +/* + Low-level subtraction, signed. Handbook of Applied Cryptography, algorithm 14.9. + dest = number - decrease. Assumes |number| > |decrease|. + + Assumptions: + `dest`, `number` != `nil` and have been initalized. + `dest` is large enough (number.used + 1) to fit result. +*/ +internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT) -> (err: Error) { + dest := dest; digit := digit; + /* + All parameters have been initialized. + + Fast paths for destination and input Int being the same. + */ + if dest == number { + /* + Fast path for `dest` is negative and unsigned addition doesn't overflow the lowest digit. + */ + if dest.sign == .Negative && (dest.digit[0] + digit < _DIGIT_MAX) { + dest.digit[0] += digit; + return nil; + } + /* + Can be subtracted from dest.digit[0] without underflow. + */ + if number.sign == .Zero_or_Positive && (dest.digit[0] > digit) { + dest.digit[0] -= digit; + return nil; + } + } + + /* + If `a` is negative, just do an unsigned addition (with fudged signs). + */ + if number.sign == .Negative { + t := number; + t.sign = .Zero_or_Positive; + + err = #force_inline internal_int_add_digit(dest, t, digit); + dest.sign = .Negative; + + clamp(dest); + return err; + } + + old_used := dest.used; + + /* + if `a`<= digit, simply fix the single digit. + */ + if number.used == 1 && (number.digit[0] <= digit) || number.used == 0 { + dest.digit[0] = digit - number.digit[0] if number.used == 1 else digit; + dest.sign = .Negative; + dest.used = 1; + } else { + dest.sign = .Zero_or_Positive; + dest.used = number.used; + + /* + Subtract with carry. + */ + carry := digit; + + #no_bounds_check for i := 0; i < number.used; i += 1 { + dest.digit[i] = number.digit[i] - carry; + carry := dest.digit[i] >> (_DIGIT_TYPE_BITS - 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. + */ + return clamp(dest); +} + +internal_sub :: proc { internal_int_sub_signed, internal_int_sub_digit, }; \ No newline at end of file diff --git a/core/math/big/test.odin b/core/math/big/test.odin index fb0b0ea6f..19185da77 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -9,7 +9,11 @@ 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`, `sub`, `mul`, `div`, ... + This file exports procedures for use with the test.py test suite. +*/ + +/* + TODO: Write tests for `internal_*` and test reusing parameters with the public implementations. */ import "core:runtime" @@ -35,7 +39,11 @@ PyRes :: struct { if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err}; } - if err = add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } + if bb.used == 1 { + if err = add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } + } else { + if err = add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } + } r: cstring; r, err = int_itoa_cstring(sum, 16, context.temp_allocator); @@ -52,7 +60,11 @@ PyRes :: struct { if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err}; } - if err = sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } + if bb.used == 1 { + if err = sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } + } else { + if err = sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } + } r: cstring; r, err = int_itoa_cstring(sum, 16, context.temp_allocator); diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin new file mode 100644 index 000000000..1088b3c2b --- /dev/null +++ b/core/math/big/tune.odin @@ -0,0 +1,73 @@ +//+ignore +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. +*/ + +import "core:fmt" +import "core:time" + +Category :: enum { + itoa, + atoi, + factorial, + factorial_bin, + choose, + lsb, + ctz, + bitfield_extract, +}; + +Event :: struct { + ticks: time.Duration, + count: int, + cycles: u64, +} +Timings := [Category]Event{}; + +print_timings :: proc() { + duration :: proc(d: time.Duration) -> (res: string) { + switch { + case d < time.Microsecond: + return fmt.tprintf("%v ns", time.duration_nanoseconds(d)); + case d < time.Millisecond: + return fmt.tprintf("%v µs", time.duration_microseconds(d)); + case: + return fmt.tprintf("%v ms", time.duration_milliseconds(d)); + } + } + + fmt.println("\nTimings:"); + for v, i in Timings { + if v.count > 0 { + avg_ticks := time.Duration(f64(v.ticks) / f64(v.count)); + avg_cycles := f64(v.cycles) / f64(v.count); + + fmt.printf("\t%v: %s / %v cycles (avg), %s / %v cycles (total, %v calls)\n", i, duration(avg_ticks), avg_cycles, duration(v.ticks), v.cycles, v.count); + } + } +} + +@(deferred_in_out=_SCOPE_END) +SCOPED_TIMING :: #force_inline proc(c: Category) -> (ticks: time.Tick, cycles: u64) { + cycles = time.read_cycle_counter(); + ticks = time.tick_now(); + return; +} +_SCOPE_END :: #force_inline proc(c: Category, ticks: time.Tick, cycles: u64) { + cycles_now := time.read_cycle_counter(); + ticks_now := time.tick_now(); + + Timings[c].ticks = time.tick_diff(ticks, ticks_now); + Timings[c].cycles = cycles_now - cycles; + Timings[c].count += 1; +} +SCOPED_COUNT_ADD :: #force_inline proc(c: Category, count: int) { + Timings[c].count += count; +} From f8442e0524faddf73487d71f56a0bdd21856c2e1 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Aug 2021 20:49:50 +0200 Subject: [PATCH 080/105] big: Split up `mul` into internal and public parts. --- core/math/big/api.odin | 11 +- core/math/big/basic.odin | 326 +++++++----------------------------- core/math/big/helpers.odin | 12 +- core/math/big/internal.odin | 239 +++++++++++++++++++++++++- core/math/big/test.odin | 5 + core/math/big/test.py | 3 + 6 files changed, 316 insertions(+), 280 deletions(-) diff --git a/core/math/big/api.odin b/core/math/big/api.odin index d2eb21fc5..047e91335 100644 --- a/core/math/big/api.odin +++ b/core/math/big/api.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 collects public procs into proc maps, as well as any of their aliases. + This file collects public proc maps and their aliases. /* */ @@ -19,15 +19,18 @@ package big */ /* - err = add(dest, a, b); + High-level addition. Handles sign. */ add :: proc { /* - int_add :: proc(dest, a, b: ^Int) -> (err: Error) + int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) */ int_add, /* - int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) + Adds the unsigned `DIGIT` immediate to an `Int`, such that the + `DIGIT` doesn't have to be turned into an `Int` first. + + int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) */ int_add_digit, }; diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 86554748a..a06c275b8 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -24,6 +24,7 @@ import "core:intrinsics" High-level addition. Handles sign. */ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + if dest == nil || a == nil || b == nil { return .Invalid_Pointer; } if err = clear_if_uninitialized(dest, a, b); err != nil { return err; } /* All parameters have been initialized. @@ -38,6 +39,7 @@ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error dest = a + digit; */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { + if dest == nil || a == nil { return .Invalid_Pointer; } if err = clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. @@ -54,6 +56,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato High-level subtraction, dest = number - decrease. Handles signs. */ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { + if dest == nil || number == nil || decrease == nil { return .Invalid_Pointer; } if err = clear_if_uninitialized(dest, number, decrease); err != nil { return err; } /* All parameters have been initialized. @@ -68,6 +71,7 @@ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> dest = a - digit; */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { + if dest == nil || a == nil { return .Invalid_Pointer; } if err = clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. @@ -85,55 +89,14 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato dest = src >> 1 */ int_halve :: proc(dest, src: ^Int) -> (err: Error) { - dest := dest; src := src; - if err = clear_if_uninitialized(dest); err != nil { - return err; - } + if dest == nil || src == nil { return .Invalid_Pointer; } + if err = clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. */ - if dest != src { - if err = grow(dest, src.used); err != nil { - return err; - } - } + if dest != src { if err = grow(dest, src.used + 1); err != nil { return err; } } - old_used := dest.used; - dest.used = src.used; - - /* - Carry - */ - fwd_carry := DIGIT(0); - - for x := dest.used - 1; 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); + return #force_inline internal_int_shr1(dest, src); } halve :: proc { int_halve, }; shr1 :: halve; @@ -143,251 +106,36 @@ shr1 :: halve; dest = src << 1 */ int_double :: proc(dest, src: ^Int) -> (err: Error) { - dest := dest; src := src; - if err = clear_if_uninitialized(dest); err != nil { - return err; - } + if dest == nil || src == nil { return .Invalid_Pointer; } + if err = clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. */ - if dest != src { - if err = grow(dest, src.used + 1); err != nil { - return err; - } - } + if dest != src { if err = grow(dest, src.used + 1); err != nil { 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); + return #force_inline internal_int_shl1(dest, src); } double :: proc { int_double, }; shl1 :: double; -/* - remainder = numerator % (1 << bits) -*/ -int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { - if err = clear_if_uninitialized(remainder); err != nil { return err; } - if err = clear_if_uninitialized(numerator); err != nil { return err; } - - if bits < 0 { return .Invalid_Argument; } - if bits == 0 { return zero(remainder); } - - /* - If the modulus is larger than the value, return the value. - */ - err = copy(remainder, numerator); - if bits >= (numerator.used * _DIGIT_BITS) || err != nil { - return; - } - - /* - Zero digits above the last digit of the modulus. - */ - zero_count := (bits / _DIGIT_BITS) + 0 if (bits % _DIGIT_BITS == 0) else 1; - /* - Zero remainder. - */ - if zero_count > 0 { - mem.zero_slice(remainder.digit[zero_count:]); - } - - /* - Clear the digit that is not completely outside/inside the modulus. - */ - remainder.digit[bits / _DIGIT_BITS] &= DIGIT(1 << DIGIT(bits % _DIGIT_BITS)) - DIGIT(1); - return clamp(remainder); -} -mod_bits :: proc { int_mod_bits, }; - /* Multiply by a DIGIT. */ -int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT) -> (err: Error) { +int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) { + if dest == nil || src == nil { return .Invalid_Pointer; } if err = clear_if_uninitialized(src, dest); err != nil { return err; } - if multiplier == 0 { - return zero(dest); - } - if multiplier == 1 { - return copy(dest, src); - } - - /* - Power of two? - */ - if multiplier == 2 { - return double(dest, src); - } - if is_power_of_two(int(multiplier)) { - ix: int; - if ix, err = log(multiplier, 2); err != nil { return err; } - return shl(dest, src, ix); - } - - /* - Ensure `dest` is big enough to hold `src` * `multiplier`. - */ - if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } - - /* - Save the original used count. - */ - old_used := dest.used; - /* - Set the sign. - */ - dest.sign = src.sign; - /* - Set up carry. - */ - carry := _WORD(0); - /* - Compute columns. - */ - ix := 0; - for ; ix < src.used; ix += 1 { - /* - Compute product and carry sum for this term - */ - product := carry + _WORD(src.digit[ix]) * _WORD(multiplier); - /* - Mask off higher bits to get a single DIGIT. - */ - dest.digit[ix] = DIGIT(product & _WORD(_MASK)); - /* - Send carry into next iteration - */ - carry = product >> _DIGIT_BITS; - } - - /* - Store final carry [if any] and increment used. - */ - dest.digit[ix] = DIGIT(carry); - dest.used = src.used + 1; - /* - Zero unused digits. - */ - zero_count := old_used - dest.used; - if zero_count > 0 { - mem.zero_slice(dest.digit[zero_count:]); - } - return clamp(dest); + return #force_inline internal_int_mul_digit(dest, src, multiplier, allocator); } /* High level multiplication (handles sign). */ -int_mul :: proc(dest, src, multiplier: ^Int) -> (err: Error) { +int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) { + if dest == nil || src == nil || multiplier == nil { return .Invalid_Pointer; } if err = clear_if_uninitialized(dest, src, multiplier); err != nil { return err; } - /* - Early out for `multiplier` is zero; Set `dest` to zero. - */ - if z, _ := is_zero(multiplier); z { return zero(dest); } - if z, _ := is_zero(src); z { return zero(dest); } - - if src == multiplier { - /* - Do we need to square? - */ - if false && src.used >= _SQR_TOOM_CUTOFF { - /* Use Toom-Cook? */ - // err = s_mp_sqr_toom(a, c); - } else if false && src.used >= _SQR_KARATSUBA_CUTOFF { - /* Karatsuba? */ - // err = s_mp_sqr_karatsuba(a, c); - } else if false && ((src.used * 2) + 1) < _WARRAY && - src.used < (_MAX_COMBA / 2) { - /* Fast comba? */ - // err = s_mp_sqr_comba(a, c); - } else { - err = _int_sqr(dest, src); - } - } else { - /* - Can we use the balance method? Check sizes. - * The smaller one needs to be larger than the Karatsuba cut-off. - * The bigger one needs to be at least about one `_MUL_KARATSUBA_CUTOFF` bigger - * to make some sense, but it depends on architecture, OS, position of the - * stars... so YMMV. - * Using it to cut the input into slices small enough for _mul_comba - * was actually slower on the author's machine, but YMMV. - */ - - min_used := min(src.used, multiplier.used); - max_used := max(src.used, multiplier.used); - digits := src.used + multiplier.used + 1; - - if false && min_used >= _MUL_KARATSUBA_CUTOFF && - max_used / 2 >= _MUL_KARATSUBA_CUTOFF && - /* - Not much effect was observed below a ratio of 1:2, but again: YMMV. - */ - max_used >= 2 * min_used { - // err = s_mp_mul_balance(a,b,c); - } else if false && min_used >= _MUL_TOOM_CUTOFF { - // err = s_mp_mul_toom(a, b, c); - } else if false && min_used >= _MUL_KARATSUBA_CUTOFF { - // err = s_mp_mul_karatsuba(a, b, c); - } else if digits < _WARRAY && min_used <= _MAX_COMBA { - /* - Can we use the fast multiplier? - * The fast multiplier can be used if the output will - * have less than MP_WARRAY digits and the number of - * digits won't affect carry propagation - */ - err = _int_mul_comba(dest, src, multiplier, digits); - } else { - err = _int_mul(dest, src, multiplier, digits); - } - } - neg := src.sign != multiplier.sign; - dest.sign = .Negative if dest.used > 0 && neg else .Zero_or_Positive; - return err; + return #force_inline internal_int_mul(dest, src, multiplier, allocator); } mul :: proc { int_mul, int_mul_digit, }; @@ -1219,6 +967,7 @@ _int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remain Function computing both GCD and (if target isn't `nil`) also LCM. */ int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { + if res_gcd == nil && res_lcm == nil { return nil; } if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != nil { return err; } az, _ := is_zero(a); bz, _ := is_zero(b); @@ -1435,6 +1184,43 @@ _int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { return err; } +/* + remainder = numerator % (1 << bits) +*/ +int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { + if err = clear_if_uninitialized(remainder); err != nil { return err; } + if err = clear_if_uninitialized(numerator); err != nil { return err; } + + if bits < 0 { return .Invalid_Argument; } + if bits == 0 { return zero(remainder); } + + /* + If the modulus is larger than the value, return the value. + */ + err = copy(remainder, numerator); + if bits >= (numerator.used * _DIGIT_BITS) || err != nil { + return; + } + + /* + Zero digits above the last digit of the modulus. + */ + zero_count := (bits / _DIGIT_BITS) + 0 if (bits % _DIGIT_BITS == 0) else 1; + /* + Zero remainder. + */ + if zero_count > 0 { + mem.zero_slice(remainder.digit[zero_count:]); + } + + /* + Clear the digit that is not completely outside/inside the modulus. + */ + remainder.digit[bits / _DIGIT_BITS] &= DIGIT(1 << DIGIT(bits % _DIGIT_BITS)) - DIGIT(1); + return clamp(remainder); +} +mod_bits :: proc { int_mod_bits, }; + when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { _factorial_table := [35]_WORD{ diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 054b05abd..eacd15283 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -287,7 +287,7 @@ int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := conte /* If not yet iniialized, initialize the `digit` backing with the allocator we were passed. - Otherwise, `[dynamic]DIGIT` already knows what allocator was used for it, so reuse will do the right thing. + Otherwise, `[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing. */ if raw.cap == 0 { a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, needed, needed, allocator); @@ -314,7 +314,7 @@ int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> raw := transmute(mem.Raw_Dynamic_Array)a.digit; if raw.cap != 0 { - mem.zero_slice(a.digit[:]); + mem.zero_slice(a.digit[:a.used]); } a.sign = .Zero_or_Positive; a.used = 0; @@ -328,7 +328,7 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return set(a, 1, minimize, allocator); + return copy(a, ONE, minimize, allocator); } one :: proc { int_one, }; @@ -597,6 +597,7 @@ _zero_unused :: proc(a: ^Int) { clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) { if !is_initialized(arg) { + if arg == nil { return nil; } return grow(arg, _DEFAULT_DIGIT_COUNT); } return err; @@ -604,7 +605,8 @@ clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) { clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { for i in args { - if i != nil && !is_initialized(i) { + if i == nil { continue; } + if !is_initialized(i) { e := grow(i, _DEFAULT_DIGIT_COUNT); if e != nil { err = e; } } @@ -690,9 +692,9 @@ initialize_constants :: proc() -> (res: int) { We set these special values to -1 or 1 so they don't get mistake for zero accidentally. This allows for shortcut tests of is_zero as .used == 0. */ + set( NAN, 1); NAN.flags = {.Immutable, .NaN}; set( INF, 1); INF.flags = {.Immutable, .Inf}; set( INF, -1); MINUS_INF.flags = {.Immutable, .Inf}; - set( NAN, 1); NAN.flags = {.Immutable, .NaN}; return #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 7f9b2f7fe..df0acbf43 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -443,4 +443,241 @@ internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT) -> (err: Error) return clamp(dest); } -internal_sub :: proc { internal_int_sub_signed, internal_int_sub_digit, }; \ No newline at end of file +internal_sub :: proc { internal_int_sub_signed, internal_int_sub_digit, }; + +/* + dest = src / 2 + dest = src >> 1 +*/ +internal_int_shr1 :: proc(dest, src: ^Int) -> (err: Error) { + old_used := dest.used; dest.used = src.used; + /* + Carry + */ + fwd_carry := DIGIT(0); + + #no_bounds_check for x := dest.used - 1; 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); +} + +/* + dest = src * 2 + dest = src << 1 +*/ +internal_int_shl1 :: proc(dest, src: ^Int) -> (err: Error) { + old_used := dest.used; dest.used = src.used + 1; + + /* + Forward carry + */ + carry := DIGIT(0); + #no_bounds_check 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); +} + +/* + Multiply by a DIGIT. +*/ +internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) { + if multiplier == 0 { + return zero(dest); + } + if multiplier == 1 { + return copy(dest, src); + } + + /* + Power of two? + */ + if multiplier == 2 { + return #force_inline shl1(dest, src); + } + if is_power_of_two(int(multiplier)) { + ix: int; + if ix, err = log(multiplier, 2); err != nil { return err; } + return shl(dest, src, ix); + } + + /* + Ensure `dest` is big enough to hold `src` * `multiplier`. + */ + if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } + + /* + Save the original used count. + */ + old_used := dest.used; + /* + Set the sign. + */ + dest.sign = src.sign; + /* + Set up carry. + */ + carry := _WORD(0); + /* + Compute columns. + */ + ix := 0; + #no_bounds_check for ; ix < src.used; ix += 1 { + /* + Compute product and carry sum for this term + */ + product := carry + _WORD(src.digit[ix]) * _WORD(multiplier); + /* + Mask off higher bits to get a single DIGIT. + */ + dest.digit[ix] = DIGIT(product & _WORD(_MASK)); + /* + Send carry into next iteration + */ + carry = product >> _DIGIT_BITS; + } + + /* + Store final carry [if any] and increment used. + */ + dest.digit[ix] = DIGIT(carry); + dest.used = src.used + 1; + /* + Zero unused digits. + */ + zero_count := old_used - dest.used; + if zero_count > 0 { + mem.zero_slice(dest.digit[zero_count:]); + } + return clamp(dest); +} + +/* + High level multiplication (handles sign). +*/ +internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) { + /* + Early out for `multiplier` is zero; Set `dest` to zero. + */ + if multiplier.used == 0 || src.used == 0 { return zero(dest); } + + if src == multiplier { + /* + Do we need to square? + */ + if false && src.used >= _SQR_TOOM_CUTOFF { + /* Use Toom-Cook? */ + // err = s_mp_sqr_toom(a, c); + } else if false && src.used >= _SQR_KARATSUBA_CUTOFF { + /* Karatsuba? */ + // err = s_mp_sqr_karatsuba(a, c); + } else if false && ((src.used * 2) + 1) < _WARRAY && + src.used < (_MAX_COMBA / 2) { + /* Fast comba? */ + // err = s_mp_sqr_comba(a, c); + } else { + err = _int_sqr(dest, src); + } + } else { + /* + Can we use the balance method? Check sizes. + * The smaller one needs to be larger than the Karatsuba cut-off. + * The bigger one needs to be at least about one `_MUL_KARATSUBA_CUTOFF` bigger + * to make some sense, but it depends on architecture, OS, position of the + * stars... so YMMV. + * Using it to cut the input into slices small enough for _mul_comba + * was actually slower on the author's machine, but YMMV. + */ + + min_used := min(src.used, multiplier.used); + max_used := max(src.used, multiplier.used); + digits := src.used + multiplier.used + 1; + + if false && min_used >= _MUL_KARATSUBA_CUTOFF && + max_used / 2 >= _MUL_KARATSUBA_CUTOFF && + /* + Not much effect was observed below a ratio of 1:2, but again: YMMV. + */ + max_used >= 2 * min_used { + // err = s_mp_mul_balance(a,b,c); + } else if false && min_used >= _MUL_TOOM_CUTOFF { + // err = s_mp_mul_toom(a, b, c); + } else if false && min_used >= _MUL_KARATSUBA_CUTOFF { + // err = s_mp_mul_karatsuba(a, b, c); + } else if digits < _WARRAY && min_used <= _MAX_COMBA { + /* + Can we use the fast multiplier? + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + err = _int_mul_comba(dest, src, multiplier, digits); + } else { + err = _int_mul(dest, src, multiplier, digits); + } + } + neg := src.sign != multiplier.sign; + dest.sign = .Negative if dest.used > 0 && neg else .Zero_or_Positive; + return err; +} + +internal_mul :: proc { internal_int_mul, internal_int_mul_digit, }; \ No newline at end of file diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 19185da77..ae105969c 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -24,6 +24,11 @@ PyRes :: struct { err: Error, } +@export test_initialize_constants :: proc "c" () -> (res: int) { + context = runtime.default_context(); + return initialize_constants(); +} + @export test_error_string :: proc "c" (err: Error) -> (res: cstring) { context = runtime.default_context(); es := Error_String; diff --git a/core/math/big/test.py b/core/math/big/test.py index f7e341ce7..836f03353 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -113,6 +113,9 @@ def load(export_name, args, res): class Res(Structure): _fields_ = [("res", c_char_p), ("err", c_uint64)] +initialize_constants = load(l.test_initialize_constants, [], c_uint64) +initialize_constants() + error_string = load(l.test_error_string, [c_byte], c_char_p) add = load(l.test_add, [c_char_p, c_char_p], Res) From 4be48973adc323838682d7f8e1f38b4082a48024 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 6 Aug 2021 14:57:53 +0200 Subject: [PATCH 081/105] big: Squashed shl1 bug when a larger dest was reused for a smaller result. --- core/math/big/basic.odin | 49 +++----------- core/math/big/build.bat | 2 +- core/math/big/example.odin | 24 ++----- core/math/big/exp_log.odin | 2 +- core/math/big/internal.odin | 124 ++++++++++++++++++++++++------------ core/math/big/test.odin | 4 +- core/math/big/test.py | 10 ++- 7 files changed, 113 insertions(+), 102 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index a06c275b8..6c7d46ed9 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -152,45 +152,18 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E /* Early out if neither of the results is wanted. */ - if quotient == nil && remainder == nil { return nil; } + if quotient == nil && remainder == nil { return nil; } + if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } - if err = clear_if_uninitialized(numerator); err != nil { return err; } - if err = clear_if_uninitialized(denominator); err != nil { return err; } - - z: bool; - if z, err = is_zero(denominator); z { return .Division_by_Zero; } - - /* - If numerator < denominator then quotient = 0, remainder = numerator. - */ - c: int; - if c, err = cmp_mag(numerator, denominator); c == -1 { - if remainder != nil { - if err = copy(remainder, numerator); err != nil { return err; } - } - if quotient != nil { - zero(quotient); - } - return nil; - } - - if false && (denominator.used > 2 * _MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) { - // err = _int_div_recursive(quotient, remainder, numerator, denominator); - } else { - err = _int_div_school(quotient, remainder, numerator, denominator); - /* - NOTE(Jeroen): We no longer need or use `_int_div_small`. - We'll keep it around for a bit. - err = _int_div_small(quotient, remainder, numerator, denominator); - */ - } - - return err; + return #force_inline internal_int_divmod(quotient, remainder, numerator, denominator); } divmod :: proc{ int_divmod, }; int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { - return int_divmod(quotient, nil, numerator, denominator); + if quotient == nil { return .Invalid_Pointer; }; + if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } + + return #force_inline internal_int_divmod(quotient, nil, numerator, denominator); } div :: proc { int_div, }; @@ -200,11 +173,10 @@ div :: proc { int_div, }; denominator < remainder <= 0 if denominator < 0 */ int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { - if err = divmod(nil, remainder, numerator, denominator); err != nil { return err; } + if remainder == nil { return .Invalid_Pointer; }; + if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } - z: bool; - if z, err = is_zero(remainder); z || denominator.sign == remainder.sign { return nil; } - return add(remainder, remainder, numerator); + return #force_inline internal_int_mod(remainder, numerator, denominator); } int_mod_digit :: proc(numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { @@ -776,7 +748,6 @@ _int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (e t2.used = 3; if t1_t2, _ := cmp_mag(t1, t2); t1_t2 != 1 { - break; } iter += 1; if iter > 100 { return .Max_Iterations_Reached; } diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 94c8c7144..b1d2a00ee 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,5 +1,5 @@ @echo off -:odin run . -vet-more +:odin run . -vet : -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 806aafda6..4777654e1 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -62,30 +62,16 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline } demo :: proc() { - err: Error; - as: string; - defer delete(as); - a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - err = factorial(a, 1224); - count, _ := count_bits(a); + foo := "686885735734829009541949746871140768343076607029752932751182108475420900392874228486622313727012705619148037570309621219533087263900443932890792804879473795673302686046941536636874184361869252299636701671980034458333859202703255467709267777184095435235980845369829397344182319113372092844648570818726316581751114346501124871729572474923695509057166373026411194094493240101036672016770945150422252961487398124677567028263059046193391737576836378376192651849283925197438927999526058932679219572030021792914065825542626400207956134072247020690107136531852625253942429167557531123651471221455967386267137846791963149859804549891438562641323068751514370656287452006867713758971418043865298618635213551059471668293725548570452377976322899027050925842868079489675596835389444833567439058609775325447891875359487104691935576723532407937236505941186660707032433807075470656782452889754501872408562496805517394619388777930253411467941214807849472083814447498068636264021405175653742244368865090604940094889189800007448083930490871954101880815781177612910234741529950538835837693870921008635195545246771593130784786737543736434086434015200264933536294884482218945403958647118802574342840790536176272341586020230110889699633073513016344826709214"; + err := atoi(a, foo, 10); - bits := 51; - be1: _WORD; + if err != nil do fmt.printf("atoi returned %v\n", err); + + print("foo: ", a); - /* - Timing loop - */ - { - SCOPED_TIMING(.bitfield_extract); - for o := 0; o < count - bits; o += 1 { - be1, _ = int_bitfield_extract(a, o, bits); - } - } - SCOPED_COUNT_ADD(.bitfield_extract, count - bits - 1); - fmt.printf("be1: %v\n", be1); } main :: proc() { diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index e0569c586..33ebe9e21 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -253,7 +253,7 @@ int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { swap(dest, x); return err; } else { - // return root_n(dest, src, 2); + return root_n(dest, src, 2); } } sqrt :: proc { int_sqrt, }; diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index df0acbf43..ed92b9f57 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -491,49 +491,30 @@ internal_int_shr1 :: proc(dest, src: ^Int) -> (err: Error) { dest = src << 1 */ internal_int_shl1 :: proc(dest, src: ^Int) -> (err: Error) { - old_used := dest.used; dest.used = src.used + 1; - + if err = copy(dest, src); err != nil { return err; } /* - Forward carry + Grow `dest` to accommodate the additional bits. */ + digits_needed := dest.used + 1; + if err = grow(dest, digits_needed); err != nil { return err; } + dest.used = digits_needed; + + mask := (DIGIT(1) << uint(1)) - DIGIT(1); + shift := DIGIT(_DIGIT_BITS - 1); carry := DIGIT(0); - #no_bounds_check 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 - */ + #no_bounds_check for x:= 0; x < dest.used; x+= 1 { + fwd_carry := (dest.digit[x] >> shift) & mask; + dest.digit[x] = (dest.digit[x] << uint(1) | carry) & _MASK; carry = fwd_carry; } /* - New leading digit? + Use final carry. */ if carry != 0 { - /* - Add a MSB which is always 1 at this point. - */ - dest.digit[dest.used] = 1; + dest.digit[dest.used] = carry; + 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); } @@ -552,7 +533,7 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := Power of two? */ if multiplier == 2 { - return #force_inline shl1(dest, src); + return #force_inline internal_int_shl1(dest, src); } if is_power_of_two(int(multiplier)) { ix: int; @@ -581,7 +562,7 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := Compute columns. */ ix := 0; - #no_bounds_check for ; ix < src.used; ix += 1 { + for ; ix < src.used; ix += 1 { /* Compute product and carry sum for this term */ @@ -600,13 +581,15 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := Store final carry [if any] and increment used. */ dest.digit[ix] = DIGIT(carry); + dest.used = src.used + 1; /* Zero unused digits. */ + //_zero_unused(dest); zero_count := old_used - dest.used; - if zero_count > 0 { - mem.zero_slice(dest.digit[zero_count:]); + if zero_count > 0 { + mem.zero_slice(dest.digit[dest.used:][:zero_count]); } return clamp(dest); } @@ -675,9 +658,72 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc err = _int_mul(dest, src, multiplier, digits); } } - neg := src.sign != multiplier.sign; + neg := src.sign != multiplier.sign; dest.sign = .Negative if dest.used > 0 && neg else .Zero_or_Positive; return err; } -internal_mul :: proc { internal_int_mul, internal_int_mul_digit, }; \ No newline at end of file +internal_mul :: proc { internal_int_mul, internal_int_mul_digit, }; + +/* + divmod. + Both the quotient and remainder are optional and may be passed a nil. +*/ +internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { + + if denominator.used == 0 { return .Division_by_Zero; } + /* + If numerator < denominator then quotient = 0, remainder = numerator. + */ + c: int; + if c, err = #force_inline cmp_mag(numerator, denominator); c == -1 { + if remainder != nil { + if err = copy(remainder, numerator, false, allocator); err != nil { return err; } + } + if quotient != nil { + zero(quotient); + } + return nil; + } + + if false && (denominator.used > 2 * _MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) { + // err = _int_div_recursive(quotient, remainder, numerator, denominator); + } else { + when true { + err = _int_div_school(quotient, remainder, numerator, denominator); + } else { + /* + NOTE(Jeroen): We no longer need or use `_int_div_small`. + We'll keep it around for a bit until we're reasonably certain div_school is bug free. + err = _int_div_small(quotient, remainder, numerator, denominator); + */ + err = _int_div_small(quotient, remainder, numerator, denominator); + } + } + return; +} +internal_divmod :: proc { internal_int_divmod, }; + +/* + Asssumes quotient, numerator and denominator to have been initialized and not to be nil. +*/ +internal_int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { + return #force_inline internal_int_divmod(quotient, nil, numerator, denominator); +} +internal_div :: proc { internal_int_div, }; + +/* + remainder = numerator % denominator. + 0 <= remainder < denominator if denominator > 0 + denominator < remainder <= 0 if denominator < 0 + + Asssumes quotient, numerator and denominator to have been initialized and not to be nil. +*/ +internal_int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { + if err = #force_inline internal_int_divmod(nil, remainder, numerator, denominator); err != nil { return err; } + + if remainder.used == 0 || denominator.sign == remainder.sign { return nil; } + + return #force_inline internal_add(remainder, remainder, numerator); +} +internal_mod :: proc{ internal_int_mod, }; \ No newline at end of file diff --git a/core/math/big/test.odin b/core/math/big/test.odin index ae105969c..744a4436a 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -24,9 +24,9 @@ PyRes :: struct { err: Error, } -@export test_initialize_constants :: proc "c" () -> (res: int) { +@export test_initialize_constants :: proc "c" () -> (res: u64) { context = runtime.default_context(); - return initialize_constants(); + return u64(initialize_constants()); } @export test_error_string :: proc "c" (err: Error) -> (res: cstring) { diff --git a/core/math/big/test.py b/core/math/big/test.py index 836f03353..6dcb235de 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -254,7 +254,13 @@ def test_pow(base = 0, power = 0, expected_error = Error.Okay): def test_sqrt(number = 0, expected_error = Error.Okay): args = [arg_to_odin(number)] - res = int_sqrt(*args) + try: + res = int_sqrt(*args) + except OSError as e: + print("{} while trying to sqrt {}.".format(e, number)) + if EXIT_ON_FAIL: exit(3) + return False + expected_result = None if expected_error == Error.Okay: if number < 0: @@ -384,6 +390,7 @@ TESTS = { [ 54321, 12345], [ 55431, 0, Error.Division_by_Zero], [ 12980742146337069150589594264770969721, 4611686018427387904 ], + [ 831956404029821402159719858789932422, 243087903122332132 ], ], test_log: [ [ 3192, 1, Error.Invalid_Argument], @@ -405,6 +412,7 @@ TESTS = { [ 42, Error.Okay, ], [ 12345678901234567890, Error.Okay, ], [ 1298074214633706907132624082305024, Error.Okay, ], + [ 686885735734829009541949746871140768343076607029752932751182108475420900392874228486622313727012705619148037570309621219533087263900443932890792804879473795673302686046941536636874184361869252299636701671980034458333859202703255467709267777184095435235980845369829397344182319113372092844648570818726316581751114346501124871729572474923695509057166373026411194094493240101036672016770945150422252961487398124677567028263059046193391737576836378376192651849283925197438927999526058932679219572030021792914065825542626400207956134072247020690107136531852625253942429167557531123651471221455967386267137846791963149859804549891438562641323068751514370656287452006867713758971418043865298618635213551059471668293725548570452377976322899027050925842868079489675596835389444833567439058609775325447891875359487104691935576723532407937236505941186660707032433807075470656782452889754501872408562496805517394619388777930253411467941214807849472083814447498068636264021405175653742244368865090604940094889189800007448083930490871954101880815781177612910234741529950538835837693870921008635195545246771593130784786737543736434086434015200264933536294884482218945403958647118802574342840790536176272341586020230110889699633073513016344826709214, Error.Okay, ], ], test_root_n: [ [ 1298074214633706907132624082305024, 2, Error.Okay, ], From 9890e7cfeb9473b85a50afdff357d493b6bcd264 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 6 Aug 2021 17:50:34 +0200 Subject: [PATCH 082/105] big: Improved `zero_unused` helper. --- core/math/big/basic.odin | 127 +++++++---------------------- core/math/big/build.bat | 2 +- core/math/big/example.odin | 16 ++-- core/math/big/helpers.odin | 17 ++-- core/math/big/internal.odin | 156 +++++++++++++++++++++++++++++------- core/math/big/logical.odin | 2 +- core/math/big/radix.odin | 2 +- core/math/big/tune.odin | 8 +- 8 files changed, 181 insertions(+), 149 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 6c7d46ed9..831488a66 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -155,17 +155,33 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E if quotient == nil && remainder == nil { return nil; } if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } - return #force_inline internal_int_divmod(quotient, remainder, numerator, denominator); + return #force_inline internal_divmod(quotient, remainder, numerator, denominator); } -divmod :: proc{ int_divmod, }; + +int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { + if quotient == nil { return 0, .Invalid_Pointer; }; + if err = clear_if_uninitialized(numerator); err != nil { return 0, err; } + + return #force_inline internal_divmod(quotient, numerator, denominator); +} +divmod :: proc{ int_divmod, int_divmod_digit, }; int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { if quotient == nil { return .Invalid_Pointer; }; if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } - return #force_inline internal_int_divmod(quotient, nil, numerator, denominator); + return #force_inline internal_divmod(quotient, nil, numerator, denominator); } -div :: proc { int_div, }; + +int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (err: Error) { + if quotient == nil { return .Invalid_Pointer; }; + if err = clear_if_uninitialized(numerator); err != nil { return err; } + + remainder: DIGIT; + remainder, err = #force_inline internal_divmod(quotient, numerator, denominator); + return err; +} +div :: proc { int_div, int_div_digit, }; /* remainder = numerator % denominator. @@ -180,7 +196,7 @@ int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { } int_mod_digit :: proc(numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { - return _int_div_digit(nil, numerator, denominator); + return #force_inline internal_divmod(nil, numerator, denominator); } mod :: proc { int_mod, int_mod_digit, }; @@ -490,13 +506,8 @@ _int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Clear unused digits [that existed in the old copy of dest]. */ - zero_count := old_used - dest.used; - /* - Zero remainder. - */ - if zero_count > 0 { - mem.zero_slice(dest.digit[dest.used:][:zero_count]); - } + zero_unused(dest, old_used); + /* Adjust dest.used based on leading zeroes. */ @@ -848,92 +859,6 @@ _int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (er return err; } -/* - Single digit division (based on routine from MPI). -*/ -_int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { - q := &Int{}; - ix: int; - - /* - Cannot divide by zero. - */ - if denominator == 0 { - return 0, .Division_by_Zero; - } - - /* - Quick outs. - */ - if denominator == 1 || numerator.used == 0 { - err = nil; - if quotient != nil { - err = copy(quotient, numerator); - } - return 0, err; - } - /* - Power of two? - */ - if denominator == 2 { - if odd, _ := is_odd(numerator); odd { - remainder = 1; - } - if quotient == nil { - return remainder, nil; - } - return remainder, shr(quotient, numerator, 1); - } - - if is_power_of_two(int(denominator)) { - ix = 1; - for ix < _DIGIT_BITS && denominator != (1 << uint(ix)) { - ix += 1; - } - remainder = numerator.digit[0] & ((1 << uint(ix)) - 1); - if quotient == nil { - return remainder, nil; - } - - return remainder, shr(quotient, numerator, int(ix)); - } - - /* - Three? - */ - if denominator == 3 { - return _int_div_3(quotient, numerator); - } - - /* - No easy answer [c'est la vie]. Just division. - */ - if err = grow(q, numerator.used); err != nil { return 0, err; } - - q.used = numerator.used; - q.sign = numerator.sign; - - w := _WORD(0); - - for ix = numerator.used - 1; ix >= 0; ix -= 1 { - t := DIGIT(0); - w = (w << _WORD(_DIGIT_BITS) | _WORD(numerator.digit[ix])); - if w >= _WORD(denominator) { - t = DIGIT(w / _WORD(denominator)); - w -= _WORD(t) * _WORD(denominator); - } - q.digit[ix] = t; - } - remainder = DIGIT(w); - - if quotient != nil { - clamp(q); - swap(q, quotient); - } - destroy(q); - return remainder, nil; -} - /* Function computing both GCD and (if target isn't `nil`) also LCM. */ @@ -1176,9 +1101,11 @@ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { /* Zero digits above the last digit of the modulus. */ - zero_count := (bits / _DIGIT_BITS) + 0 if (bits % _DIGIT_BITS == 0) else 1; + zero_count := (bits / _DIGIT_BITS); + zero_count += 0 if (bits % _DIGIT_BITS == 0) else 1; + /* - Zero remainder. + Zero remainder. Special case, can't use `zero_unused`. */ if zero_count > 0 { mem.zero_slice(remainder.digit[zero_count:]); diff --git a/core/math/big/build.bat b/core/math/big/build.bat index b1d2a00ee..22d0d5cee 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -4,7 +4,7 @@ :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed python test.py \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 4777654e1..3f99f858f 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -65,13 +65,19 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - foo := "686885735734829009541949746871140768343076607029752932751182108475420900392874228486622313727012705619148037570309621219533087263900443932890792804879473795673302686046941536636874184361869252299636701671980034458333859202703255467709267777184095435235980845369829397344182319113372092844648570818726316581751114346501124871729572474923695509057166373026411194094493240101036672016770945150422252961487398124677567028263059046193391737576836378376192651849283925197438927999526058932679219572030021792914065825542626400207956134072247020690107136531852625253942429167557531123651471221455967386267137846791963149859804549891438562641323068751514370656287452006867713758971418043865298618635213551059471668293725548570452377976322899027050925842868079489675596835389444833567439058609775325447891875359487104691935576723532407937236505941186660707032433807075470656782452889754501872408562496805517394619388777930253411467941214807849472083814447498068636264021405175653742244368865090604940094889189800007448083930490871954101880815781177612910234741529950538835837693870921008635195545246771593130784786737543736434086434015200264933536294884482218945403958647118802574342840790536176272341586020230110889699633073513016344826709214"; - err := atoi(a, foo, 10); + N :: 12345; + D :: 4; - if err != nil do fmt.printf("atoi returned %v\n", err); - - print("foo: ", a); + set(a, N); + print("a: ", a); + div(b, a, D); + rem, _ := mod(a, D); + print("b: ", b); + fmt.printf("rem: %v\n", rem); + mul(b, b, D); + add(b, b, rem); + print("b: ", b); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index eacd15283..e0e79d6f4 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -56,7 +56,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : dest.used += 1; src >>= _DIGIT_BITS; } - _zero_unused(dest); + zero_unused(dest); return nil; } @@ -94,7 +94,7 @@ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.alloca dest.sign = src.sign; dest.flags = src.flags &~ {.Immutable}; - _zero_unused(dest); + zero_unused(dest); return nil; } copy :: proc { int_copy, }; @@ -583,16 +583,11 @@ assert_initialized :: proc(a: ^Int, loc := #caller_location) { assert(is_initialized(a), "`Int` was not properly initialized.", loc); } -_zero_unused :: proc(a: ^Int) { - if a == nil { - return; - } else if !is_initialized(a) { - return; - } +zero_unused :: proc(dest: ^Int, old_used := -1) { + if dest == nil { return; } + if ! #force_inline is_initialized(dest) { return; } - if a.used < len(a.digit) { - mem.zero_slice(a.digit[a.used:]); - } + internal_zero_unused(dest, old_used); } clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) { diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index ed92b9f57..8a77786f4 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -96,13 +96,11 @@ internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocat 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]); - } + internal_zero_unused(dest, old_used); /* Adjust dest.used based on leading zeroes. */ @@ -237,13 +235,11 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ 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]); - } + internal_zero_unused(dest, old_used); + /* Adjust dest.used based on leading zeroes. */ @@ -307,13 +303,11 @@ internal_int_sub_unsigned :: proc(dest, number, decrease: ^Int, allocator := con 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]); - } + internal_zero_unused(dest, old_used); + /* Adjust dest.used based on leading zeroes. */ @@ -430,13 +424,11 @@ internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT) -> (err: Error) } } - zero_count := old_used - dest.used; /* Zero remainder. */ - if zero_count > 0 { - mem.zero_slice(dest.digit[dest.used:][:zero_count]); - } + internal_zero_unused(dest, old_used); + /* Adjust dest.used based on leading zeroes. */ @@ -472,13 +464,11 @@ internal_int_shr1 :: proc(dest, src: ^Int) -> (err: Error) { fwd_carry = carry; } - zero_count := old_used - dest.used; /* Zero remainder. */ - if zero_count > 0 { - mem.zero_slice(dest.digit[dest.used:][:zero_count]); - } + internal_zero_unused(dest, old_used); + /* Adjust dest.used based on leading zeroes. */ @@ -522,6 +512,8 @@ internal_int_shl1 :: proc(dest, src: ^Int) -> (err: Error) { Multiply by a DIGIT. */ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) { + assert(dest != nil && src != nil); + if multiplier == 0 { return zero(dest); } @@ -581,16 +573,13 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := Store final carry [if any] and increment used. */ dest.digit[ix] = DIGIT(carry); - dest.used = src.used + 1; + /* - Zero unused digits. + Zero remainder. */ - //_zero_unused(dest); - zero_count := old_used - dest.used; - if zero_count > 0 { - mem.zero_slice(dest.digit[dest.used:][:zero_count]); - } + internal_zero_unused(dest, old_used); + return clamp(dest); } @@ -702,7 +691,93 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a } return; } -internal_divmod :: proc { internal_int_divmod, }; + +/* + Single digit division (based on routine from MPI). + The quotient is optional and may be passed a nil. +*/ +internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { + /* + Cannot divide by zero. + */ + if denominator == 0 { return 0, .Division_by_Zero; } + + /* + Quick outs. + */ + if denominator == 1 || numerator.used == 0 { + if quotient != nil { + return 0, copy(quotient, numerator); + } + return 0, err; + } + /* + Power of two? + */ + if denominator == 2 { + if numerator.used > 0 && numerator.digit[0] & 1 != 0 { + // Remainder is 1 if numerator is odd. + remainder = 1; + } + if quotient == nil { + return remainder, nil; + } + return remainder, shr(quotient, numerator, 1); + } + + ix: int; + if is_power_of_two(int(denominator)) { + ix = 1; + for ix < _DIGIT_BITS && denominator != (1 << uint(ix)) { + ix += 1; + } + remainder = numerator.digit[0] & ((1 << uint(ix)) - 1); + if quotient == nil { + return remainder, nil; + } + + return remainder, shr(quotient, numerator, int(ix)); + } + + /* + Three? + */ + if denominator == 3 { + return _int_div_3(quotient, numerator); + } + + /* + No easy answer [c'est la vie]. Just division. + */ + q := &Int{}; + + if err = grow(q, numerator.used); err != nil { return 0, err; } + + q.used = numerator.used; + q.sign = numerator.sign; + + w := _WORD(0); + + for ix = numerator.used - 1; ix >= 0; ix -= 1 { + t := DIGIT(0); + w = (w << _WORD(_DIGIT_BITS) | _WORD(numerator.digit[ix])); + if w >= _WORD(denominator) { + t = DIGIT(w / _WORD(denominator)); + w -= _WORD(t) * _WORD(denominator); + } + q.digit[ix] = t; + } + remainder = DIGIT(w); + + if quotient != nil { + clamp(q); + swap(q, quotient); + } + destroy(q); + return remainder, nil; +} + +internal_divmod :: proc { internal_int_divmod, internal_int_divmod_digit, }; /* Asssumes quotient, numerator and denominator to have been initialized and not to be nil. @@ -726,4 +801,27 @@ internal_int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error return #force_inline internal_add(remainder, remainder, numerator); } -internal_mod :: proc{ internal_int_mod, }; \ No newline at end of file +internal_mod :: proc{ internal_int_mod, }; + + + +internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { + /* + If we don't pass the number of previously used DIGITs, we zero all remaining ones. + */ + zero_count: int; + if old_used == -1 { + zero_count = len(dest.digit) - dest.used; + } else { + zero_count = old_used - dest.used; + } + + /* + Zero remainder. + */ + if zero_count > 0 && dest.used < len(dest.digit) { + mem.zero_slice(dest.digit[dest.used:][:zero_count]); + } +} + +internal_zero_unused :: proc { internal_int_zero_unused, }; \ No newline at end of file diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 3f3d9d6fe..bd6bcfc6e 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -323,7 +323,7 @@ int_shr_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { quotient.digit[x] = quotient.digit[x + digits]; } quotient.used -= digits; - _zero_unused(quotient); + zero_unused(quotient); return clamp(quotient); } shr_digit :: proc { int_shr_digit, }; diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index dadc5fdc8..eb1bb2dfb 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -452,7 +452,7 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false remainder: DIGIT; for { - if remainder, err = _int_div_digit(temp, temp, DIGIT(radix)); err != nil { + if remainder, err = #force_inline internal_divmod(temp, temp, DIGIT(radix)); err != nil { destroy(temp, denominator); return len(buffer) - available, err; } diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin index 1088b3c2b..064cb18ea 100644 --- a/core/math/big/tune.odin +++ b/core/math/big/tune.odin @@ -43,7 +43,13 @@ print_timings :: proc() { } } - fmt.println("\nTimings:"); + for v in Timings { + if v.count > 0 { + fmt.println("Timings:"); + break; + } + } + for v, i in Timings { if v.count > 0 { avg_ticks := time.Duration(f64(v.ticks) / f64(v.count)); From 9321616c8052f4127d27cc582e1db08e49d9b99e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 6 Aug 2021 21:22:51 +0200 Subject: [PATCH 083/105] big: Split more into public and internal. --- core/math/big/basic.odin | 172 +++++--------------------------- core/math/big/build.bat | 5 +- core/math/big/example.odin | 29 +++--- core/math/big/internal.odin | 189 +++++++++++++++++++++++++++++++++++- core/math/big/test.odin | 2 +- core/math/big/test.py | 6 +- core/math/big/tune.odin | 2 +- 7 files changed, 236 insertions(+), 169 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 831488a66..b3dd941ac 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -12,7 +12,6 @@ package big */ import "core:mem" -import "core:intrinsics" /* =========================== @@ -140,9 +139,7 @@ int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> mul :: proc { int_mul, int_mul_digit, }; -sqr :: proc(dest, src: ^Int) -> (err: Error) { - return mul(dest, src, src); -} +sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src); } /* divmod. @@ -205,8 +202,10 @@ mod :: proc { int_mod, int_mod_digit, }; remainder = (number + addend) % modulus. */ int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { - if err = add(remainder, number, addend); err != nil { return err; } - return mod(remainder, remainder, modulus); + if remainder == nil { return .Invalid_Pointer; }; + if err = clear_if_uninitialized(number, addend, modulus); err != nil { return err; } + + return #force_inline internal_addmod(remainder, number, addend, modulus); } addmod :: proc { int_addmod, }; @@ -214,8 +213,10 @@ addmod :: proc { int_addmod, }; remainder = (number - decrease) % modulus. */ int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { - if err = add(remainder, number, decrease); err != nil { return err; } - return mod(remainder, remainder, modulus); + if remainder == nil { return .Invalid_Pointer; }; + if err = clear_if_uninitialized(number, decrease, modulus); err != nil { return err; } + + return #force_inline internal_submod(remainder, number, decrease, modulus); } submod :: proc { int_submod, }; @@ -223,8 +224,10 @@ submod :: proc { int_submod, }; remainder = (number * multiplicand) % modulus. */ int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { - if err = mul(remainder, number, multiplicand); err != nil { return err; } - return mod(remainder, remainder, modulus); + if remainder == nil { return .Invalid_Pointer; }; + if err = clear_if_uninitialized(number, multiplicand, modulus); err != nil { return err; } + + return #force_inline internal_mulmod(remainder, number, multiplicand, modulus); } mulmod :: proc { int_mulmod, }; @@ -232,89 +235,23 @@ mulmod :: proc { int_mulmod, }; remainder = (number * number) % modulus. */ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { - if err = sqr(remainder, number); err != nil { return err; } - return mod(remainder, remainder, modulus); + if remainder == nil { return .Invalid_Pointer; }; + if err = clear_if_uninitialized(number, modulus); err != nil { return err; } + + return #force_inline internal_sqrmod(remainder, number, modulus); } sqrmod :: proc { int_sqrmod, }; -/* - TODO: Use Sterling's Approximation to estimate log2(N!) to size the result. - This way we'll have to reallocate less, possibly not at all. -*/ -int_factorial :: proc(res: ^Int, n: DIGIT) -> (err: Error) { - if n < 0 || n > _FACTORIAL_MAX_N || res == nil { return .Invalid_Argument; } +int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { + if n < 0 || n > _FACTORIAL_MAX_N { return .Invalid_Argument; } + if res == nil { return .Invalid_Pointer; } - i := DIGIT(len(_factorial_table)); - if n < i { - return set(res, _factorial_table[n]); - } - if n >= _FACTORIAL_BINARY_SPLIT_CUTOFF { - return int_factorial_binary_split(res, n); - } - - if err = set(res, _factorial_table[i - 1]); err != nil { return err; } - for { - if err = mul(res, res, DIGIT(i)); err != nil || i == n { return err; } - i += 1; - } - - return nil; + return #force_inline internal_int_factorial(res, n); } - -_int_recursive_product :: proc(res: ^Int, start, stop: DIGIT, level := int(0)) -> (err: Error) { - t1, t2 := &Int{}, &Int{}; - defer destroy(t1, t2); - - if level > _FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } - - num_factors := (stop - start) >> 1; - if num_factors == 2 { - if err = set(t1, start); err != nil { return err; } - if err = add(t2, t1, 2); err != nil { return err; } - return mul(res, t1, t2); - } - - if num_factors > 1 { - mid := (start + num_factors) | 1; - if err = _int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } - if err = _int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } - return mul(res, t1, t2); - } - - if num_factors == 1 { return set(res, start); } - - return set(res, 1); -} - -/* - Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html -*/ -int_factorial_binary_split :: proc(res: ^Int, n: DIGIT) -> (err: Error) { - if n < 0 || n > _FACTORIAL_MAX_N || res == nil { return .Invalid_Argument; } - - inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(inner, outer, start, stop, temp); - - if err = set(inner, 1); err != nil { return err; } - if err = set(outer, 1); err != nil { return err; } - - bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); - - for i := bits_used; i >= 0; i -= 1 { - start := (n >> (uint(i) + 1)) + 1 | 1; - stop := (n >> uint(i)) + 1 | 1; - if err = _int_recursive_product(temp, start, stop); err != nil { return err; } - if err = mul(inner, inner, temp); err != nil { return err; } - if err = mul(outer, outer, inner); err != nil { return err; } - } - shift := n - intrinsics.count_ones(n); - - return shl(res, outer, int(shift)); -} - factorial :: proc { int_factorial, }; + /* Number of ways to choose `k` items from `n` items. Also known as the binomial coefficient. @@ -330,7 +267,7 @@ factorial :: proc { int_factorial, }; k, start from previous result */ -int_choose_digit :: proc(res: ^Int, n, k: DIGIT) -> (err: Error) { +int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { if res == nil { return .Invalid_Pointer; } if err = clear_if_uninitialized(res); err != nil { return err; } @@ -1120,66 +1057,3 @@ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { mod_bits :: proc { int_mod_bits, }; -when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { - _factorial_table := [35]_WORD{ -/* f(00): */ 1, -/* f(01): */ 1, -/* f(02): */ 2, -/* f(03): */ 6, -/* f(04): */ 24, -/* f(05): */ 120, -/* f(06): */ 720, -/* f(07): */ 5_040, -/* f(08): */ 40_320, -/* f(09): */ 362_880, -/* f(10): */ 3_628_800, -/* f(11): */ 39_916_800, -/* f(12): */ 479_001_600, -/* f(13): */ 6_227_020_800, -/* f(14): */ 87_178_291_200, -/* f(15): */ 1_307_674_368_000, -/* f(16): */ 20_922_789_888_000, -/* f(17): */ 355_687_428_096_000, -/* f(18): */ 6_402_373_705_728_000, -/* f(19): */ 121_645_100_408_832_000, -/* f(20): */ 2_432_902_008_176_640_000, -/* f(21): */ 51_090_942_171_709_440_000, -/* f(22): */ 1_124_000_727_777_607_680_000, -/* f(23): */ 25_852_016_738_884_976_640_000, -/* f(24): */ 620_448_401_733_239_439_360_000, -/* f(25): */ 15_511_210_043_330_985_984_000_000, -/* f(26): */ 403_291_461_126_605_635_584_000_000, -/* f(27): */ 10_888_869_450_418_352_160_768_000_000, -/* f(28): */ 304_888_344_611_713_860_501_504_000_000, -/* f(29): */ 8_841_761_993_739_701_954_543_616_000_000, -/* f(30): */ 265_252_859_812_191_058_636_308_480_000_000, -/* f(31): */ 8_222_838_654_177_922_817_725_562_880_000_000, -/* f(32): */ 263_130_836_933_693_530_167_218_012_160_000_000, -/* f(33): */ 8_683_317_618_811_886_495_518_194_401_280_000_000, -/* f(34): */ 295_232_799_039_604_140_847_618_609_643_520_000_000, - }; -} else { - _factorial_table := [21]_WORD{ -/* f(00): */ 1, -/* f(01): */ 1, -/* f(02): */ 2, -/* f(03): */ 6, -/* f(04): */ 24, -/* f(05): */ 120, -/* f(06): */ 720, -/* f(07): */ 5_040, -/* f(08): */ 40_320, -/* f(09): */ 362_880, -/* f(10): */ 3_628_800, -/* f(11): */ 39_916_800, -/* f(12): */ 479_001_600, -/* f(13): */ 6_227_020_800, -/* f(14): */ 87_178_291_200, -/* f(15): */ 1_307_674_368_000, -/* f(16): */ 20_922_789_888_000, -/* f(17): */ 355_687_428_096_000, -/* f(18): */ 6_402_373_705_728_000, -/* f(19): */ 121_645_100_408_832_000, -/* f(20): */ 2_432_902_008_176_640_000, - }; -}; \ No newline at end of file diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 22d0d5cee..891a55b21 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,9 @@ @echo off -:odin run . -vet -: -o:size -no-bounds-check +odin run . -vet -o:size :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size :odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -python test.py \ No newline at end of file +:python test.py \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 3f99f858f..60e06837d 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -65,19 +65,26 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - N :: 12345; - D :: 4; + N := 10_000; - set(a, N); - print("a: ", a); - div(b, a, D); - rem, _ := mod(a, D); - print("b: ", b); - fmt.printf("rem: %v\n", rem); + FACTORIAL_10_000_FIRST_100 :: "46AB3AE48966202D0FDE097BFA88FADC512AE8AFC0EA1D1D376A4109F10105E9E21F1E907151E85F926B8D82737B9030D572"; - mul(b, b, D); - add(b, b, rem); - print("b: ", b); + for _ in 0..10 + { + SCOPED_TIMING(.factorial); + factorial(a, N); + } + + as, _ := itoa(a, 16); + defer delete(as); + + fmt.printf("factorial(%v): %v (first 50 hex digits)\n", N, as[:50]); + + if as[:100] == FACTORIAL_10_000_FIRST_100 { + fmt.println("\nCorrect!"); + } else { + fmt.printf("\nWrong. Expected: %v\n", FACTORIAL_10_000_FIRST_100); + } } main :: proc() { diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 8a77786f4..49a90b8e0 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -28,6 +28,7 @@ package big */ import "core:mem" +import "core:intrinsics" /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -419,7 +420,7 @@ internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT) -> (err: Error) #no_bounds_check for i := 0; i < number.used; i += 1 { dest.digit[i] = number.digit[i] - carry; - carry := dest.digit[i] >> (_DIGIT_TYPE_BITS - 1); + carry = dest.digit[i] >> (_DIGIT_TYPE_BITS - 1); dest.digit[i] &= _MASK; } } @@ -803,6 +804,122 @@ internal_int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error } internal_mod :: proc{ internal_int_mod, }; +/* + remainder = (number + addend) % modulus. +*/ +internal_int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { + if err = #force_inline internal_add(remainder, number, addend); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus); +} +internal_addmod :: proc { internal_int_addmod, }; + +/* + remainder = (number - decrease) % modulus. +*/ +internal_int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { + if err = #force_inline internal_sub(remainder, number, decrease); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus); +} +internal_submod :: proc { internal_int_submod, }; + +/* + remainder = (number * multiplicand) % modulus. +*/ +internal_int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { + if err = #force_inline internal_mul(remainder, number, multiplicand); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus); +} +internal_mulmod :: proc { internal_int_mulmod, }; + +/* + remainder = (number * number) % modulus. +*/ +internal_int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { + if err = #force_inline internal_mul(remainder, number, number); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus); +} +internal_sqrmod :: proc { internal_int_sqrmod, }; + + + +/* + TODO: Use Sterling's Approximation to estimate log2(N!) to size the result. + This way we'll have to reallocate less, possibly not at all. +*/ +internal_int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { + if n >= _FACTORIAL_BINARY_SPLIT_CUTOFF { + return #force_inline _int_factorial_binary_split(res, n); + } + + i := len(_factorial_table); + if n < i { + return #force_inline set(res, _factorial_table[n]); + } + + if err = #force_inline set(res, _factorial_table[i - 1]); err != nil { return err; } + for { + if err = #force_inline internal_mul(res, res, DIGIT(i)); err != nil || i == n { return err; } + i += 1; + } + + return nil; +} + +_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int(0)) -> (err: Error) { + t1, t2 := &Int{}, &Int{}; + defer destroy(t1, t2); + + if level > _FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } + + num_factors := (stop - start) >> 1; + if num_factors == 2 { + if err = set(t1, start); err != nil { return err; } + when true { + if err = grow(t2, t1.used + 1); err != nil { return err; } + if err = internal_add(t2, t1, 2); err != nil { return err; } + } else { + if err = add(t2, t1, 2); err != nil { return err; } + } + return internal_mul(res, t1, t2); + } + + if num_factors > 1 { + mid := (start + num_factors) | 1; + if err = _int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } + if err = _int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } + return internal_mul(res, t1, t2); + } + + if num_factors == 1 { return #force_inline set(res, start); } + + return #force_inline set(res, 1); +} + +/* + Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html +*/ +_int_factorial_binary_split :: proc(res: ^Int, n: int) -> (err: Error) { + + inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(inner, outer, start, stop, temp); + + if err = set(inner, 1); err != nil { return err; } + if err = set(outer, 1); err != nil { return err; } + + bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); + + for i := bits_used; i >= 0; i -= 1 { + start := (n >> (uint(i) + 1)) + 1 | 1; + stop := (n >> uint(i)) + 1 | 1; + if err = _int_recursive_product(temp, start, stop); err != nil { return err; } + if err = internal_mul(inner, inner, temp); err != nil { return err; } + if err = internal_mul(outer, outer, inner); err != nil { return err; } + } + shift := n - intrinsics.count_ones(n); + + return shl(res, outer, int(shift)); +} + internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { @@ -824,4 +941,72 @@ internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { } } -internal_zero_unused :: proc { internal_int_zero_unused, }; \ No newline at end of file +internal_zero_unused :: proc { internal_int_zero_unused, }; + +/* + Tables. +*/ + +when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { + _factorial_table := [35]_WORD{ +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, +/* f(21): */ 51_090_942_171_709_440_000, +/* f(22): */ 1_124_000_727_777_607_680_000, +/* f(23): */ 25_852_016_738_884_976_640_000, +/* f(24): */ 620_448_401_733_239_439_360_000, +/* f(25): */ 15_511_210_043_330_985_984_000_000, +/* f(26): */ 403_291_461_126_605_635_584_000_000, +/* f(27): */ 10_888_869_450_418_352_160_768_000_000, +/* f(28): */ 304_888_344_611_713_860_501_504_000_000, +/* f(29): */ 8_841_761_993_739_701_954_543_616_000_000, +/* f(30): */ 265_252_859_812_191_058_636_308_480_000_000, +/* f(31): */ 8_222_838_654_177_922_817_725_562_880_000_000, +/* f(32): */ 263_130_836_933_693_530_167_218_012_160_000_000, +/* f(33): */ 8_683_317_618_811_886_495_518_194_401_280_000_000, +/* f(34): */ 295_232_799_039_604_140_847_618_609_643_520_000_000, + }; +} else { + _factorial_table := [21]_WORD{ +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, + }; +}; \ No newline at end of file diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 744a4436a..de536f521 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -296,7 +296,7 @@ PyRes :: struct { /* dest = factorial(n) */ -@export test_factorial :: proc "c" (n: DIGIT) -> (res: PyRes) { +@export test_factorial :: proc "c" (n: int) -> (res: PyRes) { context = runtime.default_context(); err: Error; diff --git a/core/math/big/test.py b/core/math/big/test.py index 6dcb235de..e8cc71152 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -17,7 +17,7 @@ EXIT_ON_FAIL = False # We skip randomized tests altogether if NO_RANDOM_TESTS is set. # NO_RANDOM_TESTS = True -NO_RANDOM_TESTS = False +#NO_RANDOM_TESTS = False # # If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. @@ -444,7 +444,9 @@ TESTS = { [ 149195686190273039203651143129455, 12 ], ], test_factorial: [ - [ 12_345 ], + [ 6_000 ], # Regular factorial, see cutoff in common.odin. + [ 12_345 ], # Binary split factorial + [ 100_000 ], ], test_gcd: [ [ 23, 25, ], diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin index 064cb18ea..fbd454002 100644 --- a/core/math/big/tune.odin +++ b/core/math/big/tune.odin @@ -45,7 +45,7 @@ print_timings :: proc() { for v in Timings { if v.count > 0 { - fmt.println("Timings:"); + fmt.println("\nTimings:"); break; } } From 629822623803b4ccac41b2b3830779ed1b52781a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 6 Aug 2021 22:58:39 +0200 Subject: [PATCH 084/105] big: Switch `choose` over to internal implementations. --- core/math/big/basic.odin | 13 +++++++------ core/math/big/build.bat | 3 ++- core/math/big/example.odin | 24 +++++++----------------- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index b3dd941ac..ecd861001 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -269,7 +269,7 @@ factorial :: proc { int_factorial, }; */ int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { if res == nil { return .Invalid_Pointer; } - if err = clear_if_uninitialized(res); err != nil { return err; } + if n < 0 || n > _FACTORIAL_MAX_N { return .Invalid_Argument; } if k > n { return zero(res); } @@ -279,12 +279,12 @@ int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { n_fac, k_fac, n_minus_k_fac := &Int{}, &Int{}, &Int{}; defer destroy(n_fac, k_fac, n_minus_k_fac); - if err = factorial(n_minus_k_fac, n - k); err != nil { return err; } - if err = factorial(k_fac, k); err != nil { return err; } - if err = mul(k_fac, k_fac, n_minus_k_fac); err != nil { return err; } + if err = #force_inline internal_int_factorial(n_minus_k_fac, n - k); err != nil { return err; } + if err = #force_inline internal_int_factorial(k_fac, k); err != nil { return err; } + if err = #force_inline internal_mul(k_fac, k_fac, n_minus_k_fac); err != nil { return err; } - if err = factorial(n_fac, n); err != nil { return err; } - if err = div(res, n_fac, k_fac); err != nil { return err; } + if err = #force_inline internal_int_factorial(n_fac, n); err != nil { return err; } + if err = #force_inline internal_div(res, n_fac, k_fac); err != nil { return err; } return err; } @@ -323,6 +323,7 @@ _int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { pb := min(b.used, digits - ix); carry := _WORD(0); iy := 0; + /* Compute the column of the output and propagate the carry. */ diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 891a55b21..1921803b5 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,5 +1,6 @@ @echo off -odin run . -vet -o:size +odin run . -vet +: -o:size :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 60e06837d..73b922993 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -65,26 +65,16 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - N := 10_000; + n := 50_000; + k := 3; - FACTORIAL_10_000_FIRST_100 :: "46AB3AE48966202D0FDE097BFA88FADC512AE8AFC0EA1D1D376A4109F10105E9E21F1E907151E85F926B8D82737B9030D572"; - - for _ in 0..10 { - SCOPED_TIMING(.factorial); - factorial(a, N); - } - - as, _ := itoa(a, 16); - defer delete(as); - - fmt.printf("factorial(%v): %v (first 50 hex digits)\n", N, as[:50]); - - if as[:100] == FACTORIAL_10_000_FIRST_100 { - fmt.println("\nCorrect!"); - } else { - fmt.printf("\nWrong. Expected: %v\n", FACTORIAL_10_000_FIRST_100); + SCOPED_TIMING(.choose); + choose(a, n, k); } + + fmt.printf("%v choose %v ", n, k); + print("= ", a); } main :: proc() { From e288a563e1cbe4432b457c6891bf86abfec27412 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 7 Aug 2021 15:27:27 +0200 Subject: [PATCH 085/105] big: Move `_mul` private functions. --- core/math/big/basic.odin | 162 ------------------- core/math/big/example.odin | 2 +- core/math/big/internal.odin | 312 +++++++++++++++++++++++++++++------- core/math/big/test.py | 10 +- 4 files changed, 257 insertions(+), 229 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index ecd861001..8afcd59f0 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -290,168 +290,6 @@ int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { } choose :: proc { int_choose_digit, }; -/* - Multiplies |a| * |b| and only computes upto digs digits of result. - HAC pp. 595, Algorithm 14.12 Modified so you can control how - many digits of output are created. -*/ -_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { - /* - Can we use the fast multiplier? - */ - if digits < _WARRAY && min(a.used, b.used) < _MAX_COMBA { - return _int_mul_comba(dest, a, b, digits); - } - - /* - Set up temporary output `Int`, which we'll swap for `dest` when done. - */ - - t := &Int{}; - - if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } - t.used = digits; - - /* - Compute the digits of the product directly. - */ - pa := a.used; - for ix := 0; ix < pa; ix += 1 { - /* - Limit ourselves to `digits` DIGITs of output. - */ - pb := min(b.used, digits - ix); - carry := _WORD(0); - iy := 0; - - /* - Compute the column of the output and propagate the carry. - */ - #no_bounds_check for iy = 0; iy < pb; iy += 1 { - /* - Compute the column as a _WORD. - */ - column := _WORD(t.digit[ix + iy]) + _WORD(a.digit[ix]) * _WORD(b.digit[iy]) + carry; - - /* - The new column is the lower part of the result. - */ - t.digit[ix + iy] = DIGIT(column & _WORD(_MASK)); - - /* - Get the carry word from the result. - */ - carry = column >> _DIGIT_BITS; - } - /* - Set carry if it is placed below digits - */ - if ix + iy < digits { - t.digit[ix + pb] = DIGIT(carry); - } - } - - swap(dest, t); - destroy(t); - return clamp(dest); -} - -/* - Fast (comba) multiplier - - This is the fast column-array [comba] multiplier. It is - designed to compute the columns of the product first - then handle the carries afterwards. This has the effect - of making the nested loops that compute the columns very - simple and schedulable on super-scalar processors. - - This has been modified to produce a variable number of - digits of output so if say only a half-product is required - you don't have to compute the upper half (a feature - required for fast Barrett reduction). - - Based on Algorithm 14.12 on pp.595 of HAC. -*/ -_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { - /* - Set up array. - */ - W: [_WARRAY]DIGIT = ---; - - /* - Grow the destination as required. - */ - if err = grow(dest, digits); err != nil { return err; } - - /* - Number of output digits to produce. - */ - pa := min(digits, a.used + b.used); - - /* - Clear the carry - */ - _W := _WORD(0); - - ix: int; - for ix = 0; ix < pa; ix += 1 { - tx, ty, iy, iz: int; - - /* - Get offsets into the two bignums. - */ - ty = min(b.used - 1, ix); - tx = ix - ty; - - /* - This is the number of times the loop will iterate, essentially. - while (tx++ < a->used && ty-- >= 0) { ... } - */ - - iy = min(a.used - tx, ty + 1); - - /* - Execute loop. - */ - #no_bounds_check for iz = 0; iz < iy; iz += 1 { - _W += _WORD(a.digit[tx + iz]) * _WORD(b.digit[ty - iz]); - } - - /* - Store term. - */ - W[ix] = DIGIT(_W) & _MASK; - - /* - Make next carry. - */ - _W = _W >> _WORD(_DIGIT_BITS); - } - - /* - Setup dest. - */ - old_used := dest.used; - dest.used = pa; - - /* - Now extract the previous digit [below the carry]. - */ - // for ix = 0; ix < pa; ix += 1 { dest.digit[ix] = W[ix]; } - - copy_slice(dest.digit[0:], W[:pa]); - - /* - Clear unused digits [that existed in the old copy of dest]. - */ - zero_unused(dest, old_used); - - /* - Adjust dest.used based on leading zeroes. - */ - - return clamp(dest); -} /* Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 73b922993..3e4a27b8c 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -65,7 +65,7 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - n := 50_000; + n := 1_024; k := 3; { diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 49a90b8e0..8505f6f31 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -643,9 +643,9 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ - err = _int_mul_comba(dest, src, multiplier, digits); + err = _private_int_mul_comba(dest, src, multiplier, digits); } else { - err = _int_mul(dest, src, multiplier, digits); + err = _private_int_mul(dest, src, multiplier, digits); } } neg := src.sign != multiplier.sign; @@ -848,7 +848,7 @@ internal_sqrmod :: proc { internal_int_sqrmod, }; */ internal_int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { if n >= _FACTORIAL_BINARY_SPLIT_CUTOFF { - return #force_inline _int_factorial_binary_split(res, n); + return #force_inline _private_int_factorial_binary_split(res, n); } i := len(_factorial_table); @@ -865,62 +865,6 @@ internal_int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { return nil; } -_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int(0)) -> (err: Error) { - t1, t2 := &Int{}, &Int{}; - defer destroy(t1, t2); - - if level > _FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } - - num_factors := (stop - start) >> 1; - if num_factors == 2 { - if err = set(t1, start); err != nil { return err; } - when true { - if err = grow(t2, t1.used + 1); err != nil { return err; } - if err = internal_add(t2, t1, 2); err != nil { return err; } - } else { - if err = add(t2, t1, 2); err != nil { return err; } - } - return internal_mul(res, t1, t2); - } - - if num_factors > 1 { - mid := (start + num_factors) | 1; - if err = _int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } - if err = _int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } - return internal_mul(res, t1, t2); - } - - if num_factors == 1 { return #force_inline set(res, start); } - - return #force_inline set(res, 1); -} - -/* - Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html -*/ -_int_factorial_binary_split :: proc(res: ^Int, n: int) -> (err: Error) { - - inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(inner, outer, start, stop, temp); - - if err = set(inner, 1); err != nil { return err; } - if err = set(outer, 1); err != nil { return err; } - - bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); - - for i := bits_used; i >= 0; i -= 1 { - start := (n >> (uint(i) + 1)) + 1 | 1; - stop := (n >> uint(i)) + 1 | 1; - if err = _int_recursive_product(temp, start, stop); err != nil { return err; } - if err = internal_mul(inner, inner, temp); err != nil { return err; } - if err = internal_mul(outer, outer, inner); err != nil { return err; } - } - shift := n - intrinsics.count_ones(n); - - return shl(res, outer, int(shift)); -} - - internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { /* @@ -943,8 +887,250 @@ internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { internal_zero_unused :: proc { internal_int_zero_unused, }; + /* - Tables. + ========================== End of low-level routines ========================== + + ============================= Private procedures ============================= + + Private procedures used by the above low-level routines follow. + + Don't call these yourself unless you really know what you're doing. + They include implementations that are optimimal for certain ranges of input only. + + These aren't exported for the same reasons. +*/ + + +/* + Multiplies |a| * |b| and only computes upto digs digits of result. + HAC pp. 595, Algorithm 14.12 Modified so you can control how + many digits of output are created. +*/ +_private_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { + /* + Can we use the fast multiplier? + */ + if digits < _WARRAY && min(a.used, b.used) < _MAX_COMBA { + return _private_int_mul_comba(dest, a, b, digits); + } + + /* + Set up temporary output `Int`, which we'll swap for `dest` when done. + */ + + t := &Int{}; + + if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } + t.used = digits; + + /* + Compute the digits of the product directly. + */ + pa := a.used; + for ix := 0; ix < pa; ix += 1 { + /* + Limit ourselves to `digits` DIGITs of output. + */ + pb := min(b.used, digits - ix); + carry := _WORD(0); + iy := 0; + + /* + Compute the column of the output and propagate the carry. + */ + #no_bounds_check for iy = 0; iy < pb; iy += 1 { + /* + Compute the column as a _WORD. + */ + column := _WORD(t.digit[ix + iy]) + _WORD(a.digit[ix]) * _WORD(b.digit[iy]) + carry; + + /* + The new column is the lower part of the result. + */ + t.digit[ix + iy] = DIGIT(column & _WORD(_MASK)); + + /* + Get the carry word from the result. + */ + carry = column >> _DIGIT_BITS; + } + /* + Set carry if it is placed below digits + */ + if ix + iy < digits { + t.digit[ix + pb] = DIGIT(carry); + } + } + + swap(dest, t); + destroy(t); + return clamp(dest); +} + +/* + Fast (comba) multiplier + + This is the fast column-array [comba] multiplier. It is + designed to compute the columns of the product first + then handle the carries afterwards. This has the effect + of making the nested loops that compute the columns very + simple and schedulable on super-scalar processors. + + This has been modified to produce a variable number of + digits of output so if say only a half-product is required + you don't have to compute the upper half (a feature + required for fast Barrett reduction). + + Based on Algorithm 14.12 on pp.595 of HAC. +*/ +_private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { + /* + Set up array. + */ + W: [_WARRAY]DIGIT = ---; + + /* + Grow the destination as required. + */ + if err = grow(dest, digits); err != nil { return err; } + + /* + Number of output digits to produce. + */ + pa := min(digits, a.used + b.used); + + /* + Clear the carry + */ + _W := _WORD(0); + + ix: int; + for ix = 0; ix < pa; ix += 1 { + tx, ty, iy, iz: int; + + /* + Get offsets into the two bignums. + */ + ty = min(b.used - 1, ix); + tx = ix - ty; + + /* + This is the number of times the loop will iterate, essentially. + while (tx++ < a->used && ty-- >= 0) { ... } + */ + + iy = min(a.used - tx, ty + 1); + + /* + Execute loop. + */ + #no_bounds_check for iz = 0; iz < iy; iz += 1 { + _W += _WORD(a.digit[tx + iz]) * _WORD(b.digit[ty - iz]); + } + + /* + Store term. + */ + W[ix] = DIGIT(_W) & _MASK; + + /* + Make next carry. + */ + _W = _W >> _WORD(_DIGIT_BITS); + } + + /* + Setup dest. + */ + old_used := dest.used; + dest.used = pa; + + /* + Now extract the previous digit [below the carry]. + */ + // for ix = 0; ix < pa; ix += 1 { dest.digit[ix] = W[ix]; } + + copy_slice(dest.digit[0:], W[:pa]); + + /* + Clear unused digits [that existed in the old copy of dest]. + */ + zero_unused(dest, old_used); + + /* + Adjust dest.used based on leading zeroes. + */ + + return clamp(dest); +} + + + +/* + Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html +*/ +_private_int_factorial_binary_split :: proc(res: ^Int, n: int) -> (err: Error) { + + inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(inner, outer, start, stop, temp); + + if err = set(inner, 1); err != nil { return err; } + if err = set(outer, 1); err != nil { return err; } + + bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); + + for i := bits_used; i >= 0; i -= 1 { + start := (n >> (uint(i) + 1)) + 1 | 1; + stop := (n >> uint(i)) + 1 | 1; + if err = _private_int_recursive_product(temp, start, stop); err != nil { return err; } + if err = internal_mul(inner, inner, temp); err != nil { return err; } + if err = internal_mul(outer, outer, inner); err != nil { return err; } + } + shift := n - intrinsics.count_ones(n); + + return shl(res, outer, int(shift)); +} + +/* + Recursive product used by binary split factorial algorithm. +*/ +_private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int(0)) -> (err: Error) { + t1, t2 := &Int{}, &Int{}; + defer destroy(t1, t2); + + if level > _FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } + + num_factors := (stop - start) >> 1; + if num_factors == 2 { + if err = set(t1, start); err != nil { return err; } + when true { + if err = grow(t2, t1.used + 1); err != nil { return err; } + if err = internal_add(t2, t1, 2); err != nil { return err; } + } else { + if err = add(t2, t1, 2); err != nil { return err; } + } + return internal_mul(res, t1, t2); + } + + if num_factors > 1 { + mid := (start + num_factors) | 1; + if err = _private_int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } + if err = _private_int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } + return internal_mul(res, t1, t2); + } + + if num_factors == 1 { return #force_inline set(res, start); } + + return #force_inline set(res, 1); +} + +/* + ======================== End of private procedures ======================= + + =============================== Private tables =============================== + + Tables used by `internal_*` and `_*`. */ when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { @@ -1009,4 +1195,8 @@ when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { /* f(19): */ 121_645_100_408_832_000, /* f(20): */ 2_432_902_008_176_640_000, }; -}; \ No newline at end of file +}; + +/* + ========================= End of private tables ======================== +*/ \ No newline at end of file diff --git a/core/math/big/test.py b/core/math/big/test.py index e8cc71152..4e7c8a406 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -96,11 +96,11 @@ class Error(Enum): # Set up exported procedures # -try: - l = cdll.LoadLibrary(LIB_PATH) -except: - print("Couldn't find or load " + LIB_PATH + ".") - exit(1) +# try: +l = cdll.LoadLibrary(LIB_PATH) +# except: +# print("Couldn't find or load " + LIB_PATH + ".") +# exit(1) def load(export_name, args, res): export_name.argtypes = args From 62dcccd7ef21ba4c0049f2051bf1cd15433fef8d Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 7 Aug 2021 16:52:04 +0200 Subject: [PATCH 086/105] big: Move division internals. --- core/math/big/basic.odin | 345 ---------------------------------- core/math/big/build.bat | 6 +- core/math/big/internal.odin | 357 +++++++++++++++++++++++++++++++++++- core/math/big/test.py | 12 +- 4 files changed, 358 insertions(+), 362 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 8afcd59f0..b6c49a5fd 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -290,351 +290,6 @@ int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { } choose :: proc { int_choose_digit, }; - -/* - Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 -*/ -_int_sqr :: proc(dest, src: ^Int) -> (err: Error) { - pa := src.used; - - t := &Int{}; ix, iy: int; - /* - Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. - */ - if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } - t.used = (2 * pa) + 1; - - #no_bounds_check for ix = 0; ix < pa; ix += 1 { - carry := DIGIT(0); - /* - First calculate the digit at 2*ix; calculate double precision result. - */ - r := _WORD(t.digit[ix+ix]) + (_WORD(src.digit[ix]) * _WORD(src.digit[ix])); - - /* - Store lower part in result. - */ - t.digit[ix+ix] = DIGIT(r & _WORD(_MASK)); - /* - Get the carry. - */ - carry = DIGIT(r >> _DIGIT_BITS); - - #no_bounds_check for iy = ix + 1; iy < pa; iy += 1 { - /* - First calculate the product. - */ - r = _WORD(src.digit[ix]) * _WORD(src.digit[iy]); - - /* Now calculate the double precision result. Nóte we use - * addition instead of *2 since it's easier to optimize - */ - r = _WORD(t.digit[ix+iy]) + r + r + _WORD(carry); - - /* - Store lower part. - */ - t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); - - /* - Get carry. - */ - carry = DIGIT(r >> _DIGIT_BITS); - } - /* - Propagate upwards. - */ - #no_bounds_check for carry != 0 { - r = _WORD(t.digit[ix+iy]) + _WORD(carry); - t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); - carry = DIGIT(r >> _WORD(_DIGIT_BITS)); - iy += 1; - } - } - - err = clamp(t); - swap(dest, t); - destroy(t); - return err; -} - -/* - Divide by three (based on routine from MPI and the GMP manual). -*/ -_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: Error) { - /* - b = 2**MP_DIGIT_BIT / 3 - */ - b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); - - q := &Int{}; - if err = grow(q, numerator.used); err != nil { return 0, err; } - q.used = numerator.used; - q.sign = numerator.sign; - - w, t: _WORD; - for ix := numerator.used; ix >= 0; ix -= 1 { - w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix]); - if w >= 3 { - /* - Multiply w by [1/3]. - */ - t = (w * b) >> _WORD(_DIGIT_BITS); - - /* - Now subtract 3 * [w/3] from w, to get the remainder. - */ - w -= t+t+t; - - /* - Fixup the remainder as required since the optimization is not exact. - */ - for w >= 3 { - t += 1; - w -= 3; - } - } else { - t = 0; - } - q.digit[ix] = DIGIT(t); - } - remainder = DIGIT(w); - - /* - [optional] store the quotient. - */ - if quotient != nil { - err = clamp(q); - swap(q, quotient); - } - destroy(q); - return remainder, nil; -} - -/* - Signed Integer Division - - c*b + d == a [i.e. a/b, c=quotient, d=remainder], HAC pp.598 Algorithm 14.20 - - Note that the description in HAC is horribly incomplete. - For example, it doesn't consider the case where digits are removed from 'x' in - the inner loop. - - It also doesn't consider the case that y has fewer than three digits, etc. - The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. -*/ -_int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { - if err = error_if_immutable(quotient, remainder); err != nil { return err; } - if err = clear_if_uninitialized(quotient, numerator, denominator); err != nil { return err; } - - q, x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(q, x, y, t1, t2); - - if err = grow(q, numerator.used + 2); err != nil { return err; } - q.used = numerator.used + 2; - - if err = init_multi(t1, t2); err != nil { return err; } - if err = copy(x, numerator); err != nil { return err; } - if err = copy(y, denominator); err != nil { return err; } - - /* - Fix the sign. - */ - neg := numerator.sign != denominator.sign; - x.sign = .Zero_or_Positive; - y.sign = .Zero_or_Positive; - - /* - Normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] - */ - norm, _ := count_bits(y); - norm %= _DIGIT_BITS; - - if norm < _DIGIT_BITS - 1 { - norm = (_DIGIT_BITS - 1) - norm; - if err = shl(x, x, norm); err != nil { return err; } - if err = shl(y, y, norm); err != nil { return err; } - } else { - norm = 0; - } - - /* - Note: HAC does 0 based, so if used==5 then it's 0,1,2,3,4, i.e. use 4 - */ - n := x.used - 1; - t := y.used - 1; - - /* - while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } - y = y*b**{n-t} - */ - - if err = shl_digit(y, n - t); err != nil { return err; } - - c, _ := cmp(x, y); - for c != -1 { - q.digit[n - t] += 1; - if err = sub(x, x, y); err != nil { return err; } - c, _ = cmp(x, y); - } - - /* - Reset y by shifting it back down. - */ - shr_digit(y, n - t); - - /* - Step 3. for i from n down to (t + 1). - */ - for i := n; i >= (t + 1); i -= 1 { - if (i > x.used) { continue; } - - /* - step 3.1 if xi == yt then set q{i-t-1} to b-1, otherwise set q{i-t-1} to (xi*b + x{i-1})/yt - */ - if x.digit[i] == y.digit[t] { - q.digit[(i - t) - 1] = 1 << (_DIGIT_BITS - 1); - } else { - - tmp := _WORD(x.digit[i]) << _DIGIT_BITS; - tmp |= _WORD(x.digit[i - 1]); - tmp /= _WORD(y.digit[t]); - if tmp > _WORD(_MASK) { - tmp = _WORD(_MASK); - } - q.digit[(i - t) - 1] = DIGIT(tmp & _WORD(_MASK)); - } - - /* while (q{i-t-1} * (yt * b + y{t-1})) > - xi * b**2 + xi-1 * b + xi-2 - - do q{i-t-1} -= 1; - */ - - iter := 0; - - q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] + 1) & _MASK; - for { - q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; - - /* - Find left hand. - */ - zero(t1); - t1.digit[0] = ((t - 1) < 0) ? 0 : y.digit[t - 1]; - t1.digit[1] = y.digit[t]; - t1.used = 2; - if err = mul(t1, t1, q.digit[(i - t) - 1]); err != nil { return err; } - - /* - Find right hand. - */ - t2.digit[0] = ((i - 2) < 0) ? 0 : x.digit[i - 2]; - t2.digit[1] = x.digit[i - 1]; /* i >= 1 always holds */ - t2.digit[2] = x.digit[i]; - t2.used = 3; - - if t1_t2, _ := cmp_mag(t1, t2); t1_t2 != 1 { - break; - } - iter += 1; if iter > 100 { return .Max_Iterations_Reached; } - } - - /* - Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} - */ - if err = int_mul_digit(t1, y, q.digit[(i - t) - 1]); err != nil { return err; } - if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } - if err = sub(x, x, t1); err != nil { return err; } - - /* - if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } - */ - if x.sign == .Negative { - if err = copy(t1, y); err != nil { return err; } - if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } - if err = add(x, x, t1); err != nil { return err; } - - q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; - } - } - - /* - Now q is the quotient and x is the remainder, [which we have to normalize] - Get sign before writing to c. - */ - z, _ := is_zero(x); - x.sign = .Zero_or_Positive if z else numerator.sign; - - if quotient != nil { - clamp(q); - swap(q, quotient); - quotient.sign = .Negative if neg else .Zero_or_Positive; - } - - if remainder != nil { - if err = shr(x, x, norm); err != nil { return err; } - swap(x, remainder); - } - - return nil; -} - -/* - Slower bit-bang division... also smaller. -*/ -@(deprecated="Use `_int_div_school`, it's 3.5x faster.") -_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { - - ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}; - c: int; - - goto_end: for { - if err = one(tq); err != nil { break goto_end; } - - num_bits, _ := count_bits(numerator); - den_bits, _ := count_bits(denominator); - n := num_bits - den_bits; - - if err = abs(ta, numerator); err != nil { break goto_end; } - if err = abs(tb, denominator); err != nil { break goto_end; } - if err = shl(tb, tb, n); err != nil { break goto_end; } - if err = shl(tq, tq, n); err != nil { break goto_end; } - - for n >= 0 { - if c, _ = cmp_mag(ta, tb); c == 0 || c == 1 { - // ta -= tb - if err = sub(ta, ta, tb); err != nil { break goto_end; } - // q += tq - if err = add( q, q, tq); err != nil { break goto_end; } - } - if err = shr1(tb, tb); err != nil { break goto_end; } - if err = shr1(tq, tq); err != nil { break goto_end; } - - n -= 1; - } - - /* - Now q == quotient and ta == remainder. - */ - neg := numerator.sign != denominator.sign; - if quotient != nil { - swap(quotient, q); - z, _ := is_zero(quotient); - quotient.sign = .Negative if neg && !z else .Zero_or_Positive; - } - if remainder != nil { - swap(remainder, ta); - z, _ := is_zero(numerator); - remainder.sign = .Zero_or_Positive if z else numerator.sign; - } - - break goto_end; - } - destroy(ta, tb, tq, q); - return err; -} - /* Function computing both GCD and (if target isn't `nil`) also LCM. */ diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 1921803b5..a12e3262a 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -odin run . -vet +:odin run . -vet : -o:size :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check +odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -:python test.py \ No newline at end of file +python test.py \ No newline at end of file diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 8505f6f31..a3e2548db 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -608,7 +608,7 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc /* Fast comba? */ // err = s_mp_sqr_comba(a, c); } else { - err = _int_sqr(dest, src); + err = _private_int_sqr(dest, src); } } else { /* @@ -680,14 +680,13 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a // err = _int_div_recursive(quotient, remainder, numerator, denominator); } else { when true { - err = _int_div_school(quotient, remainder, numerator, denominator); + err = _private_int_div_school(quotient, remainder, numerator, denominator); } else { /* - NOTE(Jeroen): We no longer need or use `_int_div_small`. + NOTE(Jeroen): We no longer need or use `_private_int_div_small`. We'll keep it around for a bit until we're reasonably certain div_school is bug free. - err = _int_div_small(quotient, remainder, numerator, denominator); */ - err = _int_div_small(quotient, remainder, numerator, denominator); + err = _private_int_div_small(quotient, remainder, numerator, denominator); } } return; @@ -744,7 +743,7 @@ internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) Three? */ if denominator == 3 { - return _int_div_3(quotient, numerator); + return _private_int_div_3(quotient, numerator); } /* @@ -1049,8 +1048,6 @@ _private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Now extract the previous digit [below the carry]. */ - // for ix = 0; ix < pa; ix += 1 { dest.digit[ix] = W[ix]; } - copy_slice(dest.digit[0:], W[:pa]); /* @@ -1065,6 +1062,350 @@ _private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { return clamp(dest); } +/* + Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 +*/ +_private_int_sqr :: proc(dest, src: ^Int) -> (err: Error) { + pa := src.used; + + t := &Int{}; ix, iy: int; + /* + Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. + */ + if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } + t.used = (2 * pa) + 1; + + #no_bounds_check for ix = 0; ix < pa; ix += 1 { + carry := DIGIT(0); + /* + First calculate the digit at 2*ix; calculate double precision result. + */ + r := _WORD(t.digit[ix+ix]) + (_WORD(src.digit[ix]) * _WORD(src.digit[ix])); + + /* + Store lower part in result. + */ + t.digit[ix+ix] = DIGIT(r & _WORD(_MASK)); + /* + Get the carry. + */ + carry = DIGIT(r >> _DIGIT_BITS); + + #no_bounds_check for iy = ix + 1; iy < pa; iy += 1 { + /* + First calculate the product. + */ + r = _WORD(src.digit[ix]) * _WORD(src.digit[iy]); + + /* Now calculate the double precision result. Nóte we use + * addition instead of *2 since it's easier to optimize + */ + r = _WORD(t.digit[ix+iy]) + r + r + _WORD(carry); + + /* + Store lower part. + */ + t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); + + /* + Get carry. + */ + carry = DIGIT(r >> _DIGIT_BITS); + } + /* + Propagate upwards. + */ + #no_bounds_check for carry != 0 { + r = _WORD(t.digit[ix+iy]) + _WORD(carry); + t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); + carry = DIGIT(r >> _WORD(_DIGIT_BITS)); + iy += 1; + } + } + + err = clamp(t); + swap(dest, t); + destroy(t); + return err; +} + +/* + Divide by three (based on routine from MPI and the GMP manual). +*/ +_private_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: Error) { + /* + b = 2^_DIGIT_BITS / 3 + */ + b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); + + q := &Int{}; + if err = grow(q, numerator.used); err != nil { return 0, err; } + q.used = numerator.used; + q.sign = numerator.sign; + + w, t: _WORD; + #no_bounds_check for ix := numerator.used; ix >= 0; ix -= 1 { + w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix]); + if w >= 3 { + /* + Multiply w by [1/3]. + */ + t = (w * b) >> _WORD(_DIGIT_BITS); + + /* + Now subtract 3 * [w/3] from w, to get the remainder. + */ + w -= t+t+t; + + /* + Fixup the remainder as required since the optimization is not exact. + */ + for w >= 3 { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.digit[ix] = DIGIT(t); + } + remainder = DIGIT(w); + + /* + [optional] store the quotient. + */ + if quotient != nil { + err = clamp(q); + swap(q, quotient); + } + destroy(q); + return remainder, nil; +} + +/* + Signed Integer Division + + c*b + d == a [i.e. a/b, c=quotient, d=remainder], HAC pp.598 Algorithm 14.20 + + Note that the description in HAC is horribly incomplete. + For example, it doesn't consider the case where digits are removed from 'x' in + the inner loop. + + It also doesn't consider the case that y has fewer than three digits, etc. + The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. +*/ +_private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { + // if err = error_if_immutable(quotient, remainder); err != nil { return err; } + // if err = clear_if_uninitialized(quotient, numerator, denominator); err != nil { return err; } + + q, x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(q, x, y, t1, t2); + + if err = grow(q, numerator.used + 2); err != nil { return err; } + q.used = numerator.used + 2; + + if err = init_multi(t1, t2); err != nil { return err; } + if err = copy(x, numerator); err != nil { return err; } + if err = copy(y, denominator); err != nil { return err; } + + /* + Fix the sign. + */ + neg := numerator.sign != denominator.sign; + x.sign = .Zero_or_Positive; + y.sign = .Zero_or_Positive; + + /* + Normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] + */ + norm, _ := count_bits(y); + norm %= _DIGIT_BITS; + + if norm < _DIGIT_BITS - 1 { + norm = (_DIGIT_BITS - 1) - norm; + if err = shl(x, x, norm); err != nil { return err; } + if err = shl(y, y, norm); err != nil { return err; } + } else { + norm = 0; + } + + /* + Note: HAC does 0 based, so if used==5 then it's 0,1,2,3,4, i.e. use 4 + */ + n := x.used - 1; + t := y.used - 1; + + /* + while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } + y = y*b**{n-t} + */ + + if err = shl_digit(y, n - t); err != nil { return err; } + + c, _ := cmp(x, y); + for c != -1 { + q.digit[n - t] += 1; + if err = sub(x, x, y); err != nil { return err; } + c, _ = cmp(x, y); + } + + /* + Reset y by shifting it back down. + */ + shr_digit(y, n - t); + + /* + Step 3. for i from n down to (t + 1). + */ + #no_bounds_check for i := n; i >= (t + 1); i -= 1 { + if (i > x.used) { continue; } + + /* + step 3.1 if xi == yt then set q{i-t-1} to b-1, otherwise set q{i-t-1} to (xi*b + x{i-1})/yt + */ + if x.digit[i] == y.digit[t] { + q.digit[(i - t) - 1] = 1 << (_DIGIT_BITS - 1); + } else { + + tmp := _WORD(x.digit[i]) << _DIGIT_BITS; + tmp |= _WORD(x.digit[i - 1]); + tmp /= _WORD(y.digit[t]); + if tmp > _WORD(_MASK) { + tmp = _WORD(_MASK); + } + q.digit[(i - t) - 1] = DIGIT(tmp & _WORD(_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + + iter := 0; + + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] + 1) & _MASK; + #no_bounds_check for { + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; + + /* + Find left hand. + */ + zero(t1); + t1.digit[0] = ((t - 1) < 0) ? 0 : y.digit[t - 1]; + t1.digit[1] = y.digit[t]; + t1.used = 2; + if err = mul(t1, t1, q.digit[(i - t) - 1]); err != nil { return err; } + + /* + Find right hand. + */ + t2.digit[0] = ((i - 2) < 0) ? 0 : x.digit[i - 2]; + t2.digit[1] = x.digit[i - 1]; /* i >= 1 always holds */ + t2.digit[2] = x.digit[i]; + t2.used = 3; + + if t1_t2, _ := cmp_mag(t1, t2); t1_t2 != 1 { + break; + } + iter += 1; if iter > 100 { return .Max_Iterations_Reached; } + } + + /* + Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} + */ + if err = int_mul_digit(t1, y, q.digit[(i - t) - 1]); err != nil { return err; } + if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = sub(x, x, t1); err != nil { return err; } + + /* + if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } + */ + if x.sign == .Negative { + if err = copy(t1, y); err != nil { return err; } + if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = add(x, x, t1); err != nil { return err; } + + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; + } + } + + /* + Now q is the quotient and x is the remainder, [which we have to normalize] + Get sign before writing to c. + */ + z, _ := is_zero(x); + x.sign = .Zero_or_Positive if z else numerator.sign; + + if quotient != nil { + clamp(q); + swap(q, quotient); + quotient.sign = .Negative if neg else .Zero_or_Positive; + } + + if remainder != nil { + if err = shr(x, x, norm); err != nil { return err; } + swap(x, remainder); + } + + return nil; +} + +/* + Slower bit-bang division... also smaller. +*/ +@(deprecated="Use `_int_div_school`, it's 3.5x faster.") +_private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { + + ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}; + c: int; + + goto_end: for { + if err = one(tq); err != nil { break goto_end; } + + num_bits, _ := count_bits(numerator); + den_bits, _ := count_bits(denominator); + n := num_bits - den_bits; + + if err = abs(ta, numerator); err != nil { break goto_end; } + if err = abs(tb, denominator); err != nil { break goto_end; } + if err = shl(tb, tb, n); err != nil { break goto_end; } + if err = shl(tq, tq, n); err != nil { break goto_end; } + + for n >= 0 { + if c, _ = cmp_mag(ta, tb); c == 0 || c == 1 { + // ta -= tb + if err = sub(ta, ta, tb); err != nil { break goto_end; } + // q += tq + if err = add( q, q, tq); err != nil { break goto_end; } + } + if err = shr1(tb, tb); err != nil { break goto_end; } + if err = shr1(tq, tq); err != nil { break goto_end; } + + n -= 1; + } + + /* + Now q == quotient and ta == remainder. + */ + neg := numerator.sign != denominator.sign; + if quotient != nil { + swap(quotient, q); + z, _ := is_zero(quotient); + quotient.sign = .Negative if neg && !z else .Zero_or_Positive; + } + if remainder != nil { + swap(remainder, ta); + z, _ := is_zero(numerator); + remainder.sign = .Zero_or_Positive if z else numerator.sign; + } + + break goto_end; + } + destroy(ta, tb, tq, q); + return err; +} + /* diff --git a/core/math/big/test.py b/core/math/big/test.py index 4e7c8a406..a06e8e119 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -17,7 +17,7 @@ EXIT_ON_FAIL = False # We skip randomized tests altogether if NO_RANDOM_TESTS is set. # NO_RANDOM_TESTS = True -#NO_RANDOM_TESTS = False +NO_RANDOM_TESTS = False # # If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. @@ -96,11 +96,11 @@ class Error(Enum): # Set up exported procedures # -# try: -l = cdll.LoadLibrary(LIB_PATH) -# except: -# print("Couldn't find or load " + LIB_PATH + ".") -# exit(1) +try: + l = cdll.LoadLibrary(LIB_PATH) +except: + print("Couldn't find or load " + LIB_PATH + ".") + exit(1) def load(export_name, args, res): export_name.argtypes = args From c3db24f834bb4245f45dd858203edbb9716a6ad9 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 7 Aug 2021 17:30:17 +0200 Subject: [PATCH 087/105] big: Split up `gcd` + `lcm`. --- core/math/big/basic.odin | 196 +----------------------------------- core/math/big/internal.odin | 179 +++++++++++++++++++++++++++++++- core/math/big/test.py | 7 +- 3 files changed, 185 insertions(+), 197 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index b6c49a5fd..6d1ab23e6 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -297,36 +297,7 @@ int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { if res_gcd == nil && res_lcm == nil { return nil; } if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != nil { return err; } - az, _ := is_zero(a); bz, _ := is_zero(b); - if az && bz { - if res_gcd != nil { - if err = zero(res_gcd); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } - else if az { - if res_gcd != nil { - if err = abs(res_gcd, b); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } - else if bz { - if res_gcd != nil { - if err = abs(res_gcd, a); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } - - return #force_inline _int_gcd_lcm(res_gcd, res_lcm, a, b); + return #force_inline internal_int_gcd_lcm(res_gcd, res_lcm, a, b); } gcd_lcm :: proc { int_gcd_lcm, }; @@ -346,171 +317,6 @@ int_lcm :: proc(res, a, b: ^Int) -> (err: Error) { } lcm :: proc { int_lcm, }; -/* - Internal function computing both GCD using the binary method, - and, if target isn't `nil`, also LCM. - Expects the arguments to have been initialized. -*/ -_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { - /* - If both `a` and `b` are zero, return zero. - If either `a` or `b`, return the other one. - - The `gcd` and `lcm` wrappers have already done this test, - but `gcd_lcm` wouldn't have, so we still need to perform it. - - If neither result is wanted, we have nothing to do. - */ - if res_gcd == nil && res_lcm == nil { return nil; } - - /* - We need a temporary because `res_gcd` is allowed to be `nil`. - */ - az, _ := is_zero(a); bz, _ := is_zero(b); - if az && bz { - /* - GCD(0, 0) and LCM(0, 0) are both 0. - */ - if res_gcd != nil { - if err = zero(res_gcd); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } else if az { - /* - We can early out with GCD = B and LCM = 0 - */ - if res_gcd != nil { - if err = abs(res_gcd, b); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } else if bz { - /* - We can early out with GCD = A and LCM = 0 - */ - if res_gcd != nil { - if err = abs(res_gcd, a); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } - - temp_gcd_res := &Int{}; - defer destroy(temp_gcd_res); - - /* - If neither `a` or `b` was zero, we need to compute `gcd`. - Get copies of `a` and `b` we can modify. - */ - u, v := &Int{}, &Int{}; - defer destroy(u, v); - if err = copy(u, a); err != nil { return err; } - if err = copy(v, b); err != nil { return err; } - - /* - Must be positive for the remainder of the algorithm. - */ - u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive; - - /* - B1. Find the common power of two for `u` and `v`. - */ - u_lsb, _ := count_lsb(u); - v_lsb, _ := count_lsb(v); - k := min(u_lsb, v_lsb); - - if k > 0 { - /* - Divide the power of two out. - */ - if err = shr(u, u, k); err != nil { return err; } - if err = shr(v, v, k); err != nil { return err; } - } - - /* - Divide any remaining factors of two out. - */ - if u_lsb != k { - if err = shr(u, u, u_lsb - k); err != nil { return err; } - } - if v_lsb != k { - if err = shr(v, v, v_lsb - k); err != nil { return err; } - } - - for v.used != 0 { - /* - Make sure `v` is the largest. - */ - if c, _ := cmp_mag(u, v); c == 1 { - /* - Swap `u` and `v` to make sure `v` is >= `u`. - */ - swap(u, v); - } - - /* - Subtract smallest from largest. - */ - if err = sub(v, v, u); err != nil { return err; } - - /* - Divide out all factors of two. - */ - b, _ := count_lsb(v); - if err = shr(v, v, b); err != nil { return err; } - } - - /* - Multiply by 2**k which we divided out at the beginning. - */ - if err = shl(temp_gcd_res, u, k); err != nil { return err; } - temp_gcd_res.sign = .Zero_or_Positive; - - /* - We've computed `gcd`, either the long way, or because one of the inputs was zero. - If we don't want `lcm`, we're done. - */ - if res_lcm == nil { - swap(temp_gcd_res, res_gcd); - return nil; - } - - /* - Computes least common multiple as `|a*b|/gcd(a,b)` - Divide the smallest by the GCD. - */ - if c, _ := cmp_mag(a, b); c == -1 { - /* - Store quotient in `t2` such that `t2 * b` is the LCM. - */ - if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } - err = mul(res_lcm, res_lcm, b); - } else { - /* - Store quotient in `t2` such that `t2 * a` is the LCM. - */ - if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } - err = mul(res_lcm, res_lcm, b); - } - - if res_gcd != nil { - swap(temp_gcd_res, res_gcd); - } - - /* - Fix the sign to positive and return. - */ - res_lcm.sign = .Zero_or_Positive; - return err; -} - /* remainder = numerator % (1 << bits) */ diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index a3e2548db..995f37000 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -680,7 +680,7 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a // err = _int_div_recursive(quotient, remainder, numerator, denominator); } else { when true { - err = _private_int_div_school(quotient, remainder, numerator, denominator); + err = #force_inline _private_int_div_school(quotient, remainder, numerator, denominator); } else { /* NOTE(Jeroen): We no longer need or use `_private_int_div_small`. @@ -864,6 +864,18 @@ internal_int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { return nil; } +/* + Returns GCD, LCM or both. + + Assumes `a` and `b` to have been initialized. + `res_gcd` and `res_lcm` can be nil or ^Int depending on which results are desired. +*/ +internal_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { + if res_gcd == nil && res_lcm == nil { return nil; } + + return #force_inline _private_int_gcd_lcm(res_gcd, res_lcm, a, b); +} + internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { /* @@ -1466,6 +1478,171 @@ _private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int return #force_inline set(res, 1); } +/* + Internal function computing both GCD using the binary method, + and, if target isn't `nil`, also LCM. + + Expects the `a` and `b` to have been initialized + and one or both of `res_gcd` or `res_lcm` not to be `nil`. + + If both `a` and `b` are zero, return zero. + If either `a` or `b`, return the other one. + + The `gcd` and `lcm` wrappers have already done this test, + but `gcd_lcm` wouldn't have, so we still need to perform it. + + If neither result is wanted, we have nothing to do. +*/ +_private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { + if res_gcd == nil && res_lcm == nil { return nil; } + + /* + We need a temporary because `res_gcd` is allowed to be `nil`. + */ + if a.used == 0 && b.used == 0 { + /* + GCD(0, 0) and LCM(0, 0) are both 0. + */ + if res_gcd != nil { + if err = zero(res_gcd); err != nil { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != nil { return err; } + } + return nil; + } else if a.used == 0 { + /* + We can early out with GCD = B and LCM = 0 + */ + if res_gcd != nil { + if err = abs(res_gcd, b); err != nil { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != nil { return err; } + } + return nil; + } else if b.used == 0 { + /* + We can early out with GCD = A and LCM = 0 + */ + if res_gcd != nil { + if err = abs(res_gcd, a); err != nil { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != nil { return err; } + } + return nil; + } + + temp_gcd_res := &Int{}; + defer destroy(temp_gcd_res); + + /* + If neither `a` or `b` was zero, we need to compute `gcd`. + Get copies of `a` and `b` we can modify. + */ + u, v := &Int{}, &Int{}; + defer destroy(u, v); + if err = copy(u, a); err != nil { return err; } + if err = copy(v, b); err != nil { return err; } + + /* + Must be positive for the remainder of the algorithm. + */ + u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive; + + /* + B1. Find the common power of two for `u` and `v`. + */ + u_lsb, _ := count_lsb(u); + v_lsb, _ := count_lsb(v); + k := min(u_lsb, v_lsb); + + if k > 0 { + /* + Divide the power of two out. + */ + if err = shr(u, u, k); err != nil { return err; } + if err = shr(v, v, k); err != nil { return err; } + } + + /* + Divide any remaining factors of two out. + */ + if u_lsb != k { + if err = shr(u, u, u_lsb - k); err != nil { return err; } + } + if v_lsb != k { + if err = shr(v, v, v_lsb - k); err != nil { return err; } + } + + for v.used != 0 { + /* + Make sure `v` is the largest. + */ + if c, _ := cmp_mag(u, v); c == 1 { + /* + Swap `u` and `v` to make sure `v` is >= `u`. + */ + swap(u, v); + } + + /* + Subtract smallest from largest. + */ + if err = sub(v, v, u); err != nil { return err; } + + /* + Divide out all factors of two. + */ + b, _ := count_lsb(v); + if err = shr(v, v, b); err != nil { return err; } + } + + /* + Multiply by 2**k which we divided out at the beginning. + */ + if err = shl(temp_gcd_res, u, k); err != nil { return err; } + temp_gcd_res.sign = .Zero_or_Positive; + + /* + We've computed `gcd`, either the long way, or because one of the inputs was zero. + If we don't want `lcm`, we're done. + */ + if res_lcm == nil { + swap(temp_gcd_res, res_gcd); + return nil; + } + + /* + Computes least common multiple as `|a*b|/gcd(a,b)` + Divide the smallest by the GCD. + */ + if c, _ := cmp_mag(a, b); c == -1 { + /* + Store quotient in `t2` such that `t2 * b` is the LCM. + */ + if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } + err = mul(res_lcm, res_lcm, b); + } else { + /* + Store quotient in `t2` such that `t2 * a` is the LCM. + */ + if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } + err = mul(res_lcm, res_lcm, b); + } + + if res_gcd != nil { + swap(temp_gcd_res, res_gcd); + } + + /* + Fix the sign to positive and return. + */ + res_lcm.sign = .Zero_or_Positive; + return err; +} + /* ======================== End of private procedures ======================= diff --git a/core/math/big/test.py b/core/math/big/test.py index a06e8e119..6bdff91db 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -446,7 +446,6 @@ TESTS = { test_factorial: [ [ 6_000 ], # Regular factorial, see cutoff in common.odin. [ 12_345 ], # Binary split factorial - [ 100_000 ], ], test_gcd: [ [ 23, 25, ], @@ -464,6 +463,12 @@ TESTS = { ], } +if not FAST_TESTS: + TESTS[test_factorial].append( + # This one on its own takes around 800ms, so we exclude it for FAST_TESTS + [ 100_000 ], + ) + total_passes = 0 total_failures = 0 From d4a03acbc3a8d99959250256fff041a8d0b3ef0d Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 7 Aug 2021 18:00:07 +0200 Subject: [PATCH 088/105] big: Split up `int_mod_bits` (res = val % (1 << bits)) --- core/math/big/basic.odin | 36 ++++------------------------ core/math/big/internal.odin | 48 +++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 6d1ab23e6..611ff6054 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -321,39 +321,11 @@ lcm :: proc { int_lcm, }; remainder = numerator % (1 << bits) */ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { - if err = clear_if_uninitialized(remainder); err != nil { return err; } - if err = clear_if_uninitialized(numerator); err != nil { return err; } - + if remainder == nil || numerator == nil { return .Invalid_Pointer; } + if err = clear_if_uninitialized(remainder, numerator); err != nil { return err; } if bits < 0 { return .Invalid_Argument; } - if bits == 0 { return zero(remainder); } - /* - If the modulus is larger than the value, return the value. - */ - err = copy(remainder, numerator); - if bits >= (numerator.used * _DIGIT_BITS) || err != nil { - return; - } - - /* - Zero digits above the last digit of the modulus. - */ - zero_count := (bits / _DIGIT_BITS); - zero_count += 0 if (bits % _DIGIT_BITS == 0) else 1; - - /* - Zero remainder. Special case, can't use `zero_unused`. - */ - if zero_count > 0 { - mem.zero_slice(remainder.digit[zero_count:]); - } - - /* - Clear the digit that is not completely outside/inside the modulus. - */ - remainder.digit[bits / _DIGIT_BITS] &= DIGIT(1 << DIGIT(bits % _DIGIT_BITS)) - DIGIT(1); - return clamp(remainder); + return #force_inline internal_int_mod_bits(remainder, numerator, bits); } -mod_bits :: proc { int_mod_bits, }; - +mod_bits :: proc { int_mod_bits, }; \ No newline at end of file diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 995f37000..3be822a05 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -876,6 +876,44 @@ internal_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { return #force_inline _private_int_gcd_lcm(res_gcd, res_lcm, a, b); } +/* + remainder = numerator % (1 << bits) + + Assumes `remainder` and `numerator` both not to be `nil` and `bits` to be >= 0. +*/ +internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { + /* + Everything is divisible by 1 << 0 == 1, so this returns 0. + */ + if bits == 0 { return zero(remainder); } + + /* + If the modulus is larger than the value, return the value. + */ + err = copy(remainder, numerator); + if bits >= (numerator.used * _DIGIT_BITS) || err != nil { + return; + } + + /* + Zero digits above the last digit of the modulus. + */ + zero_count := (bits / _DIGIT_BITS); + zero_count += 0 if (bits % _DIGIT_BITS == 0) else 1; + + /* + Zero remainder. Special case, can't use `zero_unused`. + */ + if zero_count > 0 { + mem.zero_slice(remainder.digit[zero_count:]); + } + + /* + Clear the digit that is not completely outside/inside the modulus. + */ + remainder.digit[bits / _DIGIT_BITS] &= DIGIT(1 << DIGIT(bits % _DIGIT_BITS)) - DIGIT(1); + return clamp(remainder); +} internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { /* @@ -1590,7 +1628,7 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { /* Subtract smallest from largest. */ - if err = sub(v, v, u); err != nil { return err; } + if err = internal_sub(v, v, u); err != nil { return err; } /* Divide out all factors of two. @@ -1622,14 +1660,14 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { /* Store quotient in `t2` such that `t2 * b` is the LCM. */ - if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } - err = mul(res_lcm, res_lcm, b); + if err = internal_div(res_lcm, a, temp_gcd_res); err != nil { return err; } + err = internal_mul(res_lcm, res_lcm, b); } else { /* Store quotient in `t2` such that `t2 * a` is the LCM. */ - if err = div(res_lcm, a, temp_gcd_res); err != nil { return err; } - err = mul(res_lcm, res_lcm, b); + if err = internal_div(res_lcm, a, temp_gcd_res); err != nil { return err; } + err = internal_mul(res_lcm, res_lcm, b); } if res_gcd != nil { From 777e17d80f395a383408b0a1eefb371154b6a489 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 7 Aug 2021 18:44:05 +0200 Subject: [PATCH 089/105] big: Improve tunables. --- core/math/big/basic.odin | 6 +-- core/math/big/build.bat | 6 +-- core/math/big/common.odin | 89 +++++++++++++++++++++++-------------- core/math/big/example.odin | 42 ++++++++++------- core/math/big/exp_log.odin | 6 +-- core/math/big/helpers.odin | 2 +- core/math/big/internal.odin | 18 ++++---- 7 files changed, 100 insertions(+), 69 deletions(-) diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 611ff6054..755aa51cd 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -11,8 +11,6 @@ package big This file contains basic arithmetic operations like `add`, `sub`, `mul`, `div`, ... */ -import "core:mem" - /* =========================== User-level routines @@ -244,7 +242,7 @@ sqrmod :: proc { int_sqrmod, }; int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { - if n < 0 || n > _FACTORIAL_MAX_N { return .Invalid_Argument; } + if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; } if res == nil { return .Invalid_Pointer; } return #force_inline internal_int_factorial(res, n); @@ -269,7 +267,7 @@ factorial :: proc { int_factorial, }; */ int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { if res == nil { return .Invalid_Pointer; } - if n < 0 || n > _FACTORIAL_MAX_N { return .Invalid_Argument; } + if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; } if k > n { return zero(res); } diff --git a/core/math/big/build.bat b/core/math/big/build.bat index a12e3262a..1921803b5 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -:odin run . -vet +odin run . -vet : -o:size :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -python test.py \ No newline at end of file +:python test.py \ No newline at end of file diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 1b76c9520..837fc5789 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -13,19 +13,68 @@ import "core:intrinsics" /* TODO: Make the tunables runtime adjustable where practical. + This allows to benchmark and/or setting optimized values for a certain CPU without recompiling. */ /* - Tunables + ========================== TUNABLES ========================== + + `initialize_constants` returns `#config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF)` + and we initialize this cutoff that way so that the procedure is used and called, + because it handles initializing the constants ONE, ZERO, MINUS_ONE, NAN and INF. + + `initialize_constants` also replaces the other `_DEFAULT_*` cutoffs with custom compile-time values if so `#config`ured. + +*/ +MUL_KARATSUBA_CUTOFF := initialize_constants(); +SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF; +MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF; +SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF; + +/* + These defaults were tuned on an AMD A8-6600K (64-bit) using libTomMath's `make tune`. + + TODO(Jeroen): Port this tuning algorithm and tune them for more modern processors. + + It would also be cool if we collected some data across various processor families. + This would let uss set reasonable defaults at runtime as this library initializes + itself by using `cpuid` or the ARM equivalent. */ -MATH_BIG_FORCE_64_BIT :: false; -MATH_BIG_FORCE_32_BIT :: false; +_DEFAULT_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, 80); +_DEFAULT_SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, 120); +_DEFAULT_MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, 350); +_DEFAULT_SQR_TOOM_CUTOFF :: #config(SQR_TOOM_CUTOFF, 400); + + +MAX_ITERATIONS_ROOT_N := 500; + +/* + Largest `N` for which we'll compute `N!` +*/ +FACTORIAL_MAX_N := 1_000_000; + +/* + Cutoff to switch to int_factorial_binary_split, and its max recursion level. +*/ +FACTORIAL_BINARY_SPLIT_CUTOFF := 6100; +FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS := 100; + + +/* + We don't allow these to be switched at runtime for two reasons: + + 1) 32-bit and 64-bit versions of procedures use different types for their storage, + so we'd have to double the number of procedures, and they couldn't interact. + + 2) Optimizations thanks to precomputed masks wouldn't work. +*/ +MATH_BIG_FORCE_64_BIT :: #config(MATH_BIG_FORCE_64_BIT, false); +MATH_BIG_FORCE_32_BIT :: #config(MATH_BIG_FORCE_32_BIT, false); when (MATH_BIG_FORCE_32_BIT && MATH_BIG_FORCE_64_BIT) { #panic("Cannot force 32-bit and 64-bit big backend simultaneously."); }; - -_LOW_MEMORY :: #config(BIGINT_SMALL_MEMORY, false); +_LOW_MEMORY :: #config(BIGINT_SMALL_MEMORY, false); when _LOW_MEMORY { _DEFAULT_DIGIT_COUNT :: 8; } else { @@ -33,36 +82,8 @@ when _LOW_MEMORY { } /* - `initialize_constants` returns `#config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF)` - and we initialize this cutoff that way so that the procedure is used and called, - because it handles initializing the constants ONE, ZERO, MINUS_ONE, NAN and INF. + ======================= END OF TUNABLES ======================= */ -_MUL_KARATSUBA_CUTOFF := initialize_constants(); -_SQR_KARATSUBA_CUTOFF := #config(SQR_KARATSUBA_CUTOFF, _DEFAULT_SQR_KARATSUBA_CUTOFF); -_MUL_TOOM_CUTOFF := #config(MUL_TOOM_CUTOFF, _DEFAULT_MUL_TOOM_CUTOFF); -_SQR_TOOM_CUTOFF := #config(SQR_TOOM_CUTOFF, _DEFAULT_SQR_TOOM_CUTOFF); - -/* - These defaults were tuned on an AMD A8-6600K (64-bit) using libTomMath's `make tune`. - TODO(Jeroen): Port this tuning algorithm and tune them for more modern processors. -*/ -_DEFAULT_MUL_KARATSUBA_CUTOFF :: 80; -_DEFAULT_SQR_KARATSUBA_CUTOFF :: 120; -_DEFAULT_MUL_TOOM_CUTOFF :: 350; -_DEFAULT_SQR_TOOM_CUTOFF :: 400; - -_MAX_ITERATIONS_ROOT_N :: 500; - -/* - Largest `N` for which we'll compute `N!` -*/ -_FACTORIAL_MAX_N :: 1_000_000; - -/* - Cutoff to switch to int_factorial_binary_split, and its max recursion level. -*/ -_FACTORIAL_BINARY_SPLIT_CUTOFF :: 6100; -_FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS :: 100; Sign :: enum u8 { Zero_or_Positive = 0, diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 3e4a27b8c..e8aa51c0d 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -15,17 +15,23 @@ import "core:mem" print_configation :: proc() { fmt.printf( -`Configuration: - DIGIT_BITS %v - MIN_DIGIT_COUNT %v - MAX_DIGIT_COUNT %v - DEFAULT_DIGIT_COUNT %v - MAX_COMBA %v - WARRAY %v - MUL_KARATSUBA_CUTOFF %v - SQR_KARATSUBA_CUTOFF %v - MUL_TOOM_CUTOFF %v - SQR_TOOM_CUTOFF %v +` +Configuration: + _DIGIT_BITS %v + _MIN_DIGIT_COUNT %v + _MAX_DIGIT_COUNT %v + _DEFAULT_DIGIT_COUNT %v + _MAX_COMBA %v + _WARRAY %v +Runtime tunable: + MUL_KARATSUBA_CUTOFF %v + SQR_KARATSUBA_CUTOFF %v + MUL_TOOM_CUTOFF %v + SQR_TOOM_CUTOFF %v + MAX_ITERATIONS_ROOT_N %v + FACTORIAL_MAX_N %v + FACTORIAL_BINARY_SPLIT_CUTOFF %v + FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS %v `, _DIGIT_BITS, _MIN_DIGIT_COUNT, @@ -33,10 +39,14 @@ _MAX_DIGIT_COUNT, _DEFAULT_DIGIT_COUNT, _MAX_COMBA, _WARRAY, -_MUL_KARATSUBA_CUTOFF, -_SQR_KARATSUBA_CUTOFF, -_MUL_TOOM_CUTOFF, -_SQR_TOOM_CUTOFF, +MUL_KARATSUBA_CUTOFF, +SQR_KARATSUBA_CUTOFF, +MUL_TOOM_CUTOFF, +SQR_TOOM_CUTOFF, +MAX_ITERATIONS_ROOT_N, +FACTORIAL_MAX_N, +FACTORIAL_BINARY_SPLIT_CUTOFF, +FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS, ); } @@ -84,6 +94,8 @@ main :: proc() { demo(); + print_configation(); + print_timings(); if len(ta.allocation_map) > 0 { diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin index 33ebe9e21..15dfa0b5a 100644 --- a/core/math/big/exp_log.odin +++ b/core/math/big/exp_log.odin @@ -360,7 +360,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } if c, err = cmp(t1, t2); c == 0 { break; } iterations += 1; - if iterations == _MAX_ITERATIONS_ROOT_N { + if iterations == MAX_ITERATIONS_ROOT_N { return .Max_Iterations_Reached; } } @@ -383,7 +383,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } iterations += 1; - if iterations == _MAX_ITERATIONS_ROOT_N { + if iterations == MAX_ITERATIONS_ROOT_N { return .Max_Iterations_Reached; } } @@ -401,7 +401,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } iterations += 1; - if iterations == _MAX_ITERATIONS_ROOT_N { + if iterations == MAX_ITERATIONS_ROOT_N { return .Max_Iterations_Reached; } } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index e0e79d6f4..cf7492182 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -691,7 +691,7 @@ initialize_constants :: proc() -> (res: int) { set( INF, 1); INF.flags = {.Immutable, .Inf}; set( INF, -1); MINUS_INF.flags = {.Immutable, .Inf}; - return #config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF); + return _DEFAULT_MUL_KARATSUBA_CUTOFF; } destroy_constants :: proc() { diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 3be822a05..372dc4e8d 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -597,10 +597,10 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc /* Do we need to square? */ - if false && src.used >= _SQR_TOOM_CUTOFF { + if false && src.used >= SQR_TOOM_CUTOFF { /* Use Toom-Cook? */ // err = s_mp_sqr_toom(a, c); - } else if false && src.used >= _SQR_KARATSUBA_CUTOFF { + } else if false && src.used >= SQR_KARATSUBA_CUTOFF { /* Karatsuba? */ // err = s_mp_sqr_karatsuba(a, c); } else if false && ((src.used * 2) + 1) < _WARRAY && @@ -625,16 +625,16 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc max_used := max(src.used, multiplier.used); digits := src.used + multiplier.used + 1; - if false && min_used >= _MUL_KARATSUBA_CUTOFF && - max_used / 2 >= _MUL_KARATSUBA_CUTOFF && + if false && min_used >= MUL_KARATSUBA_CUTOFF && + max_used / 2 >= MUL_KARATSUBA_CUTOFF && /* Not much effect was observed below a ratio of 1:2, but again: YMMV. */ max_used >= 2 * min_used { // err = s_mp_mul_balance(a,b,c); - } else if false && min_used >= _MUL_TOOM_CUTOFF { + } else if false && min_used >= MUL_TOOM_CUTOFF { // err = s_mp_mul_toom(a, b, c); - } else if false && min_used >= _MUL_KARATSUBA_CUTOFF { + } else if false && min_used >= MUL_KARATSUBA_CUTOFF { // err = s_mp_mul_karatsuba(a, b, c); } else if digits < _WARRAY && min_used <= _MAX_COMBA { /* @@ -676,7 +676,7 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a return nil; } - if false && (denominator.used > 2 * _MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) { + if false && (denominator.used > 2 * MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) { // err = _int_div_recursive(quotient, remainder, numerator, denominator); } else { when true { @@ -846,7 +846,7 @@ internal_sqrmod :: proc { internal_int_sqrmod, }; This way we'll have to reallocate less, possibly not at all. */ internal_int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { - if n >= _FACTORIAL_BINARY_SPLIT_CUTOFF { + if n >= FACTORIAL_BINARY_SPLIT_CUTOFF { return #force_inline _private_int_factorial_binary_split(res, n); } @@ -1490,7 +1490,7 @@ _private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int t1, t2 := &Int{}, &Int{}; defer destroy(t1, t2); - if level > _FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } + if level > FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } num_factors := (stop - start) >> 1; if num_factors == 2 { From fd95f50c560550a361b109d1f47c5d31c7621fe4 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 7 Aug 2021 22:34:40 +0200 Subject: [PATCH 090/105] big: Split up `int_is_*` comparison tests. --- core/math/big/build.bat | 6 +- core/math/big/compare.odin | 198 +++++--------------- core/math/big/example.odin | 16 +- core/math/big/internal.odin | 354 ++++++++++++++++++++++++++++++------ core/math/big/prime.odin | 41 +---- 5 files changed, 353 insertions(+), 262 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 1921803b5..a12e3262a 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -odin run . -vet +:odin run . -vet : -o:size :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check +odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -:python test.py \ No newline at end of file +python test.py \ No newline at end of file diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index 3d4f220fa..1b0aa6877 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -9,199 +9,93 @@ package big The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. This file contains various comparison routines. + + We essentially just check if params are initialized before punting to the `internal_*` versions. + This has the side benefit of being able to add additional characteristics to numbers, like NaN, + and keep support for that contained. */ import "core:intrinsics" -import "core:mem" int_is_initialized :: proc(a: ^Int) -> bool { - if a == nil { - return false; - } - raw := transmute(mem.Raw_Dynamic_Array)a.digit; - return raw.cap >= _MIN_DIGIT_COUNT; + if a == nil { return false; } + + return #force_inline internal_int_is_initialized(a); } -int_is_zero :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return false, err; - } - return a.used == 0, nil; +int_is_zero :: proc(a: ^Int) -> (zero: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_zero(a), nil; } -int_is_positive :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return false, err; - } - return a.sign == .Zero_or_Positive, nil; +int_is_positive :: proc(a: ^Int) -> (positive: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_positive(a), nil; } -int_is_negative :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return false, err; - } - return a.sign == .Negative, nil; +int_is_negative :: proc(a: ^Int) -> (negative: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_negative(a), nil; } -int_is_even :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return false, err; - } +int_is_even :: proc(a: ^Int) -> (even: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } - res, err = is_zero(a); - if err != nil { - return false, err; - } else if res == true { - return true, nil; - } - - res = false; - if a.used > 0 && a.digit[0] & 1 == 0 { - res = true; - } - return res, nil; + return #force_inline internal_is_even(a), nil; } -int_is_odd :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return false, err; - } +int_is_odd :: proc(a: ^Int) -> (odd: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } - res, err = is_even(a); - return !res, err; + return #force_inline internal_is_odd(a), nil; } -platform_int_is_power_of_two :: proc(a: int) -> bool { +platform_int_is_power_of_two :: #force_inline proc(a: int) -> bool { return ((a) != 0) && (((a) & ((a) - 1)) == 0); } int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return false, err; - } + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } - /* - Early out for Int == 0. - */ - if a.used == 0 { - return false, nil; - } - - /* - For an `Int` to be a power of two, its top limb has to be a power of two. - */ - if !platform_int_is_power_of_two(int(a.digit[a.used - 1])) { - return false, nil; - } - - /* - That was the only limb, so it's a power of two. - */ - if a.used == 1 { - return true, nil; - } - - /* - For an Int to be a power of two, all limbs except the top one have to be zero. - */ - for i := 1; i < a.used; i += 1 { - if a.digit[i - 1] != 0 { - return false, nil; - } - } - return true, nil; + return #force_inline internal_is_power_of_two(a), nil; } /* Compare two `Int`s, signed. */ -int_compare :: proc(a, b: ^Int) -> (res: int, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } - if err = clear_if_uninitialized(b); err != nil { - return 0, err; - } +int_compare :: proc(a, b: ^Int) -> (comparison: int, err: Error) { + if a == nil || b == nil { return 0, .Invalid_Pointer; } + if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } - neg: bool; - if neg, err = is_negative(a); err != nil { - return 0, err; - } - - /* Compare based on sign */ - if a.sign != b.sign { - res = -1 if neg else +1; - return res, nil; - } - - /* If negative, compare in the opposite direction */ - if neg { - return cmp_mag(b, a); - } - return cmp_mag(a, b); + return #force_inline internal_cmp(a, b), nil; } +int_cmp :: int_compare; /* Compare an `Int` to an unsigned number upto the size of the backing type. */ -int_compare_digit :: proc(a: ^Int, u: DIGIT) -> (res: int, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } +int_compare_digit :: proc(a: ^Int, b: DIGIT) -> (comparison: int, err: Error) { + if a == nil { return 0, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return 0, err; } - /* Compare based on sign */ - neg: bool; - if neg, err = is_neg(a); err != nil { - return 0, err; - } - if neg { - return -1, nil; - } - - /* Compare based on magnitude */ - if a.used > 1 { - return +1, nil; - } - - /* Compare the only digit in `a` to `u`. */ - if a.digit[0] != u { - if a.digit[0] > u { - return +1, nil; - } - return -1, nil; - } - - return 0, nil; + return #force_inline internal_cmp_digit(a, b), nil; } /* Compare the magnitude of two `Int`s, unsigned. */ int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } - if err = clear_if_uninitialized(b); err != nil { - return 0, err; - } + if a == nil || b == nil { return 0, .Invalid_Pointer; } + if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } - /* Compare based on used digits */ - if a.used != b.used { - if a.used > b.used { - return +1, nil; - } - return -1, nil; - } - - /* Same number of used digits, compare based on their value */ - for n := a.used - 1; n >= 0; n -= 1 { - if a.digit[n] != b.digit[n] { - if a.digit[n] > b.digit[n] { - return +1, nil; - } - return -1, nil; - } - } - - return 0, nil; + return #force_inline internal_cmp_mag(a, b), nil; } \ No newline at end of file diff --git a/core/math/big/example.odin b/core/math/big/example.odin index e8aa51c0d..761fa97cb 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -75,16 +75,12 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - n := 1_024; - k := 3; - - { - SCOPED_TIMING(.choose); - choose(a, n, k); - } - - fmt.printf("%v choose %v ", n, k); - print("= ", a); + power_of_two(a, 3112); + fmt.printf("a is power of two: %v\n", internal_is_power_of_two(a)); + sub(a, a, 1); + fmt.printf("a is power of two: %v\n", internal_is_power_of_two(a)); + add(a, a, 1); + fmt.printf("a is power of two: %v\n", internal_is_power_of_two(a)); } main :: proc() { diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 372dc4e8d..1ba1c7fa3 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -25,6 +25,8 @@ package big Exceptions include `quotient` and `remainder`, which are allowed to be `nil` when the calling code doesn't need them. Check the comments above each `internal_*` implementation to see what constraints it expects to have met. + + TODO: Handle +/- Infinity and NaN. */ import "core:mem" @@ -107,6 +109,7 @@ internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocat */ return clamp(dest); } +internal_add_unsigned :: proc { internal_int_add_unsigned, }; /* Low-level addition, signed. Handbook of Applied Cryptography, algorithm 14.7. @@ -136,6 +139,7 @@ internal_int_add_signed :: proc(dest, a, b: ^Int, allocator := context.allocator dest.sign = x.sign; return #force_inline internal_int_sub_unsigned(dest, x, y, allocator); } +internal_add_signed :: proc { internal_int_add_signed, }; /* Low-level addition Int+DIGIT, signed. Handbook of Applied Cryptography, algorithm 14.7. @@ -246,7 +250,6 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { */ return clamp(dest); } - internal_add :: proc { internal_int_add_signed, internal_int_add_digit, }; /* @@ -314,6 +317,7 @@ internal_int_sub_unsigned :: proc(dest, number, decrease: ^Int, allocator := con */ return clamp(dest); } +internal_sub_unsigned :: proc { internal_int_sub_unsigned, }; /* Low-level subtraction, signed. Handbook of Applied Cryptography, algorithm 14.9. @@ -915,6 +919,204 @@ internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Er return clamp(remainder); } +/* + ============================= Low-level helpers ============================= + + + `internal_*` helpers don't return an `Error` like their public counterparts do, + because they expect not to be passed `nil` or uninitialized inputs. + + This makes them more suitable for `internal_*` functions and some of the + public ones that have already satisfied these constraints. +*/ + +/* + This procedure will return `true` if the `Int` is initialized, `false` if not. + Assumes `a` not to be `nil`. +*/ +internal_int_is_initialized :: #force_inline proc(a: ^Int) -> (initialized: bool) { + raw := transmute(mem.Raw_Dynamic_Array)a.digit; + return raw.cap >= _MIN_DIGIT_COUNT; +} +internal_is_initialized :: proc { internal_int_is_initialized, }; + +/* + This procedure will return `true` if the `Int` is zero, `false` if not. + Assumes `a` not to be `nil`. +*/ +internal_int_is_zero :: #force_inline proc(a: ^Int) -> (zero: bool) { + return a.used == 0; +} +internal_is_zero :: proc { internal_int_is_zero, }; + +/* + This procedure will return `true` if the `Int` is positive, `false` if not. + Assumes `a` not to be `nil`. +*/ +internal_int_is_positive :: #force_inline proc(a: ^Int) -> (positive: bool) { + return a.sign == .Zero_or_Positive; +} +internal_is_positive :: proc { internal_int_is_positive, }; + +/* + This procedure will return `true` if the `Int` is negative, `false` if not. + Assumes `a` not to be `nil`. +*/ +internal_int_is_negative :: #force_inline proc(a: ^Int) -> (negative: bool) { + return a.sign == .Negative; +} +internal_is_negative :: proc { internal_int_is_negative, }; + +/* + This procedure will return `true` if the `Int` is even, `false` if not. + Assumes `a` not to be `nil`. +*/ +internal_int_is_even :: #force_inline proc(a: ^Int) -> (even: bool) { + if internal_is_zero(a) { return true; } + + /* + `a.used` > 0 here, because the above handled `is_zero`. + We don't need to explicitly test it. + */ + return a.digit[0] & 1 == 0; +} +internal_is_even :: proc { internal_int_is_even, }; + +/* + This procedure will return `true` if the `Int` is even, `false` if not. + Assumes `a` not to be `nil`. +*/ +internal_int_is_odd :: #force_inline proc(a: ^Int) -> (odd: bool) { + return !internal_int_is_even(a); +} +internal_is_odd :: proc { internal_int_is_odd, }; + + +/* + This procedure will return `true` if the `Int` is a power of two, `false` if not. + Assumes `a` not to be `nil`. +*/ +internal_int_is_power_of_two :: #force_inline proc(a: ^Int) -> (power_of_two: bool) { + /* + Early out for Int == 0. + */ + if #force_inline internal_is_zero(a) { return true; } + + /* + For an `Int` to be a power of two, its bottom limb has to be a power of two. + */ + if ! #force_inline platform_int_is_power_of_two(int(a.digit[a.used - 1])) { return false; } + + /* + We've established that the bottom limb is a power of two. + If it's the only limb, that makes the entire Int a power of two. + */ + if a.used == 1 { return true; } + + /* + For an `Int` to be a power of two, all limbs except the top one have to be zero. + */ + for i := 1; i < a.used && a.digit[i - 1] != 0; i += 1 { return false; } + + return true; +} +internal_is_power_of_two :: proc { internal_int_is_power_of_two, }; + +/* + Compare two `Int`s, signed. + Returns -1 if `a` < `b`, 0 if `a` == `b` and 1 if `b` > `a`. + + Expects `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`. +*/ +internal_int_compare :: #force_inline proc(a, b: ^Int) -> (comparison: int) { + a_is_negative := #force_inline internal_is_negative(a); + + /* + Compare based on sign. + */ + if a.sign != b.sign { return -1 if a_is_negative else +1; } + + /* + If `a` is negative, compare in the opposite direction */ + if a_is_negative { return #force_inline internal_compare_magnitude(b, a); } + + return #force_inline internal_compare_magnitude(a, b); +} +internal_compare :: proc { internal_int_compare, internal_int_compare_digit, }; +internal_cmp :: internal_compare; + +/* + Compare an `Int` to an unsigned number upto the size of the backing type. + + Returns -1 if `a` < `b`, 0 if `a` == `b` and 1 if `b` > `a`. + + Expects `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`. +*/ +internal_int_compare_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (comparison: int) { + /* + Compare based on sign. + */ + + if #force_inline internal_is_negative(a) { return -1; } + + /* + Compare based on magnitude. + */ + if a.used > 1 { return +1; } + + /* + Compare the only digit in `a` to `b`. + */ + switch { + case a.digit[0] < b: + return -1; + case a.digit[0] == b: + return 0; + case a.digit[0] > b: + return +1; + case: + /* + Unreachable. + Just here because Odin complains about a missing return value at the bottom of the proc otherwise. + */ + return; + } +} +internal_compare_digit :: proc { internal_int_compare_digit, }; +internal_cmp_digit :: internal_compare_digit; + +/* + Compare the magnitude of two `Int`s, unsigned. +*/ +internal_int_compare_magnitude :: #force_inline proc(a, b: ^Int) -> (comparison: int) { + /* + Compare based on used digits. + */ + if a.used != b.used { + if a.used > b.used { + return +1; + } + return -1; + } + + /* + Same number of used digits, compare based on their value. + */ + #no_bounds_check for n := a.used - 1; n >= 0; n -= 1 { + if a.digit[n] != b.digit[n] { + if a.digit[n] > b.digit[n] { + return +1; + } + return -1; + } + } + + return 0; +} +internal_compare_magnitude :: proc { internal_int_compare_magnitude, }; +internal_cmp_mag :: internal_compare_magnitude; + + internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { /* If we don't pass the number of previously used DIGITs, we zero all remaining ones. @@ -1689,67 +1891,105 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { Tables used by `internal_*` and `_*`. */ +_private_prime_table := []DIGIT{ + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653, +}; + when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { _factorial_table := [35]_WORD{ -/* f(00): */ 1, -/* f(01): */ 1, -/* f(02): */ 2, -/* f(03): */ 6, -/* f(04): */ 24, -/* f(05): */ 120, -/* f(06): */ 720, -/* f(07): */ 5_040, -/* f(08): */ 40_320, -/* f(09): */ 362_880, -/* f(10): */ 3_628_800, -/* f(11): */ 39_916_800, -/* f(12): */ 479_001_600, -/* f(13): */ 6_227_020_800, -/* f(14): */ 87_178_291_200, -/* f(15): */ 1_307_674_368_000, -/* f(16): */ 20_922_789_888_000, -/* f(17): */ 355_687_428_096_000, -/* f(18): */ 6_402_373_705_728_000, -/* f(19): */ 121_645_100_408_832_000, -/* f(20): */ 2_432_902_008_176_640_000, -/* f(21): */ 51_090_942_171_709_440_000, -/* f(22): */ 1_124_000_727_777_607_680_000, -/* f(23): */ 25_852_016_738_884_976_640_000, -/* f(24): */ 620_448_401_733_239_439_360_000, -/* f(25): */ 15_511_210_043_330_985_984_000_000, -/* f(26): */ 403_291_461_126_605_635_584_000_000, -/* f(27): */ 10_888_869_450_418_352_160_768_000_000, -/* f(28): */ 304_888_344_611_713_860_501_504_000_000, -/* f(29): */ 8_841_761_993_739_701_954_543_616_000_000, -/* f(30): */ 265_252_859_812_191_058_636_308_480_000_000, -/* f(31): */ 8_222_838_654_177_922_817_725_562_880_000_000, -/* f(32): */ 263_130_836_933_693_530_167_218_012_160_000_000, -/* f(33): */ 8_683_317_618_811_886_495_518_194_401_280_000_000, -/* f(34): */ 295_232_799_039_604_140_847_618_609_643_520_000_000, +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, +/* f(21): */ 51_090_942_171_709_440_000, +/* f(22): */ 1_124_000_727_777_607_680_000, +/* f(23): */ 25_852_016_738_884_976_640_000, +/* f(24): */ 620_448_401_733_239_439_360_000, +/* f(25): */ 15_511_210_043_330_985_984_000_000, +/* f(26): */ 403_291_461_126_605_635_584_000_000, +/* f(27): */ 10_888_869_450_418_352_160_768_000_000, +/* f(28): */ 304_888_344_611_713_860_501_504_000_000, +/* f(29): */ 8_841_761_993_739_701_954_543_616_000_000, +/* f(30): */ 265_252_859_812_191_058_636_308_480_000_000, +/* f(31): */ 8_222_838_654_177_922_817_725_562_880_000_000, +/* f(32): */ 263_130_836_933_693_530_167_218_012_160_000_000, +/* f(33): */ 8_683_317_618_811_886_495_518_194_401_280_000_000, +/* f(34): */ 295_232_799_039_604_140_847_618_609_643_520_000_000, }; } else { _factorial_table := [21]_WORD{ -/* f(00): */ 1, -/* f(01): */ 1, -/* f(02): */ 2, -/* f(03): */ 6, -/* f(04): */ 24, -/* f(05): */ 120, -/* f(06): */ 720, -/* f(07): */ 5_040, -/* f(08): */ 40_320, -/* f(09): */ 362_880, -/* f(10): */ 3_628_800, -/* f(11): */ 39_916_800, -/* f(12): */ 479_001_600, -/* f(13): */ 6_227_020_800, -/* f(14): */ 87_178_291_200, -/* f(15): */ 1_307_674_368_000, -/* f(16): */ 20_922_789_888_000, -/* f(17): */ 355_687_428_096_000, -/* f(18): */ 6_402_373_705_728_000, -/* f(19): */ 121_645_100_408_832_000, -/* f(20): */ 2_432_902_008_176_640_000, +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, }; }; diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index a64215c62..355feecc2 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -18,7 +18,7 @@ package big int_prime_is_divisible :: proc(a: ^Int) -> (res: bool, err: Error) { rem: DIGIT; - for prime in _PRIME_TABLE { + for prime in _private_prime_table { if rem, err = mod(a, prime); err != nil { return false, err; } if rem == 0 { return true, nil; } } @@ -27,42 +27,3 @@ int_prime_is_divisible :: proc(a: ^Int) -> (res: bool, err: Error) { */ return false, nil; } - - -_PRIME_TABLE := []DIGIT{ - 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, - 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, - 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, - 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, - 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, - 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, - 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, - 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, - - 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, - 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, - 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, - 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, - 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, - 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, - 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, - 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, - - 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, - 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, - 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, - 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, - 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, - 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, - 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, - 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, - - 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, - 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, - 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, - 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, - 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, - 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, - 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, - 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653, -}; \ No newline at end of file From 53bf66ce1ec8708284d2dcf726240fcbbcd90d05 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 7 Aug 2021 23:07:22 +0200 Subject: [PATCH 091/105] big: Prettify `internal_cmp_digit`. --- core/math/big/build.bat | 6 ++--- core/math/big/compare.odin | 1 + core/math/big/internal.odin | 44 ++++++++++++++++--------------------- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index a12e3262a..1921803b5 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,10 @@ @echo off -:odin run . -vet +odin run . -vet : -o:size :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -no-bounds-check :odin build . -build-mode:shared -show-timings -o:size -odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check +:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check :odin build . -build-mode:shared -show-timings -o:speed -python test.py \ No newline at end of file +:python test.py \ No newline at end of file diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin index 1b0aa6877..2581a3c38 100644 --- a/core/math/big/compare.odin +++ b/core/math/big/compare.odin @@ -89,6 +89,7 @@ int_compare_digit :: proc(a: ^Int, b: DIGIT) -> (comparison: int, err: Error) { return #force_inline internal_cmp_digit(a, b), nil; } +int_cmp_digit :: int_compare_digit; /* Compare the magnitude of two `Int`s, unsigned. diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 1ba1c7fa3..93fa04a8d 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1046,40 +1046,34 @@ internal_compare :: proc { internal_int_compare, internal_int_compare_digit, }; internal_cmp :: internal_compare; /* - Compare an `Int` to an unsigned number upto the size of the backing type. + Compare an `Int` to an unsigned number upto `DIGIT & _MASK`. + Returns -1 if `a` < `b`, 0 if `a` == `b` and 1 if `b` > `a`. - Returns -1 if `a` < `b`, 0 if `a` == `b` and 1 if `b` > `a`. - - Expects `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`. + Expects: `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`. */ internal_int_compare_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (comparison: int) { - /* - Compare based on sign. - */ + a_is_negative := #force_inline internal_is_negative(a); - if #force_inline internal_is_negative(a) { return -1; } - - /* - Compare based on magnitude. - */ - if a.used > 1 { return +1; } - - /* - Compare the only digit in `a` to `b`. - */ switch { - case a.digit[0] < b: - return -1; - case a.digit[0] == b: - return 0; - case a.digit[0] > b: - return +1; - case: + /* + Compare based on sign first. + */ + case a_is_negative: return -1; + /* + Then compare on magnitude. + */ + case a.used > 1: return +1; + /* + We have only one digit. Compare it against `b`. + */ + case a.digit[0] < b: return -1; + case a.digit[0] == b: return 0; + case a.digit[0] > b: return +1; /* Unreachable. Just here because Odin complains about a missing return value at the bottom of the proc otherwise. */ - return; + case: return; } } internal_compare_digit :: proc { internal_int_compare_digit, }; From 40b7b9ecdf3aa3c749b1bb1b04ae0637e49e16c7 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 8 Aug 2021 23:41:51 +0200 Subject: [PATCH 092/105] big: Refactor exponents and such. --- core/math/big/basic.odin | 79 +- core/math/big/build.bat | 14 +- core/math/big/common.odin | 1 + core/math/big/example.odin | 8 +- core/math/big/exp_log.odin | 496 ------------- core/math/big/internal.odin | 1364 +++++++++++++---------------------- core/math/big/private.odin | 866 ++++++++++++++++++++++ 7 files changed, 1453 insertions(+), 1375 deletions(-) delete mode 100644 core/math/big/exp_log.odin create mode 100644 core/math/big/private.odin diff --git a/core/math/big/basic.odin b/core/math/big/basic.odin index 755aa51cd..425a5488c 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/basic.odin @@ -326,4 +326,81 @@ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { return #force_inline internal_int_mod_bits(remainder, numerator, bits); } -mod_bits :: proc { int_mod_bits, }; \ No newline at end of file +mod_bits :: proc { int_mod_bits, }; + + +/* + Logs and roots and such. +*/ +int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { + if a == nil { return 0, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return 0, err; } + + return #force_inline internal_int_log(a, base); +} + +digit_log :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { + return #force_inline internal_digit_log(a, base); +} +log :: proc { int_log, digit_log, }; + +/* + Calculate `dest = base^power` using a square-multiply algorithm. +*/ +int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { + if dest == nil || base == nil { return .Invalid_Pointer; } + if err = clear_if_uninitialized(dest, base); err != nil { return err; } + + return #force_inline internal_int_pow(dest, base, power); +} + +/* + Calculate `dest = base^power` using a square-multiply algorithm. +*/ +int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { + if dest == nil { return .Invalid_Pointer; } + + return #force_inline internal_pow(dest, base, power); +} + +pow :: proc { int_pow, int_pow_int, small_pow, }; +exp :: pow; + +small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { + return #force_inline internal_small_pow(base, exponent); +} + +/* + This function is less generic than `root_n`, simpler and faster. +*/ +int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { + if dest == nil || src == nil { return .Invalid_Pointer; } + if err = clear_if_uninitialized(dest, src); err != nil { return err; } + + return #force_inline internal_int_sqrt(dest, src); +} +sqrt :: proc { int_sqrt, }; + + +/* + Find the nth root of an Integer. + Result found such that `(dest)**n <= src` and `(dest+1)**n > src` + + This algorithm uses Newton's approximation `x[i+1] = x[i] - f(x[i])/f'(x[i])`, + which will find the root in `log(n)` time where each step involves a fair bit. +*/ +int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { + /* + Fast path for n == 2. + */ + if n == 2 { return sqrt(dest, src); } + + if dest == nil || src == nil { return .Invalid_Pointer; } + /* + Initialize dest + src if needed. + */ + if err = clear_if_uninitialized(dest, src); err != nil { return err; } + + return #force_inline internal_int_root_n(dest, src, n); +} +root_n :: proc { int_root_n, }; \ No newline at end of file diff --git a/core/math/big/build.bat b/core/math/big/build.bat index 1921803b5..e98ac7015 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,10 +1,8 @@ @echo off -odin run . -vet +:odin run . -vet : -o:size -:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check -:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check -:odin build . -build-mode:shared -show-timings -o:size -:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check -:odin build . -build-mode:shared -show-timings -o:speed - -:python test.py \ No newline at end of file +:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check && python test.py +:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check && python test.py +:odin build . -build-mode:shared -show-timings -o:size && python test.py +odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check && python test.py +:odin build . -build-mode:shared -show-timings -o:speed && python test.py \ No newline at end of file diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 837fc5789..562aa46fa 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -109,6 +109,7 @@ Flags :: bit_set[Flag; u8]; Errors are a strict superset of runtime.Allocation_Error. */ Error :: enum int { + Okay = 0, Out_Of_Memory = 1, Invalid_Pointer = 2, Invalid_Argument = 3, diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 761fa97cb..02671a809 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -75,12 +75,8 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - power_of_two(a, 3112); - fmt.printf("a is power of two: %v\n", internal_is_power_of_two(a)); - sub(a, a, 1); - fmt.printf("a is power of two: %v\n", internal_is_power_of_two(a)); - add(a, a, 1); - fmt.printf("a is power of two: %v\n", internal_is_power_of_two(a)); + err := set(a, 1); + fmt.printf("err: %v\n", err); } main :: proc() { diff --git a/core/math/big/exp_log.odin b/core/math/big/exp_log.odin deleted file mode 100644 index 15dfa0b5a..000000000 --- a/core/math/big/exp_log.odin +++ /dev/null @@ -1,496 +0,0 @@ -package big - -/* - Copyright 2021 Jeroen van Rijn . - Made available under Odin's BSD-2 license. - - An arbitrary precision mathematics 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. -*/ - -int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { - if base < 2 || DIGIT(base) > _DIGIT_MAX { - return -1, .Invalid_Argument; - } - if err = clear_if_uninitialized(a); err != nil { return -1, err; } - if n, _ := is_neg(a); n { return -1, .Math_Domain_Error; } - if z, _ := is_zero(a); z { return -1, .Math_Domain_Error; } - - /* - Fast path for bases that are a power of two. - */ - if is_power_of_two(int(base)) { return _log_power_of_two(a, base); } - - /* - Fast path for `Int`s that fit within a single `DIGIT`. - */ - if a.used == 1 { return log(a.digit[0], DIGIT(base)); } - - return _int_log(a, base); - -} - -log :: proc { int_log, int_log_digit, }; - -/* - Calculate c = a**b using a square-multiply algorithm. -*/ -int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { - power := power; - if err = clear_if_uninitialized(base); err != nil { return err; } - if err = clear_if_uninitialized(dest); err != nil { return err; } - /* - Early outs. - */ - if z, _ := is_zero(base); z { - /* - A zero base is a special case. - */ - if power < 0 { - if err = zero(dest); err != nil { return err; } - return .Math_Domain_Error; - } - if power == 0 { return set(dest, 1); } - if power > 0 { return zero(dest); } - - } - if power < 0 { - /* - Fraction, so we'll return zero. - */ - return zero(dest); - } - switch(power) { - case 0: - /* - Any base to the power zero is one. - */ - return one(dest); - case 1: - /* - Any base to the power one is itself. - */ - return copy(dest, base); - case 2: - return sqr(dest, base); - } - - g := &Int{}; - if err = copy(g, base); err != nil { return err; } - - /* - Set initial result. - */ - if err = set(dest, 1); err != nil { return err; } - - loop: for power > 0 { - /* - If the bit is set, multiply. - */ - if power & 1 != 0 { - if err = mul(dest, g, dest); err != nil { - break loop; - } - } - /* - Square. - */ - if power > 1 { - if err = sqr(g, g); err != nil { - break loop; - } - } - - /* shift to next bit */ - power >>= 1; - } - - destroy(g); - return err; -} - -/* - Calculate c = a**b. -*/ -int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { - base_t := &Int{}; - defer destroy(base_t); - - if err = set(base_t, base); err != nil { return err; } - - return int_pow(dest, base_t, power); -} - -pow :: proc { int_pow, int_pow_int, }; -exp :: pow; - -/* - 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, err: Error) { - base := base; - y: int; - for y = 0; base & 1 == 0; { - y += 1; - base >>= 1; - } - log, err = count_bits(a); - return (log - 1) / y, err; -} - -/* - -*/ -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; -} - -int_log_digit :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { - /* - If the number is smaller than the base, it fits within a fraction. - Therefore, we return 0. - */ - if a < base { - return 0, nil; - } - - /* - If a number equals the base, the log is 1. - */ - if a == base { - return 1, nil; - } - - 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, nil; - } - } - - if bracket_high == N { - return high, nil; - } else { - return low, nil; - } -} - -/* - This function is less generic than `root_n`, simpler and faster. -*/ -int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { - - when true { - if err = clear_if_uninitialized(dest); err != nil { return err; } - if err = clear_if_uninitialized(src); err != nil { return err; } - - /* Must be positive. */ - if src.sign == .Negative { return .Invalid_Argument; } - - /* Easy out. If src is zero, so is dest. */ - if z, _ := is_zero(src); z { return zero(dest); } - - /* Set up temporaries. */ - x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(x, y, t1, t2); - - count: int; - if count, err = count_bits(src); err != nil { return err; } - - a, b := count >> 1, count & 1; - if err = power_of_two(x, a+b); err != nil { return err; } - - for { - /* - y = (x + n//x)//2 - */ - div(t1, src, x); - add(t2, t1, x); - shr(y, t2, 1); - - if c, _ := cmp(y, x); c == 0 || c == 1 { - swap(dest, x); - return nil; - } - swap(x, y); - } - - swap(dest, x); - return err; - } else { - return root_n(dest, src, 2); - } -} -sqrt :: proc { int_sqrt, }; - - -/* - Find the nth root of an Integer. - Result found such that `(dest)**n <= src` and `(dest+1)**n > src` - - This algorithm uses Newton's approximation `x[i+1] = x[i] - f(x[i])/f'(x[i])`, - which will find the root in `log(n)` time where each step involves a fair bit. -*/ -int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { - /* Fast path for n == 2 */ - if n == 2 { return sqrt(dest, src); } - - /* Initialize dest + src if needed. */ - if err = clear_if_uninitialized(dest); err != nil { return err; } - if err = clear_if_uninitialized(src); err != nil { return err; } - - if n < 0 || n > int(_DIGIT_MAX) { - return .Invalid_Argument; - } - - neg: bool; - if n & 1 == 0 { - if neg, err = is_neg(src); neg || err != nil { return .Invalid_Argument; } - } - - /* Set up temporaries. */ - t1, t2, t3, a := &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(t1, t2, t3); - - /* If a is negative fudge the sign but keep track. */ - a.sign = .Zero_or_Positive; - a.used = src.used; - a.digit = src.digit; - - /* - If "n" is larger than INT_MAX it is also larger than - log_2(src) because the bit-length of the "src" is measured - with an int and hence the root is always < 2 (two). - */ - if n > max(int) / 2 { - err = set(dest, 1); - dest.sign = a.sign; - return err; - } - - /* Compute seed: 2^(log_2(src)/n + 2) */ - ilog2: int; - ilog2, err = count_bits(src); - - /* "src" is smaller than max(int), we can cast safely. */ - if ilog2 < n { - err = set(dest, 1); - dest.sign = a.sign; - return err; - } - - ilog2 /= n; - if ilog2 == 0 { - err = set(dest, 1); - dest.sign = a.sign; - return err; - } - - /* Start value must be larger than root. */ - ilog2 += 2; - if err = power_of_two(t2, ilog2); err != nil { return err; } - - c: int; - iterations := 0; - for { - /* t1 = t2 */ - if err = copy(t1, t2); err != nil { return err; } - - /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ - - /* t3 = t1**(b-1) */ - if err = pow(t3, t1, n-1); err != nil { return err; } - - /* numerator */ - /* t2 = t1**b */ - if err = mul(t2, t1, t3); err != nil { return err; } - - /* t2 = t1**b - a */ - if err = sub(t2, t2, a); err != nil { return err; } - - /* denominator */ - /* t3 = t1**(b-1) * b */ - if err = mul(t3, t3, DIGIT(n)); err != nil { return err; } - - /* t3 = (t1**b - a)/(b * t1**(b-1)) */ - if err = div(t3, t2, t3); err != nil { return err; } - if err = sub(t2, t1, t3); err != nil { return err; } - - /* - Number of rounds is at most log_2(root). If it is more it - got stuck, so break out of the loop and do the rest manually. - */ - if ilog2 -= 1; ilog2 == 0 { - break; - } - if c, err = cmp(t1, t2); c == 0 { break; } - iterations += 1; - if iterations == MAX_ITERATIONS_ROOT_N { - return .Max_Iterations_Reached; - } - } - - /* Result can be off by a few so check. */ - /* Loop beneath can overshoot by one if found root is smaller than actual root. */ - - iterations = 0; - for { - if err = pow(t2, t1, n); err != nil { return err; } - - c, err = cmp(t2, a); - if c == 0 { - swap(dest, t1); - return nil; - } else if c == -1 { - if err = add(t1, t1, DIGIT(1)); err != nil { return err; } - } else { - break; - } - - iterations += 1; - if iterations == MAX_ITERATIONS_ROOT_N { - return .Max_Iterations_Reached; - } - } - - iterations = 0; - /* Correct overshoot from above or from recurrence. */ - for { - if err = pow(t2, t1, n); err != nil { return err; } - - c, err = cmp(t2, a); - if c == 1 { - if err = sub(t1, t1, DIGIT(1)); err != nil { return err; } - } else { - break; - } - - iterations += 1; - if iterations == MAX_ITERATIONS_ROOT_N { - return .Max_Iterations_Reached; - } - } - - /* Set the result. */ - swap(dest, t1); - - /* set the sign of the result */ - dest.sign = src.sign; - - return err; -} -root_n :: proc { int_root_n, }; - -/* - Internal implementation of log. -*/ -_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { - bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - - ic, _ := cmp(a, base); - if ic == -1 || ic == 0 { - return 1 if ic == 0 else 0, nil; - } - - if err = set(bi_base, base); err != nil { return -1, err; } - if err = init_multi(bracket_mid, t); err != nil { return -1, err; } - if err = set(bracket_low, 1); err != nil { return -1, err; } - if err = set(bracket_high, base); err != nil { return -1, err; } - - low := 0; high := 1; - - /* - A kind of Giant-step/baby-step algorithm. - Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/ - The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped - for small n. - */ - - for { - /* - Iterate until `a` is bracketed between low + high. - */ - if bc, _ := cmp(bracket_high, a); bc != -1 { - break; - } - - low = high; - if err = copy(bracket_low, bracket_high); err != nil { - destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return -1, err; - } - high <<= 1; - if err = sqr(bracket_high, bracket_high); err != nil { - destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return -1, err; - } - } - - for (high - low) > 1 { - mid := (high + low) >> 1; - - if err = pow(t, bi_base, mid - low); err != nil { - destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return -1, err; - } - - if err = mul(bracket_mid, bracket_low, t); err != nil { - destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return -1, err; - } - mc, _ := cmp(a, bracket_mid); - if mc == -1 { - high = mid; - swap(bracket_mid, bracket_high); - } - if mc == 1 { - low = mid; - swap(bracket_mid, bracket_low); - } - if mc == 0 { - destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return mid, nil; - } - } - - fc, _ := cmp(bracket_high, a); - res = high if fc == 0 else low; - - destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - return; -} \ No newline at end of file diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 93fa04a8d..43d496215 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -612,7 +612,7 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc /* Fast comba? */ // err = s_mp_sqr_comba(a, c); } else { - err = _private_int_sqr(dest, src); + err = #force_inline _private_int_sqr(dest, src); } } else { /* @@ -647,9 +647,9 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ - err = _private_int_mul_comba(dest, src, multiplier, digits); + err = #force_inline _private_int_mul_comba(dest, src, multiplier, digits); } else { - err = _private_int_mul(dest, src, multiplier, digits); + err = #force_inline _private_int_mul(dest, src, multiplier, digits); } } neg := src.sign != multiplier.sign; @@ -659,6 +659,14 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc internal_mul :: proc { internal_int_mul, internal_int_mul_digit, }; +internal_sqr :: proc (dest, src: ^Int) -> (res: Error) { + /* + We call `internal_mul` and not e.g. `_private_int_sqr` because the former + will dispatch to the optimal implementation depending on the source. + */ + return #force_inline internal_mul(dest, src, src); +} + /* divmod. Both the quotient and remainder are optional and may be passed a nil. @@ -838,7 +846,7 @@ internal_mulmod :: proc { internal_int_mulmod, }; remainder = (number * number) % modulus. */ internal_int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { - if err = #force_inline internal_mul(remainder, number, number); err != nil { return err; } + if err = #force_inline internal_sqr(remainder, number); err != nil { return err; } return #force_inline internal_mod(remainder, remainder, modulus); } internal_sqrmod :: proc { internal_int_sqrmod, }; @@ -1069,10 +1077,10 @@ internal_int_compare_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (comparis case a.digit[0] < b: return -1; case a.digit[0] == b: return 0; case a.digit[0] > b: return +1; - /* - Unreachable. - Just here because Odin complains about a missing return value at the bottom of the proc otherwise. - */ + /* + Unreachable. + Just here because Odin complains about a missing return value at the bottom of the proc otherwise. + */ case: return; } } @@ -1111,6 +1119,490 @@ internal_compare_magnitude :: proc { internal_int_compare_magnitude, }; internal_cmp_mag :: internal_compare_magnitude; +/* + ========================= Logs, powers and roots ============================ +*/ + +/* + Returns log_base(a). + Assumes `a` to not be `nil` and have been iniialized. +*/ +internal_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { + if base < 2 || DIGIT(base) > _DIGIT_MAX { return -1, .Invalid_Argument; } + + if internal_is_negative(a) { return -1, .Math_Domain_Error; } + if internal_is_zero(a) { return -1, .Math_Domain_Error; } + + /* + Fast path for bases that are a power of two. + */ + if platform_int_is_power_of_two(int(base)) { return private_log_power_of_two(a, base); } + + /* + Fast path for `Int`s that fit within a single `DIGIT`. + */ + if a.used == 1 { return internal_log(a.digit[0], DIGIT(base)); } + + return private_int_log(a, base); + +} + +/* + Returns log_base(a), where `a` is a DIGIT. +*/ +internal_digit_log :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) { + /* + If the number is smaller than the base, it fits within a fraction. + Therefore, we return 0. + */ + if a < base { return 0, nil; } + + /* + If a number equals the base, the log is 1. + */ + if a == base { return 1, nil; } + + 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 * #force_inline internal_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, nil; + } + } + + if bracket_high == N { + return high, nil; + } else { + return low, nil; + } +} +internal_log :: proc { internal_int_log, internal_digit_log, }; + +/* + Calculate dest = base^power using a square-multiply algorithm. + Assumes `dest` and `base` not to be `nil` and to have been initialized. +*/ +internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { + power := power; + /* + Early outs. + */ + if #force_inline internal_is_zero(base) { + /* + A zero base is a special case. + */ + if power < 0 { + if err = zero(dest); err != nil { return err; } + return .Math_Domain_Error; + } + if power == 0 { return set(dest, 1); } + if power > 0 { return zero(dest); } + + } + if power < 0 { + /* + Fraction, so we'll return zero. + */ + return zero(dest); + } + switch(power) { + case 0: + /* + Any base to the power zero is one. + */ + return one(dest); + case 1: + /* + Any base to the power one is itself. + */ + return copy(dest, base); + case 2: + return #force_inline internal_sqr(dest, base); + } + + g := &Int{}; + if err = copy(g, base); err != nil { return err; } + + /* + Set initial result. + */ + if err = set(dest, 1); err != nil { return err; } + + loop: for power > 0 { + /* + If the bit is set, multiply. + */ + if power & 1 != 0 { + if err = mul(dest, g, dest); err != nil { + break loop; + } + } + /* + Square. + */ + if power > 1 { + if err = #force_inline internal_sqr(g, g); err != nil { + break loop; + } + } + + /* shift to next bit */ + power >>= 1; + } + + destroy(g); + return err; +} + +/* + Calculate `dest = base^power`. + Assumes `dest` not to be `nil` and to have been initialized. +*/ +internal_int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { + base_t := &Int{}; + defer destroy(base_t); + + if err = set(base_t, base); err != nil { return err; } + + return #force_inline internal_int_pow(dest, base_t, power); +} + +internal_pow :: proc { internal_int_pow, internal_int_pow_int, }; +internal_exp :: pow; + +/* + Returns the log2 of an `Int`. + Assumes `a` not to be `nil` and to have been initialized. + Also assumes `base` is a power of two. +*/ +private_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { + base := base; + y: int; + for y = 0; base & 1 == 0; { + y += 1; + base >>= 1; + } + log, err = count_bits(a); + return (log - 1) / y, err; +} + +/* + +*/ +internal_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; +} + +/* + This function is less generic than `root_n`, simpler and faster. + Assumes `dest` and `src` not to be `nil` and to have been initialized. +*/ +internal_int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { + /* + Must be positive. + */ + if #force_inline internal_is_negative(src) { return .Invalid_Argument; } + + /* + Easy out. If src is zero, so is dest. + */ + if #force_inline internal_is_zero(src) { return zero(dest); } + + /* + Set up temporaries. + */ + x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(x, y, t1, t2); + + count: int; + if count, err = count_bits(src); err != nil { return err; } + + a, b := count >> 1, count & 1; + if err = power_of_two(x, a+b); err != nil { return err; } + + for { + /* + y = (x + n // x) // 2 + */ + internal_div(t1, src, x); + internal_add(t2, t1, x); + shr(y, t2, 1); + + if c := internal_cmp(y, x); c == 0 || c == 1 { + swap(dest, x); + return nil; + } + swap(x, y); + } + + swap(dest, x); + return err; +} +internal_sqrt :: proc { internal_int_sqrt, }; + + +/* + Find the nth root of an Integer. + Result found such that `(dest)**n <= src` and `(dest+1)**n > src` + + This algorithm uses Newton's approximation `x[i+1] = x[i] - f(x[i])/f'(x[i])`, + which will find the root in `log(n)` time where each step involves a fair bit. + + Assumes `dest` and `src` not to be `nil` and have been initialized. +*/ +internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { + /* + Fast path for n == 2 + */ + if n == 2 { return #force_inline internal_sqrt(dest, src); } + + if n < 0 || n > int(_DIGIT_MAX) { return .Invalid_Argument; } + + if n & 1 == 0 && #force_inline internal_is_negative(src) { return .Invalid_Argument; } + + /* + Set up temporaries. + */ + t1, t2, t3, a := &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(t1, t2, t3); + + /* + If `src` is negative fudge the sign but keep track. + */ + a.sign = .Zero_or_Positive; + a.used = src.used; + a.digit = src.digit; + + /* + If "n" is larger than INT_MAX it is also larger than + log_2(src) because the bit-length of the "src" is measured + with an int and hence the root is always < 2 (two). + */ + if n > max(int) / 2 { + err = set(dest, 1); + dest.sign = a.sign; + return err; + } + + /* + Compute seed: 2^(log_2(src)/n + 2) + */ + ilog2: int; + ilog2, err = count_bits(src); + + /* + "src" is smaller than max(int), we can cast safely. + */ + if ilog2 < n { + err = set(dest, 1); + dest.sign = a.sign; + return err; + } + + ilog2 /= n; + if ilog2 == 0 { + err = set(dest, 1); + dest.sign = a.sign; + return err; + } + + /* + Start value must be larger than root. + */ + ilog2 += 2; + if err = power_of_two(t2, ilog2); err != nil { return err; } + + c: int; + iterations := 0; + for { + /* t1 = t2 */ + if err = copy(t1, t2); err != nil { return err; } + + /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ + + /* t3 = t1**(b-1) */ + if err = internal_pow(t3, t1, n-1); err != nil { return err; } + + /* numerator */ + /* t2 = t1**b */ + if err = internal_mul(t2, t1, t3); err != nil { return err; } + + /* t2 = t1**b - a */ + if err = internal_sub(t2, t2, a); err != nil { return err; } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if err = internal_mul(t3, t3, DIGIT(n)); err != nil { return err; } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if err = internal_div(t3, t2, t3); err != nil { return err; } + if err = internal_sub(t2, t1, t3); err != nil { return err; } + + /* + Number of rounds is at most log_2(root). If it is more it + got stuck, so break out of the loop and do the rest manually. + */ + if ilog2 -= 1; ilog2 == 0 { + break; + } + if c := internal_cmp(t1, t2); c == 0 { break; } + iterations += 1; + if iterations == MAX_ITERATIONS_ROOT_N { + return .Max_Iterations_Reached; + } + } + + /* Result can be off by a few so check. */ + /* Loop beneath can overshoot by one if found root is smaller than actual root. */ + + iterations = 0; + for { + if err = internal_pow(t2, t1, n); err != nil { return err; } + + c := internal_cmp(t2, a); + if c == 0 { + swap(dest, t1); + return nil; + } else if c == -1 { + if err = internal_add(t1, t1, DIGIT(1)); err != nil { return err; } + } else { + break; + } + + iterations += 1; + if iterations == MAX_ITERATIONS_ROOT_N { + return .Max_Iterations_Reached; + } + } + + iterations = 0; + /* Correct overshoot from above or from recurrence. */ + for { + if err = internal_pow(t2, t1, n); err != nil { return err; } + + c := internal_cmp(t2, a); + if c == 1 { + if err = internal_sub(t1, t1, DIGIT(1)); err != nil { return err; } + } else { + break; + } + + iterations += 1; + if iterations == MAX_ITERATIONS_ROOT_N { + return .Max_Iterations_Reached; + } + } + + /* Set the result. */ + swap(dest, t1); + + /* set the sign of the result */ + dest.sign = src.sign; + + return err; +} +internal_root_n :: proc { internal_int_root_n, }; + +/* + Internal implementation of log. + Assumes `a` not to be `nil` and to have been initialized. +*/ +private_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { + bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + + ic := #force_inline internal_cmp(a, base); + if ic == -1 || ic == 0 { + return 1 if ic == 0 else 0, nil; + } + + if err = set(bi_base, base); err != nil { return -1, err; } + if err = init_multi(bracket_mid, t); err != nil { return -1, err; } + if err = set(bracket_low, 1); err != nil { return -1, err; } + if err = set(bracket_high, base); err != nil { return -1, err; } + + low := 0; high := 1; + + /* + A kind of Giant-step/baby-step algorithm. + Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/ + The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped + for small n. + */ + + for { + /* + Iterate until `a` is bracketed between low + high. + */ + if bc := #force_inline internal_cmp(bracket_high, a); bc != -1 { break; } + + low = high; + if err = copy(bracket_low, bracket_high); err != nil { return -1, err; } + high <<= 1; + if err = #force_inline internal_sqr(bracket_high, bracket_high); err != nil { return -1, err; } + } + + for (high - low) > 1 { + mid := (high + low) >> 1; + + if err = #force_inline internal_pow(t, bi_base, mid - low); err != nil { return -1, err; } + + if err = #force_inline internal_mul(bracket_mid, bracket_low, t); err != nil { return -1, err; } + + mc := #force_inline internal_cmp(a, bracket_mid); + if mc == -1 { + high = mid; + swap(bracket_mid, bracket_high); + } + if mc == 1 { + low = mid; + swap(bracket_mid, bracket_low); + } + if mc == 0 { return mid, nil; } + } + + fc := #force_inline internal_cmp(bracket_high, a); + res = high if fc == 0 else low; + + return; +} + +/* + Other internal helpers +*/ + internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { /* If we don't pass the number of previously used DIGITs, we zero all remaining ones. @@ -1129,864 +1621,8 @@ internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { mem.zero_slice(dest.digit[dest.used:][:zero_count]); } } - internal_zero_unused :: proc { internal_int_zero_unused, }; - /* ========================== End of low-level routines ========================== - - ============================= Private procedures ============================= - - Private procedures used by the above low-level routines follow. - - Don't call these yourself unless you really know what you're doing. - They include implementations that are optimimal for certain ranges of input only. - - These aren't exported for the same reasons. -*/ - - -/* - Multiplies |a| * |b| and only computes upto digs digits of result. - HAC pp. 595, Algorithm 14.12 Modified so you can control how - many digits of output are created. -*/ -_private_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { - /* - Can we use the fast multiplier? - */ - if digits < _WARRAY && min(a.used, b.used) < _MAX_COMBA { - return _private_int_mul_comba(dest, a, b, digits); - } - - /* - Set up temporary output `Int`, which we'll swap for `dest` when done. - */ - - t := &Int{}; - - if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } - t.used = digits; - - /* - Compute the digits of the product directly. - */ - pa := a.used; - for ix := 0; ix < pa; ix += 1 { - /* - Limit ourselves to `digits` DIGITs of output. - */ - pb := min(b.used, digits - ix); - carry := _WORD(0); - iy := 0; - - /* - Compute the column of the output and propagate the carry. - */ - #no_bounds_check for iy = 0; iy < pb; iy += 1 { - /* - Compute the column as a _WORD. - */ - column := _WORD(t.digit[ix + iy]) + _WORD(a.digit[ix]) * _WORD(b.digit[iy]) + carry; - - /* - The new column is the lower part of the result. - */ - t.digit[ix + iy] = DIGIT(column & _WORD(_MASK)); - - /* - Get the carry word from the result. - */ - carry = column >> _DIGIT_BITS; - } - /* - Set carry if it is placed below digits - */ - if ix + iy < digits { - t.digit[ix + pb] = DIGIT(carry); - } - } - - swap(dest, t); - destroy(t); - return clamp(dest); -} - -/* - Fast (comba) multiplier - - This is the fast column-array [comba] multiplier. It is - designed to compute the columns of the product first - then handle the carries afterwards. This has the effect - of making the nested loops that compute the columns very - simple and schedulable on super-scalar processors. - - This has been modified to produce a variable number of - digits of output so if say only a half-product is required - you don't have to compute the upper half (a feature - required for fast Barrett reduction). - - Based on Algorithm 14.12 on pp.595 of HAC. -*/ -_private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { - /* - Set up array. - */ - W: [_WARRAY]DIGIT = ---; - - /* - Grow the destination as required. - */ - if err = grow(dest, digits); err != nil { return err; } - - /* - Number of output digits to produce. - */ - pa := min(digits, a.used + b.used); - - /* - Clear the carry - */ - _W := _WORD(0); - - ix: int; - for ix = 0; ix < pa; ix += 1 { - tx, ty, iy, iz: int; - - /* - Get offsets into the two bignums. - */ - ty = min(b.used - 1, ix); - tx = ix - ty; - - /* - This is the number of times the loop will iterate, essentially. - while (tx++ < a->used && ty-- >= 0) { ... } - */ - - iy = min(a.used - tx, ty + 1); - - /* - Execute loop. - */ - #no_bounds_check for iz = 0; iz < iy; iz += 1 { - _W += _WORD(a.digit[tx + iz]) * _WORD(b.digit[ty - iz]); - } - - /* - Store term. - */ - W[ix] = DIGIT(_W) & _MASK; - - /* - Make next carry. - */ - _W = _W >> _WORD(_DIGIT_BITS); - } - - /* - Setup dest. - */ - old_used := dest.used; - dest.used = pa; - - /* - Now extract the previous digit [below the carry]. - */ - copy_slice(dest.digit[0:], W[:pa]); - - /* - Clear unused digits [that existed in the old copy of dest]. - */ - zero_unused(dest, old_used); - - /* - Adjust dest.used based on leading zeroes. - */ - - return clamp(dest); -} - -/* - Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 -*/ -_private_int_sqr :: proc(dest, src: ^Int) -> (err: Error) { - pa := src.used; - - t := &Int{}; ix, iy: int; - /* - Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. - */ - if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } - t.used = (2 * pa) + 1; - - #no_bounds_check for ix = 0; ix < pa; ix += 1 { - carry := DIGIT(0); - /* - First calculate the digit at 2*ix; calculate double precision result. - */ - r := _WORD(t.digit[ix+ix]) + (_WORD(src.digit[ix]) * _WORD(src.digit[ix])); - - /* - Store lower part in result. - */ - t.digit[ix+ix] = DIGIT(r & _WORD(_MASK)); - /* - Get the carry. - */ - carry = DIGIT(r >> _DIGIT_BITS); - - #no_bounds_check for iy = ix + 1; iy < pa; iy += 1 { - /* - First calculate the product. - */ - r = _WORD(src.digit[ix]) * _WORD(src.digit[iy]); - - /* Now calculate the double precision result. Nóte we use - * addition instead of *2 since it's easier to optimize - */ - r = _WORD(t.digit[ix+iy]) + r + r + _WORD(carry); - - /* - Store lower part. - */ - t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); - - /* - Get carry. - */ - carry = DIGIT(r >> _DIGIT_BITS); - } - /* - Propagate upwards. - */ - #no_bounds_check for carry != 0 { - r = _WORD(t.digit[ix+iy]) + _WORD(carry); - t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); - carry = DIGIT(r >> _WORD(_DIGIT_BITS)); - iy += 1; - } - } - - err = clamp(t); - swap(dest, t); - destroy(t); - return err; -} - -/* - Divide by three (based on routine from MPI and the GMP manual). -*/ -_private_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: Error) { - /* - b = 2^_DIGIT_BITS / 3 - */ - b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); - - q := &Int{}; - if err = grow(q, numerator.used); err != nil { return 0, err; } - q.used = numerator.used; - q.sign = numerator.sign; - - w, t: _WORD; - #no_bounds_check for ix := numerator.used; ix >= 0; ix -= 1 { - w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix]); - if w >= 3 { - /* - Multiply w by [1/3]. - */ - t = (w * b) >> _WORD(_DIGIT_BITS); - - /* - Now subtract 3 * [w/3] from w, to get the remainder. - */ - w -= t+t+t; - - /* - Fixup the remainder as required since the optimization is not exact. - */ - for w >= 3 { - t += 1; - w -= 3; - } - } else { - t = 0; - } - q.digit[ix] = DIGIT(t); - } - remainder = DIGIT(w); - - /* - [optional] store the quotient. - */ - if quotient != nil { - err = clamp(q); - swap(q, quotient); - } - destroy(q); - return remainder, nil; -} - -/* - Signed Integer Division - - c*b + d == a [i.e. a/b, c=quotient, d=remainder], HAC pp.598 Algorithm 14.20 - - Note that the description in HAC is horribly incomplete. - For example, it doesn't consider the case where digits are removed from 'x' in - the inner loop. - - It also doesn't consider the case that y has fewer than three digits, etc. - The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. -*/ -_private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { - // if err = error_if_immutable(quotient, remainder); err != nil { return err; } - // if err = clear_if_uninitialized(quotient, numerator, denominator); err != nil { return err; } - - q, x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(q, x, y, t1, t2); - - if err = grow(q, numerator.used + 2); err != nil { return err; } - q.used = numerator.used + 2; - - if err = init_multi(t1, t2); err != nil { return err; } - if err = copy(x, numerator); err != nil { return err; } - if err = copy(y, denominator); err != nil { return err; } - - /* - Fix the sign. - */ - neg := numerator.sign != denominator.sign; - x.sign = .Zero_or_Positive; - y.sign = .Zero_or_Positive; - - /* - Normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] - */ - norm, _ := count_bits(y); - norm %= _DIGIT_BITS; - - if norm < _DIGIT_BITS - 1 { - norm = (_DIGIT_BITS - 1) - norm; - if err = shl(x, x, norm); err != nil { return err; } - if err = shl(y, y, norm); err != nil { return err; } - } else { - norm = 0; - } - - /* - Note: HAC does 0 based, so if used==5 then it's 0,1,2,3,4, i.e. use 4 - */ - n := x.used - 1; - t := y.used - 1; - - /* - while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } - y = y*b**{n-t} - */ - - if err = shl_digit(y, n - t); err != nil { return err; } - - c, _ := cmp(x, y); - for c != -1 { - q.digit[n - t] += 1; - if err = sub(x, x, y); err != nil { return err; } - c, _ = cmp(x, y); - } - - /* - Reset y by shifting it back down. - */ - shr_digit(y, n - t); - - /* - Step 3. for i from n down to (t + 1). - */ - #no_bounds_check for i := n; i >= (t + 1); i -= 1 { - if (i > x.used) { continue; } - - /* - step 3.1 if xi == yt then set q{i-t-1} to b-1, otherwise set q{i-t-1} to (xi*b + x{i-1})/yt - */ - if x.digit[i] == y.digit[t] { - q.digit[(i - t) - 1] = 1 << (_DIGIT_BITS - 1); - } else { - - tmp := _WORD(x.digit[i]) << _DIGIT_BITS; - tmp |= _WORD(x.digit[i - 1]); - tmp /= _WORD(y.digit[t]); - if tmp > _WORD(_MASK) { - tmp = _WORD(_MASK); - } - q.digit[(i - t) - 1] = DIGIT(tmp & _WORD(_MASK)); - } - - /* while (q{i-t-1} * (yt * b + y{t-1})) > - xi * b**2 + xi-1 * b + xi-2 - - do q{i-t-1} -= 1; - */ - - iter := 0; - - q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] + 1) & _MASK; - #no_bounds_check for { - q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; - - /* - Find left hand. - */ - zero(t1); - t1.digit[0] = ((t - 1) < 0) ? 0 : y.digit[t - 1]; - t1.digit[1] = y.digit[t]; - t1.used = 2; - if err = mul(t1, t1, q.digit[(i - t) - 1]); err != nil { return err; } - - /* - Find right hand. - */ - t2.digit[0] = ((i - 2) < 0) ? 0 : x.digit[i - 2]; - t2.digit[1] = x.digit[i - 1]; /* i >= 1 always holds */ - t2.digit[2] = x.digit[i]; - t2.used = 3; - - if t1_t2, _ := cmp_mag(t1, t2); t1_t2 != 1 { - break; - } - iter += 1; if iter > 100 { return .Max_Iterations_Reached; } - } - - /* - Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} - */ - if err = int_mul_digit(t1, y, q.digit[(i - t) - 1]); err != nil { return err; } - if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } - if err = sub(x, x, t1); err != nil { return err; } - - /* - if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } - */ - if x.sign == .Negative { - if err = copy(t1, y); err != nil { return err; } - if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } - if err = add(x, x, t1); err != nil { return err; } - - q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; - } - } - - /* - Now q is the quotient and x is the remainder, [which we have to normalize] - Get sign before writing to c. - */ - z, _ := is_zero(x); - x.sign = .Zero_or_Positive if z else numerator.sign; - - if quotient != nil { - clamp(q); - swap(q, quotient); - quotient.sign = .Negative if neg else .Zero_or_Positive; - } - - if remainder != nil { - if err = shr(x, x, norm); err != nil { return err; } - swap(x, remainder); - } - - return nil; -} - -/* - Slower bit-bang division... also smaller. -*/ -@(deprecated="Use `_int_div_school`, it's 3.5x faster.") -_private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { - - ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}; - c: int; - - goto_end: for { - if err = one(tq); err != nil { break goto_end; } - - num_bits, _ := count_bits(numerator); - den_bits, _ := count_bits(denominator); - n := num_bits - den_bits; - - if err = abs(ta, numerator); err != nil { break goto_end; } - if err = abs(tb, denominator); err != nil { break goto_end; } - if err = shl(tb, tb, n); err != nil { break goto_end; } - if err = shl(tq, tq, n); err != nil { break goto_end; } - - for n >= 0 { - if c, _ = cmp_mag(ta, tb); c == 0 || c == 1 { - // ta -= tb - if err = sub(ta, ta, tb); err != nil { break goto_end; } - // q += tq - if err = add( q, q, tq); err != nil { break goto_end; } - } - if err = shr1(tb, tb); err != nil { break goto_end; } - if err = shr1(tq, tq); err != nil { break goto_end; } - - n -= 1; - } - - /* - Now q == quotient and ta == remainder. - */ - neg := numerator.sign != denominator.sign; - if quotient != nil { - swap(quotient, q); - z, _ := is_zero(quotient); - quotient.sign = .Negative if neg && !z else .Zero_or_Positive; - } - if remainder != nil { - swap(remainder, ta); - z, _ := is_zero(numerator); - remainder.sign = .Zero_or_Positive if z else numerator.sign; - } - - break goto_end; - } - destroy(ta, tb, tq, q); - return err; -} - - - -/* - Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html -*/ -_private_int_factorial_binary_split :: proc(res: ^Int, n: int) -> (err: Error) { - - inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(inner, outer, start, stop, temp); - - if err = set(inner, 1); err != nil { return err; } - if err = set(outer, 1); err != nil { return err; } - - bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); - - for i := bits_used; i >= 0; i -= 1 { - start := (n >> (uint(i) + 1)) + 1 | 1; - stop := (n >> uint(i)) + 1 | 1; - if err = _private_int_recursive_product(temp, start, stop); err != nil { return err; } - if err = internal_mul(inner, inner, temp); err != nil { return err; } - if err = internal_mul(outer, outer, inner); err != nil { return err; } - } - shift := n - intrinsics.count_ones(n); - - return shl(res, outer, int(shift)); -} - -/* - Recursive product used by binary split factorial algorithm. -*/ -_private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int(0)) -> (err: Error) { - t1, t2 := &Int{}, &Int{}; - defer destroy(t1, t2); - - if level > FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } - - num_factors := (stop - start) >> 1; - if num_factors == 2 { - if err = set(t1, start); err != nil { return err; } - when true { - if err = grow(t2, t1.used + 1); err != nil { return err; } - if err = internal_add(t2, t1, 2); err != nil { return err; } - } else { - if err = add(t2, t1, 2); err != nil { return err; } - } - return internal_mul(res, t1, t2); - } - - if num_factors > 1 { - mid := (start + num_factors) | 1; - if err = _private_int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } - if err = _private_int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } - return internal_mul(res, t1, t2); - } - - if num_factors == 1 { return #force_inline set(res, start); } - - return #force_inline set(res, 1); -} - -/* - Internal function computing both GCD using the binary method, - and, if target isn't `nil`, also LCM. - - Expects the `a` and `b` to have been initialized - and one or both of `res_gcd` or `res_lcm` not to be `nil`. - - If both `a` and `b` are zero, return zero. - If either `a` or `b`, return the other one. - - The `gcd` and `lcm` wrappers have already done this test, - but `gcd_lcm` wouldn't have, so we still need to perform it. - - If neither result is wanted, we have nothing to do. -*/ -_private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { - if res_gcd == nil && res_lcm == nil { return nil; } - - /* - We need a temporary because `res_gcd` is allowed to be `nil`. - */ - if a.used == 0 && b.used == 0 { - /* - GCD(0, 0) and LCM(0, 0) are both 0. - */ - if res_gcd != nil { - if err = zero(res_gcd); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } else if a.used == 0 { - /* - We can early out with GCD = B and LCM = 0 - */ - if res_gcd != nil { - if err = abs(res_gcd, b); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } else if b.used == 0 { - /* - We can early out with GCD = A and LCM = 0 - */ - if res_gcd != nil { - if err = abs(res_gcd, a); err != nil { return err; } - } - if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } - } - return nil; - } - - temp_gcd_res := &Int{}; - defer destroy(temp_gcd_res); - - /* - If neither `a` or `b` was zero, we need to compute `gcd`. - Get copies of `a` and `b` we can modify. - */ - u, v := &Int{}, &Int{}; - defer destroy(u, v); - if err = copy(u, a); err != nil { return err; } - if err = copy(v, b); err != nil { return err; } - - /* - Must be positive for the remainder of the algorithm. - */ - u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive; - - /* - B1. Find the common power of two for `u` and `v`. - */ - u_lsb, _ := count_lsb(u); - v_lsb, _ := count_lsb(v); - k := min(u_lsb, v_lsb); - - if k > 0 { - /* - Divide the power of two out. - */ - if err = shr(u, u, k); err != nil { return err; } - if err = shr(v, v, k); err != nil { return err; } - } - - /* - Divide any remaining factors of two out. - */ - if u_lsb != k { - if err = shr(u, u, u_lsb - k); err != nil { return err; } - } - if v_lsb != k { - if err = shr(v, v, v_lsb - k); err != nil { return err; } - } - - for v.used != 0 { - /* - Make sure `v` is the largest. - */ - if c, _ := cmp_mag(u, v); c == 1 { - /* - Swap `u` and `v` to make sure `v` is >= `u`. - */ - swap(u, v); - } - - /* - Subtract smallest from largest. - */ - if err = internal_sub(v, v, u); err != nil { return err; } - - /* - Divide out all factors of two. - */ - b, _ := count_lsb(v); - if err = shr(v, v, b); err != nil { return err; } - } - - /* - Multiply by 2**k which we divided out at the beginning. - */ - if err = shl(temp_gcd_res, u, k); err != nil { return err; } - temp_gcd_res.sign = .Zero_or_Positive; - - /* - We've computed `gcd`, either the long way, or because one of the inputs was zero. - If we don't want `lcm`, we're done. - */ - if res_lcm == nil { - swap(temp_gcd_res, res_gcd); - return nil; - } - - /* - Computes least common multiple as `|a*b|/gcd(a,b)` - Divide the smallest by the GCD. - */ - if c, _ := cmp_mag(a, b); c == -1 { - /* - Store quotient in `t2` such that `t2 * b` is the LCM. - */ - if err = internal_div(res_lcm, a, temp_gcd_res); err != nil { return err; } - err = internal_mul(res_lcm, res_lcm, b); - } else { - /* - Store quotient in `t2` such that `t2 * a` is the LCM. - */ - if err = internal_div(res_lcm, a, temp_gcd_res); err != nil { return err; } - err = internal_mul(res_lcm, res_lcm, b); - } - - if res_gcd != nil { - swap(temp_gcd_res, res_gcd); - } - - /* - Fix the sign to positive and return. - */ - res_lcm.sign = .Zero_or_Positive; - return err; -} - -/* - ======================== End of private procedures ======================= - - =============================== Private tables =============================== - - Tables used by `internal_*` and `_*`. -*/ - -_private_prime_table := []DIGIT{ - 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, - 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, - 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, - 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, - 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, - 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, - 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, - 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, - - 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, - 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, - 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, - 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, - 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, - 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, - 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, - 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, - - 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, - 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, - 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, - 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, - 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, - 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, - 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, - 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, - - 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, - 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, - 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, - 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, - 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, - 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, - 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, - 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653, -}; - -when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { - _factorial_table := [35]_WORD{ -/* f(00): */ 1, -/* f(01): */ 1, -/* f(02): */ 2, -/* f(03): */ 6, -/* f(04): */ 24, -/* f(05): */ 120, -/* f(06): */ 720, -/* f(07): */ 5_040, -/* f(08): */ 40_320, -/* f(09): */ 362_880, -/* f(10): */ 3_628_800, -/* f(11): */ 39_916_800, -/* f(12): */ 479_001_600, -/* f(13): */ 6_227_020_800, -/* f(14): */ 87_178_291_200, -/* f(15): */ 1_307_674_368_000, -/* f(16): */ 20_922_789_888_000, -/* f(17): */ 355_687_428_096_000, -/* f(18): */ 6_402_373_705_728_000, -/* f(19): */ 121_645_100_408_832_000, -/* f(20): */ 2_432_902_008_176_640_000, -/* f(21): */ 51_090_942_171_709_440_000, -/* f(22): */ 1_124_000_727_777_607_680_000, -/* f(23): */ 25_852_016_738_884_976_640_000, -/* f(24): */ 620_448_401_733_239_439_360_000, -/* f(25): */ 15_511_210_043_330_985_984_000_000, -/* f(26): */ 403_291_461_126_605_635_584_000_000, -/* f(27): */ 10_888_869_450_418_352_160_768_000_000, -/* f(28): */ 304_888_344_611_713_860_501_504_000_000, -/* f(29): */ 8_841_761_993_739_701_954_543_616_000_000, -/* f(30): */ 265_252_859_812_191_058_636_308_480_000_000, -/* f(31): */ 8_222_838_654_177_922_817_725_562_880_000_000, -/* f(32): */ 263_130_836_933_693_530_167_218_012_160_000_000, -/* f(33): */ 8_683_317_618_811_886_495_518_194_401_280_000_000, -/* f(34): */ 295_232_799_039_604_140_847_618_609_643_520_000_000, - }; -} else { - _factorial_table := [21]_WORD{ -/* f(00): */ 1, -/* f(01): */ 1, -/* f(02): */ 2, -/* f(03): */ 6, -/* f(04): */ 24, -/* f(05): */ 120, -/* f(06): */ 720, -/* f(07): */ 5_040, -/* f(08): */ 40_320, -/* f(09): */ 362_880, -/* f(10): */ 3_628_800, -/* f(11): */ 39_916_800, -/* f(12): */ 479_001_600, -/* f(13): */ 6_227_020_800, -/* f(14): */ 87_178_291_200, -/* f(15): */ 1_307_674_368_000, -/* f(16): */ 20_922_789_888_000, -/* f(17): */ 355_687_428_096_000, -/* f(18): */ 6_402_373_705_728_000, -/* f(19): */ 121_645_100_408_832_000, -/* f(20): */ 2_432_902_008_176_640_000, - }; -}; - -/* - ========================= End of private tables ======================== */ \ No newline at end of file diff --git a/core/math/big/private.odin b/core/math/big/private.odin new file mode 100644 index 000000000..e9db6e3ef --- /dev/null +++ b/core/math/big/private.odin @@ -0,0 +1,866 @@ +package big + +/* + Copyright 2021 Jeroen van Rijn . + Made available under Odin's BSD-2 license. + + An arbitrary precision mathematics 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. + + ============================= Private procedures ============================= + + Private procedures used by the above low-level routines follow. + + Don't call these yourself unless you really know what you're doing. + They include implementations that are optimimal for certain ranges of input only. + + These aren't exported for the same reasons. +*/ + +import "core:intrinsics" + +/* + Multiplies |a| * |b| and only computes upto digs digits of result. + HAC pp. 595, Algorithm 14.12 Modified so you can control how + many digits of output are created. +*/ +_private_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { + /* + Can we use the fast multiplier? + */ + if digits < _WARRAY && min(a.used, b.used) < _MAX_COMBA { + return #force_inline _private_int_mul_comba(dest, a, b, digits); + } + + /* + Set up temporary output `Int`, which we'll swap for `dest` when done. + */ + + t := &Int{}; + + if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } + t.used = digits; + + /* + Compute the digits of the product directly. + */ + pa := a.used; + for ix := 0; ix < pa; ix += 1 { + /* + Limit ourselves to `digits` DIGITs of output. + */ + pb := min(b.used, digits - ix); + carry := _WORD(0); + iy := 0; + + /* + Compute the column of the output and propagate the carry. + */ + #no_bounds_check for iy = 0; iy < pb; iy += 1 { + /* + Compute the column as a _WORD. + */ + column := _WORD(t.digit[ix + iy]) + _WORD(a.digit[ix]) * _WORD(b.digit[iy]) + carry; + + /* + The new column is the lower part of the result. + */ + t.digit[ix + iy] = DIGIT(column & _WORD(_MASK)); + + /* + Get the carry word from the result. + */ + carry = column >> _DIGIT_BITS; + } + /* + Set carry if it is placed below digits + */ + if ix + iy < digits { + t.digit[ix + pb] = DIGIT(carry); + } + } + + swap(dest, t); + destroy(t); + return clamp(dest); +} + +/* + Fast (comba) multiplier + + This is the fast column-array [comba] multiplier. It is + designed to compute the columns of the product first + then handle the carries afterwards. This has the effect + of making the nested loops that compute the columns very + simple and schedulable on super-scalar processors. + + This has been modified to produce a variable number of + digits of output so if say only a half-product is required + you don't have to compute the upper half (a feature + required for fast Barrett reduction). + + Based on Algorithm 14.12 on pp.595 of HAC. +*/ +_private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { + /* + Set up array. + */ + W: [_WARRAY]DIGIT = ---; + + /* + Grow the destination as required. + */ + if err = grow(dest, digits); err != nil { return err; } + + /* + Number of output digits to produce. + */ + pa := min(digits, a.used + b.used); + + /* + Clear the carry + */ + _W := _WORD(0); + + ix: int; + for ix = 0; ix < pa; ix += 1 { + tx, ty, iy, iz: int; + + /* + Get offsets into the two bignums. + */ + ty = min(b.used - 1, ix); + tx = ix - ty; + + /* + This is the number of times the loop will iterate, essentially. + while (tx++ < a->used && ty-- >= 0) { ... } + */ + + iy = min(a.used - tx, ty + 1); + + /* + Execute loop. + */ + #no_bounds_check for iz = 0; iz < iy; iz += 1 { + _W += _WORD(a.digit[tx + iz]) * _WORD(b.digit[ty - iz]); + } + + /* + Store term. + */ + W[ix] = DIGIT(_W) & _MASK; + + /* + Make next carry. + */ + _W = _W >> _WORD(_DIGIT_BITS); + } + + /* + Setup dest. + */ + old_used := dest.used; + dest.used = pa; + + /* + Now extract the previous digit [below the carry]. + */ + copy_slice(dest.digit[0:], W[:pa]); + + /* + Clear unused digits [that existed in the old copy of dest]. + */ + zero_unused(dest, old_used); + + /* + Adjust dest.used based on leading zeroes. + */ + + return clamp(dest); +} + +/* + Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 + Assumes `dest` and `src` to not be `nil`, and `src` to have been initialized. +*/ +_private_int_sqr :: proc(dest, src: ^Int) -> (err: Error) { + pa := src.used; + + t := &Int{}; ix, iy: int; + /* + Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. + */ + if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } + t.used = (2 * pa) + 1; + + #no_bounds_check for ix = 0; ix < pa; ix += 1 { + carry := DIGIT(0); + /* + First calculate the digit at 2*ix; calculate double precision result. + */ + r := _WORD(t.digit[ix+ix]) + (_WORD(src.digit[ix]) * _WORD(src.digit[ix])); + + /* + Store lower part in result. + */ + t.digit[ix+ix] = DIGIT(r & _WORD(_MASK)); + /* + Get the carry. + */ + carry = DIGIT(r >> _DIGIT_BITS); + + #no_bounds_check for iy = ix + 1; iy < pa; iy += 1 { + /* + First calculate the product. + */ + r = _WORD(src.digit[ix]) * _WORD(src.digit[iy]); + + /* Now calculate the double precision result. Nóte we use + * addition instead of *2 since it's easier to optimize + */ + r = _WORD(t.digit[ix+iy]) + r + r + _WORD(carry); + + /* + Store lower part. + */ + t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); + + /* + Get carry. + */ + carry = DIGIT(r >> _DIGIT_BITS); + } + /* + Propagate upwards. + */ + #no_bounds_check for carry != 0 { + r = _WORD(t.digit[ix+iy]) + _WORD(carry); + t.digit[ix+iy] = DIGIT(r & _WORD(_MASK)); + carry = DIGIT(r >> _WORD(_DIGIT_BITS)); + iy += 1; + } + } + + err = clamp(t); + swap(dest, t); + destroy(t); + return err; +} + +/* + Divide by three (based on routine from MPI and the GMP manual). +*/ +_private_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: Error) { + /* + b = 2^_DIGIT_BITS / 3 + */ + b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); + + q := &Int{}; + if err = grow(q, numerator.used); err != nil { return 0, err; } + q.used = numerator.used; + q.sign = numerator.sign; + + w, t: _WORD; + #no_bounds_check for ix := numerator.used; ix >= 0; ix -= 1 { + w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix]); + if w >= 3 { + /* + Multiply w by [1/3]. + */ + t = (w * b) >> _WORD(_DIGIT_BITS); + + /* + Now subtract 3 * [w/3] from w, to get the remainder. + */ + w -= t+t+t; + + /* + Fixup the remainder as required since the optimization is not exact. + */ + for w >= 3 { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.digit[ix] = DIGIT(t); + } + remainder = DIGIT(w); + + /* + [optional] store the quotient. + */ + if quotient != nil { + err = clamp(q); + swap(q, quotient); + } + destroy(q); + return remainder, nil; +} + +/* + Signed Integer Division + + c*b + d == a [i.e. a/b, c=quotient, d=remainder], HAC pp.598 Algorithm 14.20 + + Note that the description in HAC is horribly incomplete. + For example, it doesn't consider the case where digits are removed from 'x' in + the inner loop. + + It also doesn't consider the case that y has fewer than three digits, etc. + The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. +*/ +_private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { + // if err = error_if_immutable(quotient, remainder); err != nil { return err; } + // if err = clear_if_uninitialized(quotient, numerator, denominator); err != nil { return err; } + + q, x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(q, x, y, t1, t2); + + if err = grow(q, numerator.used + 2); err != nil { return err; } + q.used = numerator.used + 2; + + if err = init_multi(t1, t2); err != nil { return err; } + if err = copy(x, numerator); err != nil { return err; } + if err = copy(y, denominator); err != nil { return err; } + + /* + Fix the sign. + */ + neg := numerator.sign != denominator.sign; + x.sign = .Zero_or_Positive; + y.sign = .Zero_or_Positive; + + /* + Normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] + */ + norm, _ := count_bits(y); + norm %= _DIGIT_BITS; + + if norm < _DIGIT_BITS - 1 { + norm = (_DIGIT_BITS - 1) - norm; + if err = shl(x, x, norm); err != nil { return err; } + if err = shl(y, y, norm); err != nil { return err; } + } else { + norm = 0; + } + + /* + Note: HAC does 0 based, so if used==5 then it's 0,1,2,3,4, i.e. use 4 + */ + n := x.used - 1; + t := y.used - 1; + + /* + while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } + y = y*b**{n-t} + */ + + if err = shl_digit(y, n - t); err != nil { return err; } + + c, _ := cmp(x, y); + for c != -1 { + q.digit[n - t] += 1; + if err = sub(x, x, y); err != nil { return err; } + c, _ = cmp(x, y); + } + + /* + Reset y by shifting it back down. + */ + shr_digit(y, n - t); + + /* + Step 3. for i from n down to (t + 1). + */ + #no_bounds_check for i := n; i >= (t + 1); i -= 1 { + if (i > x.used) { continue; } + + /* + step 3.1 if xi == yt then set q{i-t-1} to b-1, otherwise set q{i-t-1} to (xi*b + x{i-1})/yt + */ + if x.digit[i] == y.digit[t] { + q.digit[(i - t) - 1] = 1 << (_DIGIT_BITS - 1); + } else { + + tmp := _WORD(x.digit[i]) << _DIGIT_BITS; + tmp |= _WORD(x.digit[i - 1]); + tmp /= _WORD(y.digit[t]); + if tmp > _WORD(_MASK) { + tmp = _WORD(_MASK); + } + q.digit[(i - t) - 1] = DIGIT(tmp & _WORD(_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + + iter := 0; + + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] + 1) & _MASK; + #no_bounds_check for { + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; + + /* + Find left hand. + */ + zero(t1); + t1.digit[0] = ((t - 1) < 0) ? 0 : y.digit[t - 1]; + t1.digit[1] = y.digit[t]; + t1.used = 2; + if err = mul(t1, t1, q.digit[(i - t) - 1]); err != nil { return err; } + + /* + Find right hand. + */ + t2.digit[0] = ((i - 2) < 0) ? 0 : x.digit[i - 2]; + t2.digit[1] = x.digit[i - 1]; /* i >= 1 always holds */ + t2.digit[2] = x.digit[i]; + t2.used = 3; + + if t1_t2, _ := cmp_mag(t1, t2); t1_t2 != 1 { + break; + } + iter += 1; if iter > 100 { return .Max_Iterations_Reached; } + } + + /* + Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} + */ + if err = int_mul_digit(t1, y, q.digit[(i - t) - 1]); err != nil { return err; } + if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = sub(x, x, t1); err != nil { return err; } + + /* + if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } + */ + if x.sign == .Negative { + if err = copy(t1, y); err != nil { return err; } + if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = add(x, x, t1); err != nil { return err; } + + q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; + } + } + + /* + Now q is the quotient and x is the remainder, [which we have to normalize] + Get sign before writing to c. + */ + z, _ := is_zero(x); + x.sign = .Zero_or_Positive if z else numerator.sign; + + if quotient != nil { + clamp(q); + swap(q, quotient); + quotient.sign = .Negative if neg else .Zero_or_Positive; + } + + if remainder != nil { + if err = shr(x, x, norm); err != nil { return err; } + swap(x, remainder); + } + + return nil; +} + +/* + Slower bit-bang division... also smaller. +*/ +@(deprecated="Use `_int_div_school`, it's 3.5x faster.") +_private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { + + ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}; + c: int; + + goto_end: for { + if err = one(tq); err != nil { break goto_end; } + + num_bits, _ := count_bits(numerator); + den_bits, _ := count_bits(denominator); + n := num_bits - den_bits; + + if err = abs(ta, numerator); err != nil { break goto_end; } + if err = abs(tb, denominator); err != nil { break goto_end; } + if err = shl(tb, tb, n); err != nil { break goto_end; } + if err = shl(tq, tq, n); err != nil { break goto_end; } + + for n >= 0 { + if c, _ = cmp_mag(ta, tb); c == 0 || c == 1 { + // ta -= tb + if err = sub(ta, ta, tb); err != nil { break goto_end; } + // q += tq + if err = add( q, q, tq); err != nil { break goto_end; } + } + if err = shr1(tb, tb); err != nil { break goto_end; } + if err = shr1(tq, tq); err != nil { break goto_end; } + + n -= 1; + } + + /* + Now q == quotient and ta == remainder. + */ + neg := numerator.sign != denominator.sign; + if quotient != nil { + swap(quotient, q); + z, _ := is_zero(quotient); + quotient.sign = .Negative if neg && !z else .Zero_or_Positive; + } + if remainder != nil { + swap(remainder, ta); + z, _ := is_zero(numerator); + remainder.sign = .Zero_or_Positive if z else numerator.sign; + } + + break goto_end; + } + destroy(ta, tb, tq, q); + return err; +} + + + +/* + Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html +*/ +_private_int_factorial_binary_split :: proc(res: ^Int, n: int) -> (err: Error) { + + inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(inner, outer, start, stop, temp); + + if err = set(inner, 1); err != nil { return err; } + if err = set(outer, 1); err != nil { return err; } + + bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); + + for i := bits_used; i >= 0; i -= 1 { + start := (n >> (uint(i) + 1)) + 1 | 1; + stop := (n >> uint(i)) + 1 | 1; + if err = _private_int_recursive_product(temp, start, stop); err != nil { return err; } + if err = internal_mul(inner, inner, temp); err != nil { return err; } + if err = internal_mul(outer, outer, inner); err != nil { return err; } + } + shift := n - intrinsics.count_ones(n); + + return shl(res, outer, int(shift)); +} + +/* + Recursive product used by binary split factorial algorithm. +*/ +_private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int(0)) -> (err: Error) { + t1, t2 := &Int{}, &Int{}; + defer destroy(t1, t2); + + if level > FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } + + num_factors := (stop - start) >> 1; + if num_factors == 2 { + if err = set(t1, start); err != nil { return err; } + when true { + if err = grow(t2, t1.used + 1); err != nil { return err; } + if err = internal_add(t2, t1, 2); err != nil { return err; } + } else { + if err = add(t2, t1, 2); err != nil { return err; } + } + return internal_mul(res, t1, t2); + } + + if num_factors > 1 { + mid := (start + num_factors) | 1; + if err = _private_int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } + if err = _private_int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } + return internal_mul(res, t1, t2); + } + + if num_factors == 1 { return #force_inline set(res, start); } + + return #force_inline set(res, 1); +} + +/* + Internal function computing both GCD using the binary method, + and, if target isn't `nil`, also LCM. + + Expects the `a` and `b` to have been initialized + and one or both of `res_gcd` or `res_lcm` not to be `nil`. + + If both `a` and `b` are zero, return zero. + If either `a` or `b`, return the other one. + + The `gcd` and `lcm` wrappers have already done this test, + but `gcd_lcm` wouldn't have, so we still need to perform it. + + If neither result is wanted, we have nothing to do. +*/ +_private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { + if res_gcd == nil && res_lcm == nil { return nil; } + + /* + We need a temporary because `res_gcd` is allowed to be `nil`. + */ + if a.used == 0 && b.used == 0 { + /* + GCD(0, 0) and LCM(0, 0) are both 0. + */ + if res_gcd != nil { + if err = zero(res_gcd); err != nil { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != nil { return err; } + } + return nil; + } else if a.used == 0 { + /* + We can early out with GCD = B and LCM = 0 + */ + if res_gcd != nil { + if err = abs(res_gcd, b); err != nil { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != nil { return err; } + } + return nil; + } else if b.used == 0 { + /* + We can early out with GCD = A and LCM = 0 + */ + if res_gcd != nil { + if err = abs(res_gcd, a); err != nil { return err; } + } + if res_lcm != nil { + if err = zero(res_lcm); err != nil { return err; } + } + return nil; + } + + temp_gcd_res := &Int{}; + defer destroy(temp_gcd_res); + + /* + If neither `a` or `b` was zero, we need to compute `gcd`. + Get copies of `a` and `b` we can modify. + */ + u, v := &Int{}, &Int{}; + defer destroy(u, v); + if err = copy(u, a); err != nil { return err; } + if err = copy(v, b); err != nil { return err; } + + /* + Must be positive for the remainder of the algorithm. + */ + u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive; + + /* + B1. Find the common power of two for `u` and `v`. + */ + u_lsb, _ := count_lsb(u); + v_lsb, _ := count_lsb(v); + k := min(u_lsb, v_lsb); + + if k > 0 { + /* + Divide the power of two out. + */ + if err = shr(u, u, k); err != nil { return err; } + if err = shr(v, v, k); err != nil { return err; } + } + + /* + Divide any remaining factors of two out. + */ + if u_lsb != k { + if err = shr(u, u, u_lsb - k); err != nil { return err; } + } + if v_lsb != k { + if err = shr(v, v, v_lsb - k); err != nil { return err; } + } + + for v.used != 0 { + /* + Make sure `v` is the largest. + */ + if c, _ := cmp_mag(u, v); c == 1 { + /* + Swap `u` and `v` to make sure `v` is >= `u`. + */ + swap(u, v); + } + + /* + Subtract smallest from largest. + */ + if err = internal_sub(v, v, u); err != nil { return err; } + + /* + Divide out all factors of two. + */ + b, _ := count_lsb(v); + if err = shr(v, v, b); err != nil { return err; } + } + + /* + Multiply by 2**k which we divided out at the beginning. + */ + if err = shl(temp_gcd_res, u, k); err != nil { return err; } + temp_gcd_res.sign = .Zero_or_Positive; + + /* + We've computed `gcd`, either the long way, or because one of the inputs was zero. + If we don't want `lcm`, we're done. + */ + if res_lcm == nil { + swap(temp_gcd_res, res_gcd); + return nil; + } + + /* + Computes least common multiple as `|a*b|/gcd(a,b)` + Divide the smallest by the GCD. + */ + if c, _ := cmp_mag(a, b); c == -1 { + /* + Store quotient in `t2` such that `t2 * b` is the LCM. + */ + if err = internal_div(res_lcm, a, temp_gcd_res); err != nil { return err; } + err = internal_mul(res_lcm, res_lcm, b); + } else { + /* + Store quotient in `t2` such that `t2 * a` is the LCM. + */ + if err = internal_div(res_lcm, a, temp_gcd_res); err != nil { return err; } + err = internal_mul(res_lcm, res_lcm, b); + } + + if res_gcd != nil { + swap(temp_gcd_res, res_gcd); + } + + /* + Fix the sign to positive and return. + */ + res_lcm.sign = .Zero_or_Positive; + return err; +} + +/* + ======================== End of private procedures ======================= + + =============================== Private tables =============================== + + Tables used by `internal_*` and `_*`. +*/ + +_private_prime_table := []DIGIT{ + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653, +}; + +when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) { + _factorial_table := [35]_WORD{ +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, +/* f(21): */ 51_090_942_171_709_440_000, +/* f(22): */ 1_124_000_727_777_607_680_000, +/* f(23): */ 25_852_016_738_884_976_640_000, +/* f(24): */ 620_448_401_733_239_439_360_000, +/* f(25): */ 15_511_210_043_330_985_984_000_000, +/* f(26): */ 403_291_461_126_605_635_584_000_000, +/* f(27): */ 10_888_869_450_418_352_160_768_000_000, +/* f(28): */ 304_888_344_611_713_860_501_504_000_000, +/* f(29): */ 8_841_761_993_739_701_954_543_616_000_000, +/* f(30): */ 265_252_859_812_191_058_636_308_480_000_000, +/* f(31): */ 8_222_838_654_177_922_817_725_562_880_000_000, +/* f(32): */ 263_130_836_933_693_530_167_218_012_160_000_000, +/* f(33): */ 8_683_317_618_811_886_495_518_194_401_280_000_000, +/* f(34): */ 295_232_799_039_604_140_847_618_609_643_520_000_000, + }; +} else { + _factorial_table := [21]_WORD{ +/* f(00): */ 1, +/* f(01): */ 1, +/* f(02): */ 2, +/* f(03): */ 6, +/* f(04): */ 24, +/* f(05): */ 120, +/* f(06): */ 720, +/* f(07): */ 5_040, +/* f(08): */ 40_320, +/* f(09): */ 362_880, +/* f(10): */ 3_628_800, +/* f(11): */ 39_916_800, +/* f(12): */ 479_001_600, +/* f(13): */ 6_227_020_800, +/* f(14): */ 87_178_291_200, +/* f(15): */ 1_307_674_368_000, +/* f(16): */ 20_922_789_888_000, +/* f(17): */ 355_687_428_096_000, +/* f(18): */ 6_402_373_705_728_000, +/* f(19): */ 121_645_100_408_832_000, +/* f(20): */ 2_432_902_008_176_640_000, + }; +}; + +/* + ========================= End of private tables ======================== +*/ \ No newline at end of file From 6d34a8344a73b0dee63af686b3059ae41cb81889 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 9 Aug 2021 17:24:29 +0200 Subject: [PATCH 093/105] big: Refactor helpers. --- core/math/big/compare.odin | 102 ---- core/math/big/helpers.odin | 25 +- core/math/big/internal.odin | 662 +++++++++++++++++++++- core/math/big/private.odin | 2 +- core/math/big/{basic.odin => public.odin} | 90 ++- core/math/big/test.py | 6 + 6 files changed, 768 insertions(+), 119 deletions(-) delete mode 100644 core/math/big/compare.odin rename core/math/big/{basic.odin => public.odin} (80%) diff --git a/core/math/big/compare.odin b/core/math/big/compare.odin deleted file mode 100644 index 2581a3c38..000000000 --- a/core/math/big/compare.odin +++ /dev/null @@ -1,102 +0,0 @@ -package big - -/* - Copyright 2021 Jeroen van Rijn . - Made available under Odin's BSD-2 license. - - An arbitrary precision mathematics 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 various comparison routines. - - We essentially just check if params are initialized before punting to the `internal_*` versions. - This has the side benefit of being able to add additional characteristics to numbers, like NaN, - and keep support for that contained. -*/ - -import "core:intrinsics" - -int_is_initialized :: proc(a: ^Int) -> bool { - if a == nil { return false; } - - return #force_inline internal_int_is_initialized(a); -} - -int_is_zero :: proc(a: ^Int) -> (zero: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } - if err = clear_if_uninitialized(a); err != nil { return false, err; } - - return #force_inline internal_is_zero(a), nil; -} - -int_is_positive :: proc(a: ^Int) -> (positive: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } - if err = clear_if_uninitialized(a); err != nil { return false, err; } - - return #force_inline internal_is_positive(a), nil; -} - -int_is_negative :: proc(a: ^Int) -> (negative: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } - if err = clear_if_uninitialized(a); err != nil { return false, err; } - - return #force_inline internal_is_negative(a), nil; -} - -int_is_even :: proc(a: ^Int) -> (even: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } - if err = clear_if_uninitialized(a); err != nil { return false, err; } - - return #force_inline internal_is_even(a), nil; -} - -int_is_odd :: proc(a: ^Int) -> (odd: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } - if err = clear_if_uninitialized(a); err != nil { return false, err; } - - return #force_inline internal_is_odd(a), nil; -} - -platform_int_is_power_of_two :: #force_inline proc(a: int) -> bool { - return ((a) != 0) && (((a) & ((a) - 1)) == 0); -} - -int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } - if err = clear_if_uninitialized(a); err != nil { return false, err; } - - return #force_inline internal_is_power_of_two(a), nil; -} - -/* - Compare two `Int`s, signed. -*/ -int_compare :: proc(a, b: ^Int) -> (comparison: int, err: Error) { - if a == nil || b == nil { return 0, .Invalid_Pointer; } - if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } - - return #force_inline internal_cmp(a, b), nil; -} -int_cmp :: int_compare; - -/* - Compare an `Int` to an unsigned number upto the size of the backing type. -*/ -int_compare_digit :: proc(a: ^Int, b: DIGIT) -> (comparison: int, err: Error) { - if a == nil { return 0, .Invalid_Pointer; } - if err = clear_if_uninitialized(a); err != nil { return 0, err; } - - return #force_inline internal_cmp_digit(a, b), nil; -} -int_cmp_digit :: int_compare_digit; - -/* - Compare the magnitude of two `Int`s, unsigned. -*/ -int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { - if a == nil || b == nil { return 0, .Invalid_Pointer; } - if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } - - return #force_inline internal_cmp_mag(a, b), nil; -} \ No newline at end of file diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index cf7492182..33f091399 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -13,8 +13,6 @@ import "core:mem" import "core:intrinsics" import rnd "core:math/rand" -// import "core:fmt" - /* TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere. */ @@ -26,6 +24,7 @@ int_destroy :: proc(integers: ..^Int) { integers := integers; for a in &integers { + assert(a != nil, "int_destroy(nil)"); mem.zero_slice(a.digit[:]); raw := transmute(mem.Raw_Dynamic_Array)a.digit; if raw.cap > 0 { @@ -328,7 +327,7 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return copy(a, ONE, minimize, allocator); + return copy(a, INT_ONE, minimize, allocator); } one :: proc { int_one, }; @@ -676,25 +675,29 @@ clamp :: proc(a: ^Int) -> (err: Error) { /* Initialize constants. */ -ONE, ZERO, MINUS_ONE, INF, MINUS_INF, NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; +INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; initialize_constants :: proc() -> (res: int) { - set( ZERO, 0); ZERO.flags = {.Immutable}; - set( ONE, 1); ONE.flags = {.Immutable}; - set(MINUS_ONE, -1); MINUS_ONE.flags = {.Immutable}; + internal_set( INT_ZERO, 0); INT_ZERO.flags = {.Immutable}; + internal_set( INT_ONE, 1); INT_ONE.flags = {.Immutable}; + internal_set(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable}; /* We set these special values to -1 or 1 so they don't get mistake for zero accidentally. This allows for shortcut tests of is_zero as .used == 0. */ - set( NAN, 1); NAN.flags = {.Immutable, .NaN}; - set( INF, 1); INF.flags = {.Immutable, .Inf}; - set( INF, -1); MINUS_INF.flags = {.Immutable, .Inf}; + internal_set( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN}; + internal_set( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf}; + internal_set( INT_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}; return _DEFAULT_MUL_KARATSUBA_CUTOFF; } +/* + Destroy constants. + Optional for an EXE, as this would be called at the very end of a process. +*/ destroy_constants :: proc() { - destroy(ONE, ZERO, MINUS_ONE, INF, NAN); + internal_destroy(INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN); } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 43d496215..952d0ed8b 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -31,6 +31,7 @@ package big import "core:mem" import "core:intrinsics" +import rnd "core:math/rand" /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -1232,7 +1233,7 @@ internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { /* Any base to the power zero is one. */ - return one(dest); + return #force_inline internal_one(dest); case 1: /* Any base to the power one is itself. @@ -1477,7 +1478,7 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { if ilog2 -= 1; ilog2 == 0 { break; } - if c := internal_cmp(t1, t2); c == 0 { break; } + if c = internal_cmp(t1, t2); c == 0 { break; } iterations += 1; if iterations == MAX_ITERATIONS_ROOT_N { return .Max_Iterations_Reached; @@ -1491,7 +1492,7 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { for { if err = internal_pow(t2, t1, n); err != nil { return err; } - c := internal_cmp(t2, a); + c = internal_cmp(t2, a); if c == 0 { swap(dest, t1); return nil; @@ -1512,7 +1513,7 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { for { if err = internal_pow(t2, t1, n); err != nil { return err; } - c := internal_cmp(t2, a); + c = internal_cmp(t2, a); if c == 1 { if err = internal_sub(t1, t1, DIGIT(1)); err != nil { return err; } } else { @@ -1603,6 +1604,659 @@ private_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { Other internal helpers */ +/* + Deallocates the backing memory of one or more `Int`s. + Asssumes none of the `integers` to be a `nil`. +*/ +internal_int_destroy :: proc(integers: ..^Int) { + integers := integers; + + for a in &integers { + mem.zero_slice(a.digit[:]); + raw := transmute(mem.Raw_Dynamic_Array)a.digit; + if raw.cap > 0 { + free(&a.digit[0]); + } + a = &Int{}; + } +} +internal_destroy :: proc{ internal_int_destroy, }; + +/* + Helpers to set an `Int` to a specific value. +*/ +internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) + where intrinsics.type_is_integer(T) { + src := src; + + if err = error_if_immutable(dest); err != nil { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } + + dest.flags = {}; // We're not -Inf, Inf, NaN or Immutable. + + dest.used = 0; + dest.sign = .Zero_or_Positive if src >= 0 else .Negative; + src = abs(src); + + for src != 0 { + dest.digit[dest.used] = DIGIT(src) & _MASK; + dest.used += 1; + src >>= _DIGIT_BITS; + } + zero_unused(dest); + return nil; +} + +internal_set :: proc { internal_int_set_from_integer, internal_int_copy }; + +/* + Copy one `Int` to another. +*/ +internal_int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + /* + If dest == src, do nothing + */ + if (dest == src) { return nil; } + + if err = error_if_immutable(dest); err != nil { return err; } + if err = clear_if_uninitialized(src); err != nil { return err; } + + /* + Grow `dest` to fit `src`. + If `dest` is not yet initialized, it will be using `allocator`. + */ + needed := src.used if minimize else max(src.used, _DEFAULT_DIGIT_COUNT); + + if err = grow(dest, needed, minimize, allocator); err != nil { + return err; + } + + /* + Copy everything over and zero high digits. + */ + for v, i in src.digit[:src.used] { + dest.digit[i] = v; + } + dest.used = src.used; + dest.sign = src.sign; + dest.flags = src.flags &~ {.Immutable}; + + zero_unused(dest); + return nil; +} +internal_copy :: proc { internal_int_copy, }; + +/* + In normal code, you can also write `a, b = b, a`. + However, that only swaps within the current scope. + This helper swaps completely. +*/ +internal_int_swap :: proc(a, b: ^Int) { + a := a; b := b; + + a.used, b.used = b.used, a.used; + a.sign, b.sign = b.sign, a.sign; + a.digit, b.digit = b.digit, a.digit; +} +internal_swap :: proc { internal_int_swap, }; + +/* + Set `dest` to |`src`|. +*/ +internal_int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + /* + Check that src is usable. + */ + if err = clear_if_uninitialized(src); err != nil { + return err; + } + /* + If `dest == src`, just fix `dest`'s sign. + */ + if (dest == src) { + dest.sign = .Zero_or_Positive; + return nil; + } + + /* + Copy `src` to `dest` + */ + if err = copy(dest, src, false, allocator); err != nil { + return err; + } + + /* + Fix sign. + */ + dest.sign = .Zero_or_Positive; + return nil; +} + +internal_platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { + return n if n >= 0 else -n; +} +internal_abs :: proc{ internal_int_abs, internal_platform_abs, }; + +/* + Set `dest` to `-src`. +*/ +internal_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + /* + Check that src is usable. + */ + if err = clear_if_uninitialized(src); err != nil { + return err; + } + /* + If `dest == src`, just fix `dest`'s sign. + */ + sign := Sign.Zero_or_Positive; + if z, _ := is_zero(src); z { + sign = .Negative; + } + if n, _ := is_neg(src); n { + sign = .Negative; + } + if (dest == src) { + dest.sign = sign; + return nil; + } + /* + Copy `src` to `dest` + */ + if err = copy(dest, src, false, allocator); err != nil { + return err; + } + + /* + Fix sign. + */ + dest.sign = sign; + return nil; +} + +/* + Helpers to extract values from the `Int`. +*/ +internal_int_bitfield_extract_single :: proc(a: ^Int, offset: int) -> (bit: _WORD, err: Error) { + return int_bitfield_extract(a, offset, 1); +} + +internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { + /* + Check that `a` is usable. + */ + if err = clear_if_uninitialized(a); err != nil { return 0, err; } + /* + Early out for single bit. + */ + if count == 1 { + limb := offset / _DIGIT_BITS; + if limb < 0 || limb >= a.used { return 0, .Invalid_Argument; } + i := _WORD(1 << _WORD((offset % _DIGIT_BITS))); + return 1 if ((_WORD(a.digit[limb]) & i) != 0) else 0, nil; + } + + if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } + + /* + There are 3 possible cases. + - [offset:][:count] covers 1 DIGIT, + e.g. offset: 0, count: 60 = bits 0..59 + - [offset:][:count] covers 2 DIGITS, + e.g. offset: 5, count: 60 = bits 5..59, 0..4 + e.g. offset: 0, count: 120 = bits 0..59, 60..119 + - [offset:][:count] covers 3 DIGITS, + e.g. offset: 40, count: 100 = bits 40..59, 0..59, 0..19 + e.g. offset: 40, count: 120 = bits 40..59, 0..59, 0..39 + */ + + limb := offset / _DIGIT_BITS; + bits_left := count; + bits_offset := offset % _DIGIT_BITS; + + num_bits := min(bits_left, _DIGIT_BITS - bits_offset); + + shift := offset % _DIGIT_BITS; + mask := (_WORD(1) << uint(num_bits)) - 1; + res = (_WORD(a.digit[limb]) >> uint(shift)) & mask; + + bits_left -= num_bits; + if bits_left == 0 { return res, nil; } + + res_shift := num_bits; + num_bits = min(bits_left, _DIGIT_BITS); + mask = (1 << uint(num_bits)) - 1; + + res |= (_WORD(a.digit[limb + 1]) & mask) << uint(res_shift); + + bits_left -= num_bits; + if bits_left == 0 { return res, nil; } + + mask = (1 << uint(bits_left)) - 1; + res_shift += _DIGIT_BITS; + + res |= (_WORD(a.digit[limb + 2]) & mask) << uint(res_shift); + + return res, nil; +} + +/* + Resize backing store. + We don't need to pass the allocator, because the storage itself stores it. + + Assumes `a` not to be `nil`, and to have already been initialized. +*/ +internal_int_shrink :: proc(a: ^Int) -> (err: Error) { + if a == nil { + return .Invalid_Pointer; + } + + needed := max(_MIN_DIGIT_COUNT, a.used); + + if a.used != needed { + return grow(a, needed); + } + return nil; +} +internal_shrink :: proc { internal_int_shrink, }; + +internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { + if a == nil { + return .Invalid_Pointer; + } + raw := transmute(mem.Raw_Dynamic_Array)a.digit; + + /* + We need at least _MIN_DIGIT_COUNT or a.used digits, whichever is bigger. + The caller is asking for `digits`. Let's be accomodating. + */ + needed := max(_MIN_DIGIT_COUNT, a.used, digits); + if !allow_shrink { + needed = max(needed, raw.cap); + } + + /* + If not yet iniialized, initialize the `digit` backing with the allocator we were passed. + Otherwise, `[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing. + */ + if raw.cap == 0 { + a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, needed, needed, allocator); + } else if raw.cap != needed { + resize(&a.digit, needed); + } + /* + Let's see if the allocation/resize worked as expected. + */ + if len(a.digit) != needed { + return .Out_Of_Memory; + } + return nil; +} +internal_grow :: proc { internal_int_grow, }; + +/* + Clear `Int` and resize it to the default size. +*/ +internal_int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + if a == nil { + return .Invalid_Pointer; + } + + raw := transmute(mem.Raw_Dynamic_Array)a.digit; + if raw.cap != 0 { + mem.zero_slice(a.digit[:a.used]); + } + a.sign = .Zero_or_Positive; + a.used = 0; + + return grow(a, a.used, minimize, allocator); +} +internal_clear :: proc { internal_int_clear, }; +internal_zero :: internal_clear; + +/* + Set the `Int` to 1 and optionally shrink it to the minimum backing size. +*/ +internal_int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return internal_copy(a, INT_ONE, minimize, allocator); +} +internal_one :: proc { internal_int_one, }; + +/* + Set the `Int` to -1 and optionally shrink it to the minimum backing size. +*/ +internal_int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + return internal_set(a, -1, minimize, allocator); +} +internal_minus_one :: proc { internal_int_minus_one, }; + +/* + Set the `Int` to Inf and optionally shrink it to the minimum backing size. +*/ +internal_int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + err = internal_set(a, 1, minimize, allocator); + a.flags |= { .Inf, }; + return err; +} +internal_inf :: proc { internal_int_inf, }; + +/* + Set the `Int` to -Inf and optionally shrink it to the minimum backing size. +*/ +internal_int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + err = internal_set(a, -1, minimize, allocator); + a.flags |= { .Inf, }; + return err; +} +internal_minus_inf :: proc { internal_int_inf, }; + +/* + Set the `Int` to NaN and optionally shrink it to the minimum backing size. +*/ +internal_int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + err = internal_set(a, 1, minimize, allocator); + a.flags |= { .NaN, }; + return err; +} +internal_nan :: proc { internal_int_nan, }; + +internal_power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { + /* + Check that `a` is usable. + */ + if a == nil { + return .Invalid_Pointer; + } + + if power < 0 || power > _MAX_BIT_COUNT { + return .Invalid_Argument; + } + + /* + Grow to accomodate the single bit. + */ + a.used = (power / _DIGIT_BITS) + 1; + if err = internal_grow(a, a.used); err != nil { + return err; + } + /* + Zero the entirety. + */ + mem.zero_slice(a.digit[:]); + + /* + Set the bit. + */ + a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS)); + return nil; +} + +internal_int_get_u128 :: proc(a: ^Int) -> (res: u128, err: Error) { + return internal_int_get(a, u128); +} +internal_get_u128 :: proc { internal_int_get_u128, }; + +internal_int_get_i128 :: proc(a: ^Int) -> (res: i128, err: Error) { + return internal_int_get(a, i128); +} +internal_get_i128 :: proc { internal_int_get_i128, }; + +internal_int_get_u64 :: proc(a: ^Int) -> (res: u64, err: Error) { + return internal_int_get(a, u64); +} +internal_get_u64 :: proc { internal_int_get_u64, }; + +internal_int_get_i64 :: proc(a: ^Int) -> (res: i64, err: Error) { + return internal_int_get(a, i64); +} +internal_get_i64 :: proc { internal_int_get_i64, }; + +internal_int_get_u32 :: proc(a: ^Int) -> (res: u32, err: Error) { + return internal_int_get(a, u32); +} +internal_get_u32 :: proc { internal_int_get_u32, }; + +internal_int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) { + return internal_int_get(a, i32); +} +internal_get_i32 :: proc { internal_int_get_i32, }; + +/* + TODO: Think about using `count_bits` to check if the value could be returned completely, + and maybe return max(T), .Integer_Overflow if not? +*/ +internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { + if err = clear_if_uninitialized(a); err != nil { return 0, err; } + + size_in_bits := int(size_of(T) * 8); + i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS); + i = min(int(a.used), i); + + for ; i >= 0; i -= 1 { + res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS; + res |= T(a.digit[i]); + if size_in_bits <= _DIGIT_BITS { + break; + }; + } + + when !intrinsics.type_is_unsigned(T) { + /* + Mask off sign bit. + */ + res ~= 1 << uint(size_in_bits - 1); + /* + Set the sign. + */ + if a.sign == .Negative { + res = -res; + } + } + return; +} +internal_get :: proc { internal_int_get, }; + +internal_int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { + if err = clear_if_uninitialized(a); err != nil { + return 0, err; + } + + l := min(a.used, 17); // log2(max(f64)) is approximately 1020, or 17 legs. + fac := f64(1 << _DIGIT_BITS); + d := 0.0; + + for i := l; i >= 0; i -= 1 { + d = (d * fac) + f64(a.digit[i]); + } + + res = -d if a.sign == .Negative else d; + return; +} + +/* + Count bits in an `Int`. +*/ +internal_count_bits :: proc(a: ^Int) -> (count: int, err: Error) { + if err = clear_if_uninitialized(a); err != nil { + return 0, err; + } + /* + Fast path for zero. + */ + if z, _ := is_zero(a); z { + return 0, nil; + } + /* + Get the number of DIGITs and use it. + */ + count = (a.used - 1) * _DIGIT_BITS; + /* + Take the last DIGIT and count the bits in it. + */ + clz := int(intrinsics.count_leading_zeros(a.digit[a.used - 1])); + count += (_DIGIT_TYPE_BITS - clz); + return; +} + +/* + Returns the number of trailing zeroes before the first one. + Differs from regular `ctz` in that 0 returns 0. +*/ +internal_int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { + if err = clear_if_uninitialized(a); err != nil { return -1, err; } + + _ctz :: intrinsics.count_trailing_zeros; + /* + Easy out. + */ + if z, _ := is_zero(a); z { return 0, nil; } + + /* + Scan lower digits until non-zero. + */ + x: int; + for x = 0; x < a.used && a.digit[x] == 0; x += 1 {} + + q := a.digit[x]; + x *= _DIGIT_BITS; + return x + count_lsb(q), nil; +} + +internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) + where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) { + return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0; +} + +internal_count_lsb :: proc { internal_int_count_lsb, internal_platform_count_lsb, }; + +internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { + when _DIGIT_BITS == 60 { // DIGIT = u64 + return DIGIT(rnd.uint64(r)) & _MASK; + } else when _DIGIT_BITS == 28 { // DIGIT = u32 + return DIGIT(rnd.uint32(r)) & _MASK; + } else { + panic("Unsupported DIGIT size."); + } + + return 0; // We shouldn't get here. +} + +internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Error) { + bits := bits; + + if bits <= 0 { return .Invalid_Argument; } + + digits := bits / _DIGIT_BITS; + bits %= _DIGIT_BITS; + + if bits > 0 { + digits += 1; + } + + if err = grow(dest, digits); err != nil { return err; } + + for i := 0; i < digits; i += 1 { + dest.digit[i] = int_random_digit(r) & _MASK; + } + if bits > 0 { + dest.digit[digits - 1] &= ((1 << uint(bits)) - 1); + } + dest.used = digits; + return nil; +} +internal_rand :: proc { internal_int_rand, }; + +/* + Internal helpers. +*/ +internal_assert_initialized :: proc(a: ^Int, loc := #caller_location) { + assert(internal_is_initialized(a), "`Int` was not properly initialized.", loc); +} + +internal_clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) { + if !internal_is_initialized(arg) { + if arg == nil { return nil; } + return internal_grow(arg, _DEFAULT_DIGIT_COUNT); + } + return err; +} + +internal_clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { + for i in args { + if i == nil { continue; } + if !internal_is_initialized(i) { + e := internal_grow(i, _DEFAULT_DIGIT_COUNT); + if e != nil { err = e; } + } + } + return err; +} +internal_clear_if_uninitialized :: proc {internal_clear_if_uninitialized_single, internal_clear_if_uninitialized_multi, }; + +internal_error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { + if arg != nil && .Immutable in arg.flags { return .Assignment_To_Immutable; } + return nil; +} + +internal_error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { + for i in args { + if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable; } + } + return nil; +} +internal_error_if_immutable :: proc {internal_error_if_immutable_single, internal_error_if_immutable_multi, }; + +/* + Allocates several `Int`s at once. +*/ +internal_int_init_multi :: proc(integers: ..^Int) -> (err: Error) { + integers := integers; + for a in &integers { + if err = internal_clear(a); err != nil { return err; } + } + return nil; +} + +internal_init_multi :: proc { internal_int_init_multi, }; + +_private_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { + digits := digits; + if err = clear_if_uninitialized(src); err != nil { return err; } + if err = clear_if_uninitialized(dest); err != nil { return err; } + /* + If dest == src, do nothing + */ + if (dest == src) { + return nil; + } + + digits = min(digits, len(src.digit), len(dest.digit)); + mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); + return nil; +} + +/* + Trim unused digits. + + This is used to ensure that leading zero digits are trimmed and the leading "used" digit will be non-zero. + Typically very fast. Also fixes the sign if there are no more leading digits. +*/ +internal_clamp :: proc(a: ^Int) -> (err: Error) { + if err = internal_clear_if_uninitialized(a); err != nil { + return err; + } + for a.used > 0 && a.digit[a.used - 1] == 0 { + a.used -= 1; + } + + if z, _ := is_zero(a); z { + a.sign = .Zero_or_Positive; + } + return nil; +} + + internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) { /* If we don't pass the number of previously used DIGITs, we zero all remaining ones. diff --git a/core/math/big/private.odin b/core/math/big/private.odin index e9db6e3ef..84e699364 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -481,7 +481,7 @@ _private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int c: int; goto_end: for { - if err = one(tq); err != nil { break goto_end; } + if err = internal_one(tq); err != nil { break goto_end; } num_bits, _ := count_bits(numerator); den_bits, _ := count_bits(denominator); diff --git a/core/math/big/basic.odin b/core/math/big/public.odin similarity index 80% rename from core/math/big/basic.odin rename to core/math/big/public.odin index 425a5488c..2dbb8024f 100644 --- a/core/math/big/basic.odin +++ b/core/math/big/public.odin @@ -403,4 +403,92 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { return #force_inline internal_int_root_n(dest, src, n); } -root_n :: proc { int_root_n, }; \ No newline at end of file +root_n :: proc { int_root_n, }; + +/* + Comparison routines. +*/ + +int_is_initialized :: proc(a: ^Int) -> bool { + if a == nil { return false; } + + return #force_inline internal_int_is_initialized(a); +} + +int_is_zero :: proc(a: ^Int) -> (zero: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_zero(a), nil; +} + +int_is_positive :: proc(a: ^Int) -> (positive: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_positive(a), nil; +} + +int_is_negative :: proc(a: ^Int) -> (negative: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_negative(a), nil; +} + +int_is_even :: proc(a: ^Int) -> (even: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_even(a), nil; +} + +int_is_odd :: proc(a: ^Int) -> (odd: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_odd(a), nil; +} + +platform_int_is_power_of_two :: #force_inline proc(a: int) -> bool { + return ((a) != 0) && (((a) & ((a) - 1)) == 0); +} + +int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { + if a == nil { return false, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return false, err; } + + return #force_inline internal_is_power_of_two(a), nil; +} + +/* + Compare two `Int`s, signed. +*/ +int_compare :: proc(a, b: ^Int) -> (comparison: int, err: Error) { + if a == nil || b == nil { return 0, .Invalid_Pointer; } + if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } + + return #force_inline internal_cmp(a, b), nil; +} +int_cmp :: int_compare; + +/* + Compare an `Int` to an unsigned number upto the size of the backing type. +*/ +int_compare_digit :: proc(a: ^Int, b: DIGIT) -> (comparison: int, err: Error) { + if a == nil { return 0, .Invalid_Pointer; } + if err = clear_if_uninitialized(a); err != nil { return 0, err; } + + return #force_inline internal_cmp_digit(a, b), nil; +} +int_cmp_digit :: int_compare_digit; + +/* + Compare the magnitude of two `Int`s, unsigned. +*/ +int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { + if a == nil || b == nil { return 0, .Invalid_Pointer; } + if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } + + return #force_inline internal_cmp_mag(a, b), nil; +} \ No newline at end of file diff --git a/core/math/big/test.py b/core/math/big/test.py index 6bdff91db..ca0ec98c3 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -4,6 +4,7 @@ import math import os import platform import time +import gc from enum import Enum # @@ -92,6 +93,11 @@ class Error(Enum): Math_Domain_Error = 9 Unimplemented = 127 +# +# Disable garbage collection +# +gc.disable() + # # Set up exported procedures # From 07dca737f0b75e791efa21f82d362f0755f43045 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 9 Aug 2021 19:08:39 +0200 Subject: [PATCH 094/105] big: More refactoring. --- core/math/big/helpers.odin | 461 ++++++++++++------------------------ core/math/big/internal.odin | 18 +- 2 files changed, 156 insertions(+), 323 deletions(-) diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 33f091399..3b28b1833 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -12,6 +12,7 @@ package big import "core:mem" import "core:intrinsics" import rnd "core:math/rand" +import "core:fmt" /* TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere. @@ -24,14 +25,9 @@ int_destroy :: proc(integers: ..^Int) { integers := integers; for a in &integers { - assert(a != nil, "int_destroy(nil)"); - mem.zero_slice(a.digit[:]); - raw := transmute(mem.Raw_Dynamic_Array)a.digit; - if raw.cap > 0 { - free(&a.digit[0]); - } - a = &Int{}; + assert_if_nil(a); } + #force_inline internal_int_destroy(..integers); } /* @@ -41,22 +37,13 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : where intrinsics.type_is_integer(T) { src := src; - if err = error_if_immutable(dest); err != nil { return err; } - if err = clear_if_uninitialized(dest); err != nil { return err; } + /* + Check that `src` is usable and `dest` isn't immutable. + */ + assert_if_nil(dest); + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } - dest.flags = {}; // We're not -Inf, Inf, NaN or Immutable. - - dest.used = 0; - dest.sign = .Zero_or_Positive if src >= 0 else .Negative; - src = abs(src); - - for src != 0 { - dest.digit[dest.used] = DIGIT(src) & _MASK; - dest.used += 1; - src >>= _DIGIT_BITS; - } - zero_unused(dest); - return nil; + return #force_inline internal_int_set_from_integer(dest, src, minimize, allocator); } set :: proc { int_set_from_integer, int_copy }; @@ -70,31 +57,14 @@ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.alloca */ if (dest == src) { return nil; } - if err = error_if_immutable(dest); err != nil { return err; } - if err = clear_if_uninitialized(src); err != nil { return err; } - /* - Grow `dest` to fit `src`. - If `dest` is not yet initialized, it will be using `allocator`. + Check that `src` is usable and `dest` isn't immutable. */ - needed := src.used if minimize else max(src.used, _DEFAULT_DIGIT_COUNT); + assert_if_nil(dest, src); + if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } - if err = grow(dest, needed, minimize, allocator); err != nil { - return err; - } - - /* - Copy everything over and zero high digits. - */ - for v, i in src.digit[:src.used] { - dest.digit[i] = v; - } - dest.used = src.used; - dest.sign = src.sign; - dest.flags = src.flags &~ {.Immutable}; - - zero_unused(dest); - return nil; + return #force_inline internal_int_copy(dest, src, minimize, allocator); } copy :: proc { int_copy, }; @@ -104,11 +74,8 @@ copy :: proc { int_copy, }; This helper swaps completely. */ int_swap :: proc(a, b: ^Int) { - a := a; b := b; - - a.used, b.used = b.used, a.used; - a.sign, b.sign = b.sign, a.sign; - a.digit, b.digit = b.digit, a.digit; + assert_if_nil(a, b); + #force_inline internal_swap(a, b); } swap :: proc { int_swap, }; @@ -117,189 +84,72 @@ swap :: proc { int_swap, }; */ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* - Check that src is usable. + Check that `src` is usable and `dest` isn't immutable. */ - if err = clear_if_uninitialized(src); err != nil { - return err; - } - /* - If `dest == src`, just fix `dest`'s sign. - */ - if (dest == src) { - dest.sign = .Zero_or_Positive; - return nil; - } + assert_if_nil(dest, src); + if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } - /* - Copy `src` to `dest` - */ - if err = copy(dest, src, false, allocator); err != nil { - return err; - } - - /* - Fix sign. - */ - dest.sign = .Zero_or_Positive; - return nil; + return #force_inline internal_int_abs(dest, src, allocator); } platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { return n if n >= 0 else -n; } -abs :: proc{int_abs, platform_abs}; +abs :: proc{ int_abs, platform_abs, }; /* Set `dest` to `-src`. */ -neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { +int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* - Check that src is usable. + Check that `src` is usable and `dest` isn't immutable. */ - if err = clear_if_uninitialized(src); err != nil { - return err; - } - /* - If `dest == src`, just fix `dest`'s sign. - */ - sign := Sign.Zero_or_Positive; - if z, _ := is_zero(src); z { - sign = .Negative; - } - if n, _ := is_neg(src); n { - sign = .Negative; - } - if (dest == src) { - dest.sign = sign; - return nil; - } - /* - Copy `src` to `dest` - */ - if err = copy(dest, src, false, allocator); err != nil { - return err; - } + assert_if_nil(dest, src); + if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } - /* - Fix sign. - */ - dest.sign = sign; - return nil; + return #force_inline internal_int_neg(dest, src, allocator); } +neg :: proc { int_neg, }; /* Helpers to extract values from the `Int`. */ int_bitfield_extract_single :: proc(a: ^Int, offset: int) -> (bit: _WORD, err: Error) { - return int_bitfield_extract(a, offset, 1); + return #force_inline int_bitfield_extract(a, offset, 1); } int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* Check that `a` is usable. */ - if err = clear_if_uninitialized(a); err != nil { return 0, err; } - /* - Early out for single bit. - */ - if count == 1 { - limb := offset / _DIGIT_BITS; - if limb < 0 || limb >= a.used { return 0, .Invalid_Argument; } - i := _WORD(1 << _WORD((offset % _DIGIT_BITS))); - return 1 if ((_WORD(a.digit[limb]) & i) != 0) else 0, nil; - } + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a); err != nil { return 0, err; } - if count > _WORD_BITS || count < 1 { return 0, .Invalid_Argument; } - - /* - There are 3 possible cases. - - [offset:][:count] covers 1 DIGIT, - e.g. offset: 0, count: 60 = bits 0..59 - - [offset:][:count] covers 2 DIGITS, - e.g. offset: 5, count: 60 = bits 5..59, 0..4 - e.g. offset: 0, count: 120 = bits 0..59, 60..119 - - [offset:][:count] covers 3 DIGITS, - e.g. offset: 40, count: 100 = bits 40..59, 0..59, 0..19 - e.g. offset: 40, count: 120 = bits 40..59, 0..59, 0..39 - */ - - limb := offset / _DIGIT_BITS; - bits_left := count; - bits_offset := offset % _DIGIT_BITS; - - num_bits := min(bits_left, _DIGIT_BITS - bits_offset); - - shift := offset % _DIGIT_BITS; - mask := (_WORD(1) << uint(num_bits)) - 1; - res = (_WORD(a.digit[limb]) >> uint(shift)) & mask; - - bits_left -= num_bits; - if bits_left == 0 { return res, nil; } - - res_shift := num_bits; - num_bits = min(bits_left, _DIGIT_BITS); - mask = (1 << uint(num_bits)) - 1; - - res |= (_WORD(a.digit[limb + 1]) & mask) << uint(res_shift); - - bits_left -= num_bits; - if bits_left == 0 { return res, nil; } - - mask = (1 << uint(bits_left)) - 1; - res_shift += _DIGIT_BITS; - - res |= (_WORD(a.digit[limb + 2]) & mask) << uint(res_shift); - - return res, nil; + return #force_inline internal_int_bitfield_extract(a, offset, count); } /* Resize backing store. */ shrink :: proc(a: ^Int) -> (err: Error) { - if a == nil { - return .Invalid_Pointer; - } + /* + Check that `a` is usable. + */ + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a); err != nil { return err; } - needed := max(_MIN_DIGIT_COUNT, a.used); - - if a.used != needed { - return grow(a, needed); - } - return nil; + return #force_inline internal_shrink(a); } int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { - if a == nil { - return .Invalid_Pointer; - } - raw := transmute(mem.Raw_Dynamic_Array)a.digit; + /* + Check that `a` is usable. + */ + assert_if_nil(a); - /* - We need at least _MIN_DIGIT_COUNT or a.used digits, whichever is bigger. - The caller is asking for `digits`. Let's be accomodating. - */ - needed := max(_MIN_DIGIT_COUNT, a.used, digits); - if !allow_shrink { - needed = max(needed, raw.cap); - } - - /* - If not yet iniialized, initialize the `digit` backing with the allocator we were passed. - Otherwise, `[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing. - */ - if raw.cap == 0 { - a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, needed, needed, allocator); - } else if raw.cap != needed { - resize(&a.digit, needed); - } - /* - Let's see if the allocation/resize worked as expected. - */ - if len(a.digit) != needed { - return .Out_Of_Memory; - } - return nil; + return #force_inline internal_int_grow(a, digits, allow_shrink, allocator); } grow :: proc { int_grow, }; @@ -307,18 +157,12 @@ grow :: proc { int_grow, }; Clear `Int` and resize it to the default size. */ int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if a == nil { - return .Invalid_Pointer; - } + /* + Check that `a` is usable. + */ + assert_if_nil(a); - raw := transmute(mem.Raw_Dynamic_Array)a.digit; - if raw.cap != 0 { - mem.zero_slice(a.digit[:a.used]); - } - a.sign = .Zero_or_Positive; - a.used = 0; - - return grow(a, a.used, minimize, allocator); + return #force_inline internal_int_clear(a, minimize, allocator); } clear :: proc { int_clear, }; zero :: clear; @@ -327,7 +171,12 @@ zero :: clear; Set the `Int` to 1 and optionally shrink it to the minimum backing size. */ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return copy(a, INT_ONE, minimize, allocator); + /* + Check that `a` is usable. + */ + assert_if_nil(a); + + return #force_inline internal_one(a, minimize, allocator); } one :: proc { int_one, }; @@ -335,7 +184,12 @@ one :: proc { int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return set(a, -1, minimize, allocator); + /* + Check that `a` is usable. + */ + assert_if_nil(a); + + return #force_inline internal_minus_one(a, minimize, allocator); } minus_one :: proc { int_minus_one, }; @@ -343,9 +197,12 @@ minus_one :: proc { int_minus_one, }; Set the `Int` to Inf and optionally shrink it to the minimum backing size. */ int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - err = set(a, 1, minimize, allocator); - a.flags |= { .Inf, }; - return err; + /* + Check that `a` is usable. + */ + assert_if_nil(a); + + return #force_inline internal_inf(a, minimize, allocator); } inf :: proc { int_inf, }; @@ -353,9 +210,12 @@ inf :: proc { int_inf, }; Set the `Int` to -Inf and optionally shrink it to the minimum backing size. */ int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - err = set(a, -1, minimize, allocator); - a.flags |= { .Inf, }; - return err; + /* + Check that `a` is usable. + */ + assert_if_nil(a); + + return #force_inline internal_minus_inf(a, minimize, allocator); } minus_inf :: proc { int_inf, }; @@ -363,69 +223,80 @@ minus_inf :: proc { int_inf, }; Set the `Int` to NaN and optionally shrink it to the minimum backing size. */ int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - err = set(a, 1, minimize, allocator); - a.flags |= { .NaN, }; - return err; -} -nan :: proc { int_nan, }; - -power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { /* Check that `a` is usable. */ - if a == nil { - return .Invalid_Pointer; - } + assert_if_nil(a); - if power < 0 || power > _MAX_BIT_COUNT { - return .Invalid_Argument; - } + return #force_inline internal_nan(a, minimize, allocator); +} +nan :: proc { int_nan, }; +power_of_two :: proc(a: ^Int, power: int, allocator := context.allocator) -> (err: Error) { /* - Grow to accomodate the single bit. + Check that `a` is usable. */ - a.used = (power / _DIGIT_BITS) + 1; - if err = grow(a, a.used); err != nil { - return err; - } - /* - Zero the entirety. - */ - mem.zero_slice(a.digit[:]); + assert_if_nil(a); - /* - Set the bit. - */ - a.digit[power / _DIGIT_BITS] = 1 << uint((power % _DIGIT_BITS)); - return nil; + return #force_inline internal_int_power_of_two(a, power, allocator); } int_get_u128 :: proc(a: ^Int) -> (res: u128, err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + return int_get(a, u128); } get_u128 :: proc { int_get_u128, }; int_get_i128 :: proc(a: ^Int) -> (res: i128, err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + return int_get(a, i128); } get_i128 :: proc { int_get_i128, }; int_get_u64 :: proc(a: ^Int) -> (res: u64, err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + return int_get(a, u64); } get_u64 :: proc { int_get_u64, }; int_get_i64 :: proc(a: ^Int) -> (res: i64, err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + return int_get(a, i64); } get_i64 :: proc { int_get_i64, }; int_get_u32 :: proc(a: ^Int) -> (res: u32, err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + return int_get(a, u32); } get_u32 :: proc { int_get_u32, }; int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + return int_get(a, i32); } get_i32 :: proc { int_get_i32, }; @@ -434,101 +305,52 @@ get_i32 :: proc { int_get_i32, }; TODO: Think about using `count_bits` to check if the value could be returned completely, and maybe return max(T), .Integer_Overflow if not? */ -int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { - if err = clear_if_uninitialized(a); err != nil { return 0, err; } +int_get :: proc(a: ^Int, $T: typeid, allocator := context.allocator) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return T{}, err; } - size_in_bits := int(size_of(T) * 8); - i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS); - i = min(int(a.used), i); - - for ; i >= 0; i -= 1 { - res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS; - res |= T(a.digit[i]); - if size_in_bits <= _DIGIT_BITS { - break; - }; - } - - when !intrinsics.type_is_unsigned(T) { - /* - Mask off sign bit. - */ - res ~= 1 << uint(size_in_bits - 1); - /* - Set the sign. - */ - if a.sign == .Negative { - res = -res; - } - } - return; + return #force_inline internal_int_get(a, T); } get :: proc { int_get, }; -int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } +int_get_float :: proc(a: ^Int, allocator := context.allocator) -> (res: f64, err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return 0, err; } - l := min(a.used, 17); // log2(max(f64)) is approximately 1020, or 17 legs. - fac := f64(1 << _DIGIT_BITS); - d := 0.0; - - for i := l; i >= 0; i -= 1 { - d = (d * fac) + f64(a.digit[i]); - } - - res = -d if a.sign == .Negative else d; - return; + return #force_inline internal_int_get_float(a); } /* Count bits in an `Int`. */ -count_bits :: proc(a: ^Int) -> (count: int, err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } +count_bits :: proc(a: ^Int, allocator := context.allocator) -> (count: int, err: Error) { /* - Fast path for zero. + Check that `a` is usable. */ - if z, _ := is_zero(a); z { - return 0, nil; - } - /* - Get the number of DIGITs and use it. - */ - count = (a.used - 1) * _DIGIT_BITS; - /* - Take the last DIGIT and count the bits in it. - */ - clz := int(intrinsics.count_leading_zeros(a.digit[a.used - 1])); - count += (_DIGIT_TYPE_BITS - clz); - return; + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return 0, err; } + + return #force_inline internal_count_bits(a); } /* Returns the number of trailing zeroes before the first one. Differs from regular `ctz` in that 0 returns 0. */ -int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { - if err = clear_if_uninitialized(a); err != nil { return -1, err; } - - _ctz :: intrinsics.count_trailing_zeros; +int_count_lsb :: proc(a: ^Int, allocator := context.allocator) -> (count: int, err: Error) { /* - Easy out. + Check that `a` is usable. */ - if z, _ := is_zero(a); z { return 0, nil; } + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return 0, err; } - /* - Scan lower digits until non-zero. - */ - x: int; - for x = 0; x < a.used && a.digit[x] == 0; x += 1 {} - - q := a.digit[x]; - x *= _DIGIT_BITS; - return x + count_lsb(q), nil; + return #force_inline internal_int_count_lsb(a); } platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) @@ -701,3 +523,14 @@ destroy_constants :: proc() { internal_destroy(INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN); } + +assert_if_nil :: #force_inline proc(integers: ..^Int, loc := #caller_location) { + integers := integers; + + for i in &integers { + if i == nil { + msg := fmt.tprintf("%v(nil)", loc.procedure); + assert(false, msg, loc); + } + } +} diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 952d0ed8b..f92c41f2b 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1740,7 +1740,7 @@ internal_abs :: proc{ internal_int_abs, internal_platform_abs, }; /* Set `dest` to `-src`. */ -internal_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { +internal_int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Check that src is usable. */ @@ -1774,6 +1774,8 @@ internal_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: E dest.sign = sign; return nil; } +internal_neg :: proc { internal_int_neg, }; + /* Helpers to extract values from the `Int`. @@ -1961,7 +1963,7 @@ internal_int_nan :: proc(a: ^Int, minimize := false, allocator := context.alloca } internal_nan :: proc { internal_int_nan, }; -internal_power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { +internal_int_power_of_two :: proc(a: ^Int, power: int, allocator := context.allocator) -> (err: Error) { /* Check that `a` is usable. */ @@ -1977,7 +1979,7 @@ internal_power_of_two :: proc(a: ^Int, power: int) -> (err: Error) { Grow to accomodate the single bit. */ a.used = (power / _DIGIT_BITS) + 1; - if err = internal_grow(a, a.used); err != nil { + if err = internal_grow(a, a.used, false, allocator); err != nil { return err; } /* @@ -2027,8 +2029,6 @@ internal_get_i32 :: proc { internal_int_get_i32, }; and maybe return max(T), .Integer_Overflow if not? */ internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { - if err = clear_if_uninitialized(a); err != nil { return 0, err; } - size_in_bits := int(size_of(T) * 8); i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS); i = min(int(a.used), i); @@ -2174,19 +2174,19 @@ internal_assert_initialized :: proc(a: ^Int, loc := #caller_location) { assert(internal_is_initialized(a), "`Int` was not properly initialized.", loc); } -internal_clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) { +internal_clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) -> (err: Error) { if !internal_is_initialized(arg) { if arg == nil { return nil; } - return internal_grow(arg, _DEFAULT_DIGIT_COUNT); + return internal_grow(arg, _DEFAULT_DIGIT_COUNT, true, allocator); } return err; } -internal_clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { +internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { for i in args { if i == nil { continue; } if !internal_is_initialized(i) { - e := internal_grow(i, _DEFAULT_DIGIT_COUNT); + e := internal_grow(i, _DEFAULT_DIGIT_COUNT, true, allocator); if e != nil { err = e; } } } From d505a05d3661e4b76a7c0e8b6e42a2d8062dd359 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 9 Aug 2021 20:54:16 +0200 Subject: [PATCH 095/105] big: More refactoring. --- core/math/big/common.odin | 4 +- core/math/big/helpers.odin | 111 +++++++++++++++--------------------- core/math/big/internal.odin | 28 +++------ core/math/big/logical.odin | 1 + core/math/big/public.odin | 73 ++++++++++++------------ 5 files changed, 95 insertions(+), 122 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 562aa46fa..56b8a1083 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -111,7 +111,7 @@ Flags :: bit_set[Flag; u8]; Error :: enum int { Okay = 0, Out_Of_Memory = 1, - Invalid_Pointer = 2, + // Invalid_Pointer = 2, Invalid_Argument = 3, Assignment_To_Immutable = 4, @@ -127,7 +127,7 @@ Error :: enum int { Error_String :: #partial [Error]string{ .Out_Of_Memory = "Out of memory", - .Invalid_Pointer = "Invalid pointer", + // .Invalid_Pointer = "Invalid pointer", .Invalid_Argument = "Invalid argument", .Assignment_To_Immutable = "Assignment to immutable", diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 3b28b1833..dd8f70f4b 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -41,7 +41,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest); - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } return #force_inline internal_int_set_from_integer(dest, src, minimize, allocator); } @@ -61,8 +61,8 @@ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.alloca Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest, src); - if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } return #force_inline internal_int_copy(dest, src, minimize, allocator); } @@ -87,8 +87,8 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest, src); - if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } return #force_inline internal_int_abs(dest, src, allocator); } @@ -106,8 +106,8 @@ int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest, src); - if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } return #force_inline internal_int_neg(dest, src, allocator); } @@ -116,16 +116,16 @@ neg :: proc { int_neg, }; /* Helpers to extract values from the `Int`. */ -int_bitfield_extract_single :: proc(a: ^Int, offset: int) -> (bit: _WORD, err: Error) { - return #force_inline int_bitfield_extract(a, offset, 1); +int_bitfield_extract_single :: proc(a: ^Int, offset: int, allocator := context.allocator) -> (bit: _WORD, err: Error) { + return #force_inline int_bitfield_extract(a, offset, 1, allocator); } -int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { +int_bitfield_extract :: proc(a: ^Int, offset, count: int, allocator := context.allocator) -> (res: _WORD, err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - if err = #force_inline internal_clear_if_uninitialized(a); err != nil { return 0, err; } + if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return {}, err; } return #force_inline internal_int_bitfield_extract(a, offset, count); } @@ -372,28 +372,13 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { return 0; // We shouldn't get here. } -int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Error) { - bits := bits; +int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { + /* + Check that `a` is usable. + */ + assert_if_nil(dest); + return #force_inline internal_int_rand(dest, bits, r, allocator); - if bits <= 0 { return .Invalid_Argument; } - - digits := bits / _DIGIT_BITS; - bits %= _DIGIT_BITS; - - if bits > 0 { - digits += 1; - } - - if err = grow(dest, digits); err != nil { return err; } - - for i := 0; i < digits; i += 1 { - dest.digit[i] = int_random_digit(r) & _MASK; - } - if bits > 0 { - dest.digit[digits - 1] &= ((1 << uint(bits)) - 1); - } - dest.used = digits; - return nil; } rand :: proc { int_rand, }; @@ -401,31 +386,28 @@ rand :: proc { int_rand, }; Internal helpers. */ assert_initialized :: proc(a: ^Int, loc := #caller_location) { + assert_if_nil(a); assert(is_initialized(a), "`Int` was not properly initialized.", loc); } zero_unused :: proc(dest: ^Int, old_used := -1) { - if dest == nil { return; } + assert_if_nil(dest); if ! #force_inline is_initialized(dest) { return; } - internal_zero_unused(dest, old_used); + #force_inline internal_zero_unused(dest, old_used); } -clear_if_uninitialized_single :: proc(arg: ^Int) -> (err: Error) { - if !is_initialized(arg) { - if arg == nil { return nil; } - return grow(arg, _DEFAULT_DIGIT_COUNT); - } - return err; +clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(arg); + return #force_inline internal_clear_if_uninitialized_single(arg, allocator); } -clear_if_uninitialized_multi :: proc(args: ..^Int) -> (err: Error) { - for i in args { - if i == nil { continue; } - if !is_initialized(i) { - e := grow(i, _DEFAULT_DIGIT_COUNT); - if e != nil { err = e; } - } +clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { + args := args; + assert_if_nil(..args); + + for i in &args { + if err = #force_inline internal_clear_if_uninitialized_single(i, allocator); err != nil { return nil; } } return err; } @@ -448,28 +430,33 @@ error_if_immutable :: proc {error_if_immutable_single, error_if_immutable_multi, Allocates several `Int`s at once. */ int_init_multi :: proc(integers: ..^Int) -> (err: Error) { + assert_if_nil(..integers); + integers := integers; for a in &integers { - if err = clear(a); err != nil { return err; } + if err = #force_inline internal_clear(a); err != nil { return err; } } return nil; } init_multi :: proc { int_init_multi, }; -_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { +_copy_digits :: proc(dest, src: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { digits := digits; - if err = clear_if_uninitialized(src); err != nil { return err; } - if err = clear_if_uninitialized(dest); err != nil { return err; } + /* + Check that `src` is usable and `dest` isn't immutable. + */ + assert_if_nil(dest, src); + if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + /* If dest == src, do nothing */ - if (dest == src) { - return nil; - } + if (dest == src) { return nil; } digits = min(digits, len(src.digit), len(dest.digit)); - mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); + #force_inline mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); return nil; } @@ -479,10 +466,10 @@ _copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { This is used to ensure that leading zero digits are trimmed and the leading "used" digit will be non-zero. Typically very fast. Also fixes the sign if there are no more leading digits. */ -clamp :: proc(a: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a); err != nil { - return err; - } +clamp :: proc(a: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return err; } + for a.used > 0 && a.digit[a.used - 1] == 0 { a.used -= 1; } @@ -493,7 +480,6 @@ clamp :: proc(a: ^Int) -> (err: Error) { return nil; } - /* Initialize constants. */ @@ -528,9 +514,6 @@ assert_if_nil :: #force_inline proc(integers: ..^Int, loc := #caller_location) { integers := integers; for i in &integers { - if i == nil { - msg := fmt.tprintf("%v(nil)", loc.procedure); - assert(false, msg, loc); - } + assert(i != nil, "(nil)", loc); } } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index f92c41f2b..9498e473c 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1850,10 +1850,6 @@ internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WOR Assumes `a` not to be `nil`, and to have already been initialized. */ internal_int_shrink :: proc(a: ^Int) -> (err: Error) { - if a == nil { - return .Invalid_Pointer; - } - needed := max(_MIN_DIGIT_COUNT, a.used); if a.used != needed { @@ -1864,9 +1860,6 @@ internal_int_shrink :: proc(a: ^Int) -> (err: Error) { internal_shrink :: proc { internal_int_shrink, }; internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { - if a == nil { - return .Invalid_Pointer; - } raw := transmute(mem.Raw_Dynamic_Array)a.digit; /* @@ -1899,12 +1892,9 @@ internal_grow :: proc { internal_int_grow, }; /* Clear `Int` and resize it to the default size. + Assumes `a` not to be `nil`. */ internal_int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - if a == nil { - return .Invalid_Pointer; - } - raw := transmute(mem.Raw_Dynamic_Array)a.digit; if raw.cap != 0 { mem.zero_slice(a.digit[:a.used]); @@ -1912,7 +1902,7 @@ internal_int_clear :: proc(a: ^Int, minimize := false, allocator := context.allo a.sign = .Zero_or_Positive; a.used = 0; - return grow(a, a.used, minimize, allocator); + return #force_inline internal_grow(a, a.used, minimize, allocator); } internal_clear :: proc { internal_int_clear, }; internal_zero :: internal_clear; @@ -1967,9 +1957,7 @@ internal_int_power_of_two :: proc(a: ^Int, power: int, allocator := context.allo /* Check that `a` is usable. */ - if a == nil { - return .Invalid_Pointer; - } + assert_if_nil(a); if power < 0 || power > _MAX_BIT_COUNT { return .Invalid_Argument; @@ -2142,7 +2130,7 @@ internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { return 0; // We shouldn't get here. } -internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Error) { +internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { bits := bits; if bits <= 0 { return .Invalid_Argument; } @@ -2154,7 +2142,7 @@ internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Er digits += 1; } - if err = grow(dest, digits); err != nil { return err; } + if err = grow(dest, digits, true, allocator); err != nil { return err; } for i := 0; i < digits; i += 1 { dest.digit[i] = int_random_digit(r) & _MASK; @@ -2176,17 +2164,15 @@ internal_assert_initialized :: proc(a: ^Int, loc := #caller_location) { internal_clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) -> (err: Error) { if !internal_is_initialized(arg) { - if arg == nil { return nil; } - return internal_grow(arg, _DEFAULT_DIGIT_COUNT, true, allocator); + return #force_inline internal_grow(arg, _DEFAULT_DIGIT_COUNT, true, allocator); } return err; } internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { for i in args { - if i == nil { continue; } if !internal_is_initialized(i) { - e := internal_grow(i, _DEFAULT_DIGIT_COUNT, true, allocator); + e := #force_inline internal_grow(i, _DEFAULT_DIGIT_COUNT, true, allocator); if e != nil { err = e; } } } diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index bd6bcfc6e..0ab9e2f9b 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -24,6 +24,7 @@ import "core:mem" 2's complement `and`, returns `dest = a & b;` */ int_and :: proc(dest, a, b: ^Int) -> (err: Error) { + assert_if_nil(dest, a, b); if err = clear_if_uninitialized(a, b); err != nil { return err; } used := max(a.used, b.used) + 1; diff --git a/core/math/big/public.odin b/core/math/big/public.odin index 2dbb8024f..fdb7bcbd5 100644 --- a/core/math/big/public.odin +++ b/core/math/big/public.odin @@ -21,7 +21,7 @@ package big High-level addition. Handles sign. */ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { - if dest == nil || a == nil || b == nil { return .Invalid_Pointer; } + assert_if_nil(dest, a, b); if err = clear_if_uninitialized(dest, a, b); err != nil { return err; } /* All parameters have been initialized. @@ -36,7 +36,7 @@ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error dest = a + digit; */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { - if dest == nil || a == nil { return .Invalid_Pointer; } + assert_if_nil(dest, a); if err = clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. @@ -53,7 +53,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato High-level subtraction, dest = number - decrease. Handles signs. */ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { - if dest == nil || number == nil || decrease == nil { return .Invalid_Pointer; } + assert_if_nil(dest, number, decrease); if err = clear_if_uninitialized(dest, number, decrease); err != nil { return err; } /* All parameters have been initialized. @@ -68,7 +68,7 @@ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> dest = a - digit; */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { - if dest == nil || a == nil { return .Invalid_Pointer; } + assert_if_nil(dest, a); if err = clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. @@ -86,7 +86,7 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato dest = src >> 1 */ int_halve :: proc(dest, src: ^Int) -> (err: Error) { - if dest == nil || src == nil { return .Invalid_Pointer; } + assert_if_nil(dest, src); if err = clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. @@ -103,7 +103,7 @@ shr1 :: halve; dest = src << 1 */ int_double :: proc(dest, src: ^Int) -> (err: Error) { - if dest == nil || src == nil { return .Invalid_Pointer; } + assert_if_nil(dest, src); if err = clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. @@ -119,7 +119,7 @@ shl1 :: double; Multiply by a DIGIT. */ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) { - if dest == nil || src == nil { return .Invalid_Pointer; } + assert_if_nil(dest, src); if err = clear_if_uninitialized(src, dest); err != nil { return err; } return #force_inline internal_int_mul_digit(dest, src, multiplier, allocator); @@ -129,7 +129,7 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.a High level multiplication (handles sign). */ int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) { - if dest == nil || src == nil || multiplier == nil { return .Invalid_Pointer; } + assert_if_nil(dest, src, multiplier); if err = clear_if_uninitialized(dest, src, multiplier); err != nil { return err; } return #force_inline internal_int_mul(dest, src, multiplier, allocator); @@ -154,7 +154,7 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E } int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { - if quotient == nil { return 0, .Invalid_Pointer; }; + assert_if_nil(quotient, numerator); if err = clear_if_uninitialized(numerator); err != nil { return 0, err; } return #force_inline internal_divmod(quotient, numerator, denominator); @@ -162,14 +162,14 @@ int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (rema divmod :: proc{ int_divmod, int_divmod_digit, }; int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { - if quotient == nil { return .Invalid_Pointer; }; + assert_if_nil(quotient, numerator, denominator); if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } return #force_inline internal_divmod(quotient, nil, numerator, denominator); } int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (err: Error) { - if quotient == nil { return .Invalid_Pointer; }; + assert_if_nil(quotient, numerator); if err = clear_if_uninitialized(numerator); err != nil { return err; } remainder: DIGIT; @@ -184,7 +184,7 @@ div :: proc { int_div, int_div_digit, }; denominator < remainder <= 0 if denominator < 0 */ int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { - if remainder == nil { return .Invalid_Pointer; }; + assert_if_nil(remainder, numerator, denominator); if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } return #force_inline internal_int_mod(remainder, numerator, denominator); @@ -200,7 +200,7 @@ mod :: proc { int_mod, int_mod_digit, }; remainder = (number + addend) % modulus. */ int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { - if remainder == nil { return .Invalid_Pointer; }; + assert_if_nil(remainder, number, addend); if err = clear_if_uninitialized(number, addend, modulus); err != nil { return err; } return #force_inline internal_addmod(remainder, number, addend, modulus); @@ -211,7 +211,7 @@ addmod :: proc { int_addmod, }; remainder = (number - decrease) % modulus. */ int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { - if remainder == nil { return .Invalid_Pointer; }; + assert_if_nil(remainder, number, decrease); if err = clear_if_uninitialized(number, decrease, modulus); err != nil { return err; } return #force_inline internal_submod(remainder, number, decrease, modulus); @@ -222,7 +222,7 @@ submod :: proc { int_submod, }; remainder = (number * multiplicand) % modulus. */ int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { - if remainder == nil { return .Invalid_Pointer; }; + assert_if_nil(remainder, number, multiplicand); if err = clear_if_uninitialized(number, multiplicand, modulus); err != nil { return err; } return #force_inline internal_mulmod(remainder, number, multiplicand, modulus); @@ -233,7 +233,7 @@ mulmod :: proc { int_mulmod, }; remainder = (number * number) % modulus. */ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { - if remainder == nil { return .Invalid_Pointer; }; + assert_if_nil(remainder, number, modulus); if err = clear_if_uninitialized(number, modulus); err != nil { return err; } return #force_inline internal_sqrmod(remainder, number, modulus); @@ -243,7 +243,7 @@ sqrmod :: proc { int_sqrmod, }; int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; } - if res == nil { return .Invalid_Pointer; } + assert_if_nil(res); return #force_inline internal_int_factorial(res, n); } @@ -266,7 +266,7 @@ factorial :: proc { int_factorial, }; */ int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { - if res == nil { return .Invalid_Pointer; } + assert_if_nil(res); if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; } if k > n { return zero(res); } @@ -291,10 +291,12 @@ choose :: proc { int_choose_digit, }; /* Function computing both GCD and (if target isn't `nil`) also LCM. */ -int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { +int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.allocator) -> (err: Error) { if res_gcd == nil && res_lcm == nil { return nil; } - if err = clear_if_uninitialized(res_gcd, res_lcm, a, b); err != nil { return err; } + assert_if_nil(a, b); + if err = clear_if_uninitialized(a, allocator); err != nil { return err; } + if err = clear_if_uninitialized(b, allocator); err != nil { return err; } return #force_inline internal_int_gcd_lcm(res_gcd, res_lcm, a, b); } gcd_lcm :: proc { int_gcd_lcm, }; @@ -319,7 +321,8 @@ lcm :: proc { int_lcm, }; remainder = numerator % (1 << bits) */ int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { - if remainder == nil || numerator == nil { return .Invalid_Pointer; } + assert_if_nil(remainder, numerator); + if err = clear_if_uninitialized(remainder, numerator); err != nil { return err; } if bits < 0 { return .Invalid_Argument; } @@ -333,7 +336,7 @@ mod_bits :: proc { int_mod_bits, }; Logs and roots and such. */ int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { - if a == nil { return 0, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return 0, err; } return #force_inline internal_int_log(a, base); @@ -348,7 +351,7 @@ log :: proc { int_log, digit_log, }; Calculate `dest = base^power` using a square-multiply algorithm. */ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { - if dest == nil || base == nil { return .Invalid_Pointer; } + assert_if_nil(dest, base); if err = clear_if_uninitialized(dest, base); err != nil { return err; } return #force_inline internal_int_pow(dest, base, power); @@ -358,7 +361,7 @@ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { Calculate `dest = base^power` using a square-multiply algorithm. */ int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { - if dest == nil { return .Invalid_Pointer; } + assert_if_nil(dest); return #force_inline internal_pow(dest, base, power); } @@ -374,7 +377,7 @@ small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { This function is less generic than `root_n`, simpler and faster. */ int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { - if dest == nil || src == nil { return .Invalid_Pointer; } + assert_if_nil(dest, src); if err = clear_if_uninitialized(dest, src); err != nil { return err; } return #force_inline internal_int_sqrt(dest, src); @@ -395,7 +398,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { */ if n == 2 { return sqrt(dest, src); } - if dest == nil || src == nil { return .Invalid_Pointer; } + assert_if_nil(dest, src); /* Initialize dest + src if needed. */ @@ -416,35 +419,35 @@ int_is_initialized :: proc(a: ^Int) -> bool { } int_is_zero :: proc(a: ^Int) -> (zero: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_zero(a), nil; } int_is_positive :: proc(a: ^Int) -> (positive: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_positive(a), nil; } int_is_negative :: proc(a: ^Int) -> (negative: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_negative(a), nil; } int_is_even :: proc(a: ^Int) -> (even: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_even(a), nil; } int_is_odd :: proc(a: ^Int) -> (odd: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_odd(a), nil; @@ -455,7 +458,7 @@ platform_int_is_power_of_two :: #force_inline proc(a: int) -> bool { } int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { - if a == nil { return false, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_power_of_two(a), nil; @@ -465,7 +468,7 @@ int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { Compare two `Int`s, signed. */ int_compare :: proc(a, b: ^Int) -> (comparison: int, err: Error) { - if a == nil || b == nil { return 0, .Invalid_Pointer; } + assert_if_nil(a, b); if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } return #force_inline internal_cmp(a, b), nil; @@ -476,7 +479,7 @@ int_cmp :: int_compare; Compare an `Int` to an unsigned number upto the size of the backing type. */ int_compare_digit :: proc(a: ^Int, b: DIGIT) -> (comparison: int, err: Error) { - if a == nil { return 0, .Invalid_Pointer; } + assert_if_nil(a); if err = clear_if_uninitialized(a); err != nil { return 0, err; } return #force_inline internal_cmp_digit(a, b), nil; @@ -487,7 +490,7 @@ int_cmp_digit :: int_compare_digit; Compare the magnitude of two `Int`s, unsigned. */ int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { - if a == nil || b == nil { return 0, .Invalid_Pointer; } + assert_if_nil(a, b); if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } return #force_inline internal_cmp_mag(a, b), nil; From 1ebb0bd9d62ed4abae07c32886c6d3374adf642d Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 9 Aug 2021 22:20:53 +0200 Subject: [PATCH 096/105] big: More refactoring. --- core/math/big/internal.odin | 434 ++++++++++++++++++++++++++++++++++-- core/math/big/logical.odin | 385 ++++---------------------------- core/math/big/prime.odin | 6 +- core/math/big/public.odin | 66 +++--- core/math/big/radix.odin | 33 ++- core/math/big/test.odin | 38 ++-- 6 files changed, 530 insertions(+), 432 deletions(-) diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 9498e473c..39aa3bbfd 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -149,7 +149,8 @@ internal_add_signed :: proc { internal_int_add_signed, }; `dest` and `a` != `nil` and have been initalized. `dest` is large enough (a.used + 1) to fit result. */ -internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { +internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { + if err = internal_grow(dest, a.used + 1, false, allocator); err != nil { return err; } /* Fast paths for destination and input Int being the same. */ @@ -183,7 +184,7 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) { /* dest = |a| - digit */ - if err = #force_inline internal_int_add_digit(dest, a, digit); err != nil { + if err = #force_inline internal_int_add_digit(dest, a, digit, allocator); err != nil { /* Restore a's sign. */ @@ -367,7 +368,9 @@ internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := conte `dest`, `number` != `nil` and have been initalized. `dest` is large enough (number.used + 1) to fit result. */ -internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT) -> (err: Error) { +internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { + if err = internal_grow(dest, number.used + 1, false, allocator); err != nil { return err; } + dest := dest; digit := digit; /* All parameters have been initialized. @@ -1630,7 +1633,7 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al src := src; if err = error_if_immutable(dest); err != nil { return err; } - if err = clear_if_uninitialized(dest); err != nil { return err; } + if err = internal_clear_if_uninitialized_single(dest); err != nil { return err; } dest.flags = {}; // We're not -Inf, Inf, NaN or Immutable. @@ -1659,7 +1662,7 @@ internal_int_copy :: proc(dest, src: ^Int, minimize := false, allocator := conte if (dest == src) { return nil; } if err = error_if_immutable(dest); err != nil { return err; } - if err = clear_if_uninitialized(src); err != nil { return err; } + if err = internal_clear_if_uninitialized_single(src); err != nil { return err; } /* Grow `dest` to fit `src`. @@ -1707,7 +1710,7 @@ internal_int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (er /* Check that src is usable. */ - if err = clear_if_uninitialized(src); err != nil { + if err = internal_clear_if_uninitialized_single(src); err != nil { return err; } /* @@ -1744,7 +1747,7 @@ internal_int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (er /* Check that src is usable. */ - if err = clear_if_uninitialized(src); err != nil { + if err = internal_clear_if_uninitialized_single(src); err != nil { return err; } /* @@ -1788,7 +1791,7 @@ internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WOR /* Check that `a` is usable. */ - if err = clear_if_uninitialized(a); err != nil { return 0, err; } + if err = internal_clear_if_uninitialized_single(a); err != nil { return 0, err; } /* Early out for single bit. */ @@ -2046,7 +2049,7 @@ internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intr internal_get :: proc { internal_int_get, }; internal_int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { - if err = clear_if_uninitialized(a); err != nil { + if err = internal_clear_if_uninitialized_single(a); err != nil { return 0, err; } @@ -2062,11 +2065,410 @@ internal_int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { return; } +/* + The `and`, `or` and `xor` binops differ in two lines only. + We could handle those with a switch, but that adds overhead. + + TODO: Implement versions that take a DIGIT immediate. +*/ + +/* + 2's complement `and`, returns `dest = a & b;` +*/ +internal_int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + used := max(a.used, b.used) + 1; + /* + Grow the destination to accomodate the result. + */ + if err = internal_grow(dest, used, false, allocator); err != nil { return err; } + + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a && neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + + for i := 0; i < used; i += 1 { + x, y: DIGIT; + + /* + Convert to 2's complement if negative. + */ + if 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 neg_b { + 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; + return clamp(dest); +} +internal_and :: proc { internal_int_and, }; + +/* + 2's complement `or`, returns `dest = a | b;` +*/ +internal_int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + used := max(a.used, b.used) + 1; + /* + Grow the destination to accomodate the result. + */ + if err = grow(dest, used, false, allocator); err != nil { return err; } + + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a || neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + + for i := 0; i < used; i += 1 { + x, y: DIGIT; + + /* + Convert to 2's complement if negative. + */ + if 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 neg_b { + 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; + return clamp(dest); +} +internal_or :: proc { internal_int_or, }; + +/* + 2's complement `xor`, returns `dest = a ~ b;` +*/ +internal_int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + used := max(a.used, b.used) + 1; + /* + Grow the destination to accomodate the result. + */ + if err = grow(dest, used, false, allocator); err != nil { return err; } + + neg_a, _ := is_neg(a); + neg_b, _ := is_neg(b); + neg := neg_a != neg_b; + + ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); + + for i := 0; i < used; i += 1 { + x, y: DIGIT; + + /* + Convert to 2's complement if negative. + */ + if 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 neg_b { + 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; + return clamp(dest); +} +internal_xor :: proc { internal_int_xor, }; + +/* + dest = ~src +*/ +internal_int_complement :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + /* + 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 = internal_sub(dest, src, 1); + /* + Restore sign. + */ + src.sign = old_sign; + + return err; +} +internal_complement :: proc { internal_int_complement, }; + +/* + quotient, remainder := numerator >> bits; + `remainder` is allowed to be passed a `nil`, in which case `mod` won't be computed. +*/ +internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + bits := bits; + if bits < 0 { return .Invalid_Argument; } + + if err = internal_copy(quotient, numerator, true, allocator); err != nil { return err; } + + /* + Shift right by a certain bit count (store quotient and optional remainder.) + `numerator` should not be used after this. + */ + if remainder != nil { + if err = mod_bits(remainder, numerator, bits); err != nil { return err; } + } + + /* + Shift by as many digits in the bit count. + */ + if bits >= _DIGIT_BITS { + if err = shr_digit(quotient, bits / _DIGIT_BITS); err != nil { return err; } + } + + /* + Shift any bit count < _DIGIT_BITS. + */ + bits %= _DIGIT_BITS; + if bits != 0 { + mask := DIGIT(1 << uint(bits)) - 1; + shift := DIGIT(_DIGIT_BITS - bits); + carry := DIGIT(0); + + for x := quotient.used - 1; x >= 0; x -= 1 { + /* + Get the lower bits of this word in a temp. + */ + fwd_carry := quotient.digit[x] & mask; + + /* + Shift the current word and mix in the carry bits from the previous word. + */ + quotient.digit[x] = (quotient.digit[x] >> uint(bits)) | (carry << shift); + + /* + Update carry from forward carry. + */ + carry = fwd_carry; + } + + } + return clamp(numerator); +} +internal_shrmod :: proc { internal_int_shrmod, }; + +internal_int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + return internal_shrmod(dest, nil, source, bits, allocator); +} +internal_shr :: proc { internal_int_shr, }; + +/* + Shift right by `digits` * _DIGIT_BITS bits. +*/ +internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + if digits <= 0 { return nil; } + + /* + If digits > used simply zero and return. + */ + if digits > quotient.used { + return internal_zero(quotient, true, allocator); + } + + /* + Much like `int_shl_digit`, this is implemented using a sliding window, + except the window goes the other way around. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + + for x := 0; x < (quotient.used - digits); x += 1 { + quotient.digit[x] = quotient.digit[x + digits]; + } + quotient.used -= digits; + zero_unused(quotient); + return clamp(quotient); +} +internal_shr_digit :: proc { internal_int_shr_digit, }; + +/* + Shift right by a certain bit count with sign extension. +*/ +internal_int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + if src.sign == .Zero_or_Positive { + return internal_shr(dest, src, bits, allocator); + } + if err = internal_int_add_digit(dest, src, DIGIT(1), allocator); err != nil { return err; } + + if err = internal_shr(dest, dest, bits, allocator); err != nil { return err; } + return internal_sub(dest, src, DIGIT(1), allocator); +} + +internal_shr_signed :: proc { internal_int_shr_signed, }; + +/* + Shift left by a certain bit count. +*/ +internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + bits := bits; + + if bits < 0 { return .Invalid_Argument; } + + if err = copy(dest, src, false, allocator); err != nil { return err; } + + /* + Grow `dest` to accommodate the additional bits. + */ + digits_needed := dest.used + (bits / _DIGIT_BITS) + 1; + if err = grow(dest, digits_needed); err != nil { return err; } + dest.used = digits_needed; + /* + Shift by as many digits in the bit count as we have. + */ + if bits >= _DIGIT_BITS { + if err = shl_digit(dest, bits / _DIGIT_BITS); err != nil { return err; } + } + + /* + Shift any remaining bit count < _DIGIT_BITS + */ + bits %= _DIGIT_BITS; + if bits != 0 { + mask := (DIGIT(1) << uint(bits)) - DIGIT(1); + shift := DIGIT(_DIGIT_BITS - bits); + carry := DIGIT(0); + + for x:= 0; x < dest.used; x+= 1 { + fwd_carry := (dest.digit[x] >> shift) & mask; + dest.digit[x] = (dest.digit[x] << uint(bits) | carry) & _MASK; + carry = fwd_carry; + } + + /* + Use final carry. + */ + if carry != 0 { + dest.digit[dest.used] = carry; + dest.used += 1; + } + } + return clamp(dest); +} +internal_shl :: proc { internal_int_shl, }; + + +/* + Shift left by `digits` * _DIGIT_BITS bits. +*/ +internal_int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { + if digits <= 0 { return nil; } + + /* + No need to shift a zero. + */ + z: bool; + if z, err = is_zero(quotient); z || err != nil { return err; } + + /* + Resize `quotient` to accomodate extra digits. + */ + if err = grow(quotient, quotient.used + digits); err != nil { return err; } + + /* + Increment the used by the shift amount then copy upwards. + */ + + /* + Much like `int_shr_digit`, this is implemented using a sliding window, + except the window goes the other way around. + */ + for x := quotient.used; x > 0; x -= 1 { + quotient.digit[x+digits-1] = quotient.digit[x-1]; + } + + quotient.used += digits; + mem.zero_slice(quotient.digit[:digits]); + return nil; +} +internal_shl_digit :: proc { internal_int_shl_digit, }; + /* Count bits in an `Int`. */ internal_count_bits :: proc(a: ^Int) -> (count: int, err: Error) { - if err = clear_if_uninitialized(a); err != nil { + if err = internal_clear_if_uninitialized_single(a); err != nil { return 0, err; } /* @@ -2092,7 +2494,7 @@ internal_count_bits :: proc(a: ^Int) -> (count: int, err: Error) { Differs from regular `ctz` in that 0 returns 0. */ internal_int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { - if err = clear_if_uninitialized(a); err != nil { return -1, err; } + if err = internal_clear_if_uninitialized_single(a); err != nil { return -1, err; } _ctz :: intrinsics.count_trailing_zeros; /* @@ -2163,7 +2565,7 @@ internal_assert_initialized :: proc(a: ^Int, loc := #caller_location) { } internal_clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) -> (err: Error) { - if !internal_is_initialized(arg) { + if ! #force_inline internal_is_initialized(arg) { return #force_inline internal_grow(arg, _DEFAULT_DIGIT_COUNT, true, allocator); } return err; @@ -2171,7 +2573,7 @@ internal_clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.a internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { for i in args { - if !internal_is_initialized(i) { + if ! #force_inline internal_is_initialized(i) { e := #force_inline internal_grow(i, _DEFAULT_DIGIT_COUNT, true, allocator); if e != nil { err = e; } } @@ -2208,8 +2610,8 @@ internal_init_multi :: proc { internal_int_init_multi, }; _private_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { digits := digits; - if err = clear_if_uninitialized(src); err != nil { return err; } - if err = clear_if_uninitialized(dest); err != nil { return err; } + if err = internal_clear_if_uninitialized_single(src); err != nil { return err; } + if err = internal_clear_if_uninitialized_single(dest); err != nil { return err; } /* If dest == src, do nothing */ @@ -2229,7 +2631,7 @@ _private_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { Typically very fast. Also fixes the sign if there are no more leading digits. */ internal_clamp :: proc(a: ^Int) -> (err: Error) { - if err = internal_clear_if_uninitialized(a); err != nil { + if err = internal_clear_if_uninitialized_single(a); err != nil { return err; } for a.used > 0 && a.digit[a.used - 1] == 0 { diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 0ab9e2f9b..57adf6f1e 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -23,210 +23,48 @@ import "core:mem" /* 2's complement `and`, returns `dest = a & b;` */ -int_and :: proc(dest, a, b: ^Int) -> (err: Error) { +int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a, b); - if err = clear_if_uninitialized(a, b); err != nil { return err; } + if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } - used := max(a.used, b.used) + 1; - /* - Grow the destination to accomodate the result. - */ - if err = grow(dest, used); err != nil { return err; } - - neg_a, _ := is_neg(a); - neg_b, _ := is_neg(b); - neg := neg_a && neg_b; - - ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - - for i := 0; i < used; i += 1 { - x, y: DIGIT; - - /* - Convert to 2's complement if negative. - */ - if 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 neg_b { - 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; - return clamp(dest); + return #force_inline internal_int_and(dest, a, b, allocator); } -and :: proc { int_add, }; +and :: proc { int_and, }; /* 2's complement `or`, returns `dest = a | b;` */ -int_or :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a, b); err != nil { return err; } - used := max(a.used, b.used) + 1; - /* - Grow the destination to accomodate the result. - */ - if err = grow(dest, used); err != nil { return err; } +int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dest, a, b); + if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } - neg_a, _ := is_neg(a); - neg_b, _ := is_neg(b); - neg := neg_a || neg_b; - - ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - - for i := 0; i < used; i += 1 { - x, y: DIGIT; - - /* - Convert to 2's complement if negative. - */ - if 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 neg_b { - 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; - return clamp(dest); + return #force_inline internal_int_or(dest, a, b, allocator); } or :: proc { int_or, }; /* - 2's complement `xor`, returns `dest = a ~ b;` + 2's complement `xor`, returns `dest = a ^ b;` */ -int_xor :: proc(dest, a, b: ^Int) -> (err: Error) { - if err = clear_if_uninitialized(a, b); err != nil { return err; } +int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dest, a, b); + if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } - used := max(a.used, b.used) + 1; - /* - Grow the destination to accomodate the result. - */ - if err = grow(dest, used); err != nil { return err; } - - neg_a, _ := is_neg(a); - neg_b, _ := is_neg(b); - neg := neg_a != neg_b; - - ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - - for i := 0; i < used; i += 1 { - x, y: DIGIT; - - /* - Convert to 2's complement if negative. - */ - if 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 neg_b { - 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; - return clamp(dest); + return #force_inline internal_int_xor(dest, a, b, allocator); } xor :: proc { int_xor, }; /* dest = ~src */ -int_complement :: proc(dest, src: ^Int) -> (err: Error) { +int_complement :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* - Check that src is usable. Dest will get checked by `sub`. + Check that `src` and `dest` are usable. */ - if err = clear_if_uninitialized(src); err != nil { return err; } + assert_if_nil(dest, src); + if err = internal_clear_if_uninitialized(dest, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized(src, allocator); err != nil { 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; + return #force_inline internal_int_complement(dest, src); } complement :: proc { int_complement, }; @@ -234,115 +72,43 @@ complement :: proc { int_complement, }; quotient, remainder := numerator >> bits; `remainder` is allowed to be passed a `nil`, in which case `mod` won't be computed. */ -int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int) -> (err: Error) { - bits := bits; - if err = clear_if_uninitialized(quotient, numerator); err != nil { return err; } +int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(quotient, numerator); + if err = internal_clear_if_uninitialized(quotient, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized(numerator, allocator); err != nil { return err; } - if bits < 0 { return .Invalid_Argument; } - - if err = copy(quotient, numerator); err != nil { return err; } - - /* - Shift right by a certain bit count (store quotient and optional remainder.) - `numerator` should not be used after this. - */ - if remainder != nil { - if err = mod_bits(remainder, numerator, bits); err != nil { return err; } - } - - /* - Shift by as many digits in the bit count. - */ - if bits >= _DIGIT_BITS { - if err = shr_digit(quotient, bits / _DIGIT_BITS); err != nil { return err; } - } - - /* - Shift any bit count < _DIGIT_BITS. - */ - bits %= _DIGIT_BITS; - if bits != 0 { - mask := DIGIT(1 << uint(bits)) - 1; - shift := DIGIT(_DIGIT_BITS - bits); - carry := DIGIT(0); - - for x := quotient.used - 1; x >= 0; x -= 1 { - /* - Get the lower bits of this word in a temp. - */ - fwd_carry := quotient.digit[x] & mask; - - /* - Shift the current word and mix in the carry bits from the previous word. - */ - quotient.digit[x] = (quotient.digit[x] >> uint(bits)) | (carry << shift); - - /* - Update carry from forward carry. - */ - carry = fwd_carry; - } - - } - return clamp(numerator); + return #force_inline internal_int_shrmod(quotient, remainder, numerator, bits, allocator); } shrmod :: proc { int_shrmod, }; int_shr :: proc(dest, source: ^Int, bits: int) -> (err: Error) { - return shrmod(dest, nil, source, bits); + return #force_inline shrmod(dest, nil, source, bits); } shr :: proc { int_shr, }; /* Shift right by `digits` * _DIGIT_BITS bits. */ -int_shr_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { +int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { /* Check that `quotient` is usable. */ - if err = clear_if_uninitialized(quotient); err != nil { return err; } + assert_if_nil(quotient); + if err = internal_clear_if_uninitialized(quotient, allocator); err != nil { return err; } - if digits <= 0 { return nil; } - - /* - If digits > used simply zero and return. - */ - if digits > quotient.used { - return zero(quotient); - } - - /* - Much like `int_shl_digit`, this is implemented using a sliding window, - except the window goes the other way around. - - b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> - /\ | ----> - \-------------------/ ----> - */ - - for x := 0; x < (quotient.used - digits); x += 1 { - quotient.digit[x] = quotient.digit[x + digits]; - } - quotient.used -= digits; - zero_unused(quotient); - return clamp(quotient); + return #force_inline internal_int_shr_digit(quotient, digits, allocator); } shr_digit :: proc { int_shr_digit, }; /* Shift right by a certain bit count with sign extension. */ -int_shr_signed :: proc(dest, src: ^Int, bits: int) -> (err: Error) { - if err = clear_if_uninitialized(src); err != nil { return err; } - if err = clear_if_uninitialized(dest); err != nil { return err; } +int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dest, src); + if err = internal_clear_if_uninitialized(dest, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized(src, allocator); err != nil { return err; } - if src.sign == .Zero_or_Positive { - return shr(dest, src, bits); - } - if err = add(dest, src, DIGIT(1)); err != nil { return err; } - - if err = shr(dest, dest, bits); err != nil { return err; } - return sub(dest, src, DIGIT(1)); + return #force_inline internal_int_shr_signed(dest, src, bits); } shr_signed :: proc { int_shr_signed, }; @@ -350,53 +116,12 @@ shr_signed :: proc { int_shr_signed, }; /* Shift left by a certain bit count. */ -int_shl :: proc(dest, src: ^Int, bits: int) -> (err: Error) { - bits := bits; - if err = clear_if_uninitialized(src, dest); err != nil { return err; } +int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + assert_if_nil(dest, src); + if err = internal_clear_if_uninitialized(dest, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized(src, allocator); err != nil { return err; } - if bits < 0 { - return .Invalid_Argument; - } - - if err = copy(dest, src); err != nil { return err; } - - /* - Grow `dest` to accommodate the additional bits. - */ - digits_needed := dest.used + (bits / _DIGIT_BITS) + 1; - if err = grow(dest, digits_needed); err != nil { return err; } - dest.used = digits_needed; - /* - Shift by as many digits in the bit count as we have. - */ - if bits >= _DIGIT_BITS { - if err = shl_digit(dest, bits / _DIGIT_BITS); err != nil { return err; } - } - - /* - Shift any remaining bit count < _DIGIT_BITS - */ - bits %= _DIGIT_BITS; - if bits != 0 { - mask := (DIGIT(1) << uint(bits)) - DIGIT(1); - shift := DIGIT(_DIGIT_BITS - bits); - carry := DIGIT(0); - - for x:= 0; x < dest.used; x+= 1 { - fwd_carry := (dest.digit[x] >> shift) & mask; - dest.digit[x] = (dest.digit[x] << uint(bits) | carry) & _MASK; - carry = fwd_carry; - } - - /* - Use final carry. - */ - if carry != 0 { - dest.digit[dest.used] = carry; - dest.used += 1; - } - } - return clamp(dest); + return #force_inline internal_int_shl(dest, src, bits); } shl :: proc { int_shl, }; @@ -408,35 +133,9 @@ int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { /* Check that `quotient` is usable. */ - if err = clear_if_uninitialized(quotient); err != nil { return err; } + assert_if_nil(quotient); + if err = internal_clear_if_uninitialized(quotient); err != nil { return err; } - if digits <= 0 { return nil; } - - /* - No need to shift a zero. - */ - z: bool; - if z, err = is_zero(quotient); z || err != nil { return err; } - - /* - Resize `quotient` to accomodate extra digits. - */ - if err = grow(quotient, quotient.used + digits); err != nil { return err; } - - /* - Increment the used by the shift amount then copy upwards. - */ - - /* - Much like `int_shr_digit`, this is implemented using a sliding window, - except the window goes the other way around. - */ - for x := quotient.used; x > 0; x -= 1 { - quotient.digit[x+digits-1] = quotient.digit[x-1]; - } - - quotient.used += digits; - mem.zero_slice(quotient.digit[:digits]); - return nil; + return #force_inline internal_int_shl_digit(quotient, digits); } shl_digit :: proc { int_shl_digit, }; \ No newline at end of file diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index 355feecc2..7041b8c39 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -15,11 +15,13 @@ package big Determines if an Integer is divisible by one of the _PRIME_TABLE primes. Returns true if it is, false if not. */ -int_prime_is_divisible :: proc(a: ^Int) -> (res: bool, err: Error) { +int_prime_is_divisible :: proc(a: ^Int, allocator := context.allocator) -> (res: bool, err: Error) { + assert_if_nil(a); + if err = internal_clear_if_uninitialized(a, allocator); err != nil { return {}, err; } rem: DIGIT; for prime in _private_prime_table { - if rem, err = mod(a, prime); err != nil { return false, err; } + if rem, err = #force_inline int_mod_digit(a, prime); err != nil { return false, err; } if rem == 0 { return true, nil; } } /* diff --git a/core/math/big/public.odin b/core/math/big/public.odin index fdb7bcbd5..f5dbbe06d 100644 --- a/core/math/big/public.odin +++ b/core/math/big/public.odin @@ -22,7 +22,7 @@ package big */ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a, b); - if err = clear_if_uninitialized(dest, a, b); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, a, b); err != nil { return err; } /* All parameters have been initialized. */ @@ -37,7 +37,7 @@ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a); - if err = clear_if_uninitialized(a); err != nil { return err; } + if err = internal_clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. */ @@ -54,7 +54,7 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato */ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, number, decrease); - if err = clear_if_uninitialized(dest, number, decrease); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, number, decrease); err != nil { return err; } /* All parameters have been initialized. */ @@ -69,7 +69,7 @@ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a); - if err = clear_if_uninitialized(a); err != nil { return err; } + if err = internal_clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. */ @@ -87,7 +87,7 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato */ int_halve :: proc(dest, src: ^Int) -> (err: Error) { assert_if_nil(dest, src); - if err = clear_if_uninitialized(dest, src); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. */ @@ -104,7 +104,7 @@ shr1 :: halve; */ int_double :: proc(dest, src: ^Int) -> (err: Error) { assert_if_nil(dest, src); - if err = clear_if_uninitialized(dest, src); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. */ @@ -120,7 +120,7 @@ shl1 :: double; */ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src); - if err = clear_if_uninitialized(src, dest); err != nil { return err; } + if err = internal_clear_if_uninitialized(src, dest); err != nil { return err; } return #force_inline internal_int_mul_digit(dest, src, multiplier, allocator); } @@ -130,7 +130,7 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.a */ int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src, multiplier); - if err = clear_if_uninitialized(dest, src, multiplier); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, src, multiplier); err != nil { return err; } return #force_inline internal_int_mul(dest, src, multiplier, allocator); } @@ -148,14 +148,14 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E Early out if neither of the results is wanted. */ if quotient == nil && remainder == nil { return nil; } - if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } + if err = internal_clear_if_uninitialized(numerator, denominator); err != nil { return err; } return #force_inline internal_divmod(quotient, remainder, numerator, denominator); } int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { assert_if_nil(quotient, numerator); - if err = clear_if_uninitialized(numerator); err != nil { return 0, err; } + if err = internal_clear_if_uninitialized(numerator); err != nil { return 0, err; } return #force_inline internal_divmod(quotient, numerator, denominator); } @@ -163,14 +163,14 @@ divmod :: proc{ int_divmod, int_divmod_digit, }; int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { assert_if_nil(quotient, numerator, denominator); - if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } + if err = internal_clear_if_uninitialized(numerator, denominator); err != nil { return err; } return #force_inline internal_divmod(quotient, nil, numerator, denominator); } int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (err: Error) { assert_if_nil(quotient, numerator); - if err = clear_if_uninitialized(numerator); err != nil { return err; } + if err = internal_clear_if_uninitialized(numerator); err != nil { return err; } remainder: DIGIT; remainder, err = #force_inline internal_divmod(quotient, numerator, denominator); @@ -185,7 +185,7 @@ div :: proc { int_div, int_div_digit, }; */ int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { assert_if_nil(remainder, numerator, denominator); - if err = clear_if_uninitialized(numerator, denominator); err != nil { return err; } + if err = internal_clear_if_uninitialized(numerator, denominator); err != nil { return err; } return #force_inline internal_int_mod(remainder, numerator, denominator); } @@ -201,7 +201,7 @@ mod :: proc { int_mod, int_mod_digit, }; */ int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { assert_if_nil(remainder, number, addend); - if err = clear_if_uninitialized(number, addend, modulus); err != nil { return err; } + if err = internal_clear_if_uninitialized(number, addend, modulus); err != nil { return err; } return #force_inline internal_addmod(remainder, number, addend, modulus); } @@ -212,7 +212,7 @@ addmod :: proc { int_addmod, }; */ int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { assert_if_nil(remainder, number, decrease); - if err = clear_if_uninitialized(number, decrease, modulus); err != nil { return err; } + if err = internal_clear_if_uninitialized(number, decrease, modulus); err != nil { return err; } return #force_inline internal_submod(remainder, number, decrease, modulus); } @@ -223,7 +223,7 @@ submod :: proc { int_submod, }; */ int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { assert_if_nil(remainder, number, multiplicand); - if err = clear_if_uninitialized(number, multiplicand, modulus); err != nil { return err; } + if err = internal_clear_if_uninitialized(number, multiplicand, modulus); err != nil { return err; } return #force_inline internal_mulmod(remainder, number, multiplicand, modulus); } @@ -234,7 +234,7 @@ mulmod :: proc { int_mulmod, }; */ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { assert_if_nil(remainder, number, modulus); - if err = clear_if_uninitialized(number, modulus); err != nil { return err; } + if err = internal_clear_if_uninitialized(number, modulus); err != nil { return err; } return #force_inline internal_sqrmod(remainder, number, modulus); } @@ -295,8 +295,8 @@ int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.allocator if res_gcd == nil && res_lcm == nil { return nil; } assert_if_nil(a, b); - if err = clear_if_uninitialized(a, allocator); err != nil { return err; } - if err = clear_if_uninitialized(b, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized(a, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized(b, allocator); err != nil { return err; } return #force_inline internal_int_gcd_lcm(res_gcd, res_lcm, a, b); } gcd_lcm :: proc { int_gcd_lcm, }; @@ -323,7 +323,7 @@ lcm :: proc { int_lcm, }; int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { assert_if_nil(remainder, numerator); - if err = clear_if_uninitialized(remainder, numerator); err != nil { return err; } + if err = internal_clear_if_uninitialized(remainder, numerator); err != nil { return err; } if bits < 0 { return .Invalid_Argument; } return #force_inline internal_int_mod_bits(remainder, numerator, bits); @@ -337,7 +337,7 @@ mod_bits :: proc { int_mod_bits, }; */ int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return 0, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return 0, err; } return #force_inline internal_int_log(a, base); } @@ -352,7 +352,7 @@ log :: proc { int_log, digit_log, }; */ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { assert_if_nil(dest, base); - if err = clear_if_uninitialized(dest, base); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, base); err != nil { return err; } return #force_inline internal_int_pow(dest, base, power); } @@ -378,7 +378,7 @@ small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { */ int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { assert_if_nil(dest, src); - if err = clear_if_uninitialized(dest, src); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } return #force_inline internal_int_sqrt(dest, src); } @@ -402,7 +402,7 @@ int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { /* Initialize dest + src if needed. */ - if err = clear_if_uninitialized(dest, src); err != nil { return err; } + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } return #force_inline internal_int_root_n(dest, src, n); } @@ -420,35 +420,35 @@ int_is_initialized :: proc(a: ^Int) -> bool { int_is_zero :: proc(a: ^Int) -> (zero: bool, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return false, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_zero(a), nil; } int_is_positive :: proc(a: ^Int) -> (positive: bool, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return false, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_positive(a), nil; } int_is_negative :: proc(a: ^Int) -> (negative: bool, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return false, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_negative(a), nil; } int_is_even :: proc(a: ^Int) -> (even: bool, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return false, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_even(a), nil; } int_is_odd :: proc(a: ^Int) -> (odd: bool, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return false, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_odd(a), nil; } @@ -459,7 +459,7 @@ platform_int_is_power_of_two :: #force_inline proc(a: int) -> bool { int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return false, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_power_of_two(a), nil; } @@ -469,7 +469,7 @@ int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { */ int_compare :: proc(a, b: ^Int) -> (comparison: int, err: Error) { assert_if_nil(a, b); - if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } + if err = internal_clear_if_uninitialized(a, b); err != nil { return 0, err; } return #force_inline internal_cmp(a, b), nil; } @@ -480,7 +480,7 @@ int_cmp :: int_compare; */ int_compare_digit :: proc(a: ^Int, b: DIGIT) -> (comparison: int, err: Error) { assert_if_nil(a); - if err = clear_if_uninitialized(a); err != nil { return 0, err; } + if err = internal_clear_if_uninitialized(a); err != nil { return 0, err; } return #force_inline internal_cmp_digit(a, b), nil; } @@ -491,7 +491,7 @@ int_cmp_digit :: int_compare_digit; */ int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { assert_if_nil(a, b); - if err = clear_if_uninitialized(a, b); err != nil { return 0, err; } + if err = internal_clear_if_uninitialized(a, b); err != nil { return 0, err; } return #force_inline internal_cmp_mag(a, b), nil; } \ No newline at end of file diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index eb1bb2dfb..28c5a2559 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -18,10 +18,10 @@ import "core:mem" This version of `itoa` allocates one behalf of the caller. The caller must free the string. */ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { + assert_if_nil(a); + a := a; radix := radix; - if err = clear_if_uninitialized(a); err != nil { - return "", err; - } + if err = clear_if_uninitialized(a, allocator); err != nil { return "", err; } /* Radix defaults to 10. */ @@ -61,10 +61,10 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc This version of `itoa` allocates one behalf of the caller. The caller must free the string. */ int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) { + assert_if_nil(a); + a := a; radix := radix; - if err = clear_if_uninitialized(a); err != nil { - return "", err; - } + if err = clear_if_uninitialized(a, allocator); err != nil { return "", err; } /* Radix defaults to 10. */ @@ -96,10 +96,9 @@ int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocato and having to perform a buffer overflow check each character. */ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) { + assert_if_nil(a); a := a; radix := radix; size := size; - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } + if err = clear_if_uninitialized(a); err != nil { return 0, err; } /* Radix defaults to 10. */ @@ -237,27 +236,23 @@ int_to_cstring :: int_itoa_cstring; /* Read a string [ASCII] in a given radix. */ -int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { +int_atoi :: proc(res: ^Int, input: string, radix: i8, allocator := context.allocator) -> (err: Error) { input := input; /* Make sure the radix is ok. */ - if radix < 2 || radix > 64 { - return .Invalid_Argument; - } + if radix < 2 || radix > 64 { return .Invalid_Argument; } /* Set the integer to the default of zero. */ - if err = zero(res); err != nil { return err; } + if err = zero(res, false, allocator); err != nil { return err; } /* We'll interpret an empty string as zero. */ - if len(input) == 0 { - return nil; - } + if len(input) == 0 { return nil; } /* If the leading digit is a minus set the sign to negative. @@ -297,8 +292,8 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8) -> (err: Error) { break; } - if err = mul(res, res, DIGIT(radix)); err != nil { return err; } - if err = add(res, res, DIGIT(y)); err != nil { return err; } + if err = internal_mul(res, res, DIGIT(radix)); err != nil { return err; } + if err = internal_add(res, res, DIGIT(y)); err != nil { return err; } input = input[1:]; } diff --git a/core/math/big/test.odin b/core/math/big/test.odin index de536f521..4aa0b93bf 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -45,9 +45,9 @@ PyRes :: struct { if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err}; } if bb.used == 1 { - if err = add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } + if err = #force_inline internal_add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } } else { - if err = add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } + if err = #force_inline internal_add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; } } r: cstring; @@ -66,9 +66,9 @@ PyRes :: struct { if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err}; } if bb.used == 1 { - if err = sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } + if err = #force_inline internal_sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } } else { - if err = sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } + if err = #force_inline internal_sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; } } r: cstring; @@ -86,7 +86,7 @@ PyRes :: struct { if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err}; } - if err = mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err}; } + if err = #force_inline internal_mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(product, 16, context.temp_allocator); @@ -106,7 +106,7 @@ PyRes :: struct { if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err}; } - if err = div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err}; } + if err = #force_inline internal_div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err}; } r: cstring; r, err = int_itoa_cstring(quotient, 16, context.temp_allocator); @@ -127,9 +127,9 @@ PyRes :: struct { defer destroy(aa); if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err}; } - if l, err = log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err}; } + if l, err = #force_inline internal_log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err}; } - zero(aa); + #force_inline internal_zero(aa); aa.digit[0] = DIGIT(l) & _MASK; aa.digit[1] = DIGIT(l) >> _DIGIT_BITS; aa.used = 2; @@ -152,7 +152,7 @@ PyRes :: struct { defer destroy(dest, bb); if err = atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err}; } - if err = pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err}; } + if err = #force_inline internal_pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); @@ -171,7 +171,7 @@ PyRes :: struct { defer destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err}; } - if err = sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err}; } + if err = #force_inline internal_sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); @@ -190,7 +190,7 @@ PyRes :: struct { defer destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err}; } - if err = root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err}; } + if err = #force_inline internal_root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); @@ -209,7 +209,7 @@ PyRes :: struct { defer destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err}; } - if err = shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; } + if err = #force_inline internal_shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); @@ -228,7 +228,7 @@ PyRes :: struct { defer destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err}; } - if err = shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; } + if err = #force_inline internal_shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); @@ -247,7 +247,7 @@ PyRes :: struct { defer destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err}; } - if err = shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err}; } + if err = #force_inline internal_shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); @@ -266,7 +266,7 @@ PyRes :: struct { defer destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err}; } - if err = shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; } + if err = #force_inline internal_shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); @@ -285,7 +285,7 @@ PyRes :: struct { defer destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err}; } - if err = shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err}; } + if err = #force_inline internal_shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err}; } r: cstring; r, err = int_itoa_cstring(src, 16, context.temp_allocator); @@ -303,7 +303,7 @@ PyRes :: struct { dest := &Int{}; defer destroy(dest); - if err = factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err}; } + if err = #force_inline factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); @@ -323,7 +323,7 @@ PyRes :: struct { if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err}; } if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err}; } - if err = gcd(dest, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err}; } + if err = #force_inline gcd(dest, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); @@ -343,7 +343,7 @@ PyRes :: struct { if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err}; } if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err}; } - if err = lcm(dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err}; } + if err = #force_inline lcm(dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); From 19ff27788c329e6d05659edb0dcee5c1550263d0 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 10 Aug 2021 01:22:02 +0200 Subject: [PATCH 097/105] big: Refactoring. --- core/math/big/helpers.odin | 2 +- core/math/big/internal.odin | 424 ++++++++++++++++-------------------- core/math/big/private.odin | 38 ++-- 3 files changed, 206 insertions(+), 258 deletions(-) diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index dd8f70f4b..e6b73430e 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -336,7 +336,7 @@ count_bits :: proc(a: ^Int, allocator := context.allocator) -> (count: int, err: assert_if_nil(a); if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return 0, err; } - return #force_inline internal_count_bits(a); + return #force_inline internal_count_bits(a), nil; } /* diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 39aa3bbfd..e916f9115 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -46,14 +46,13 @@ internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocat if x.used < y.used { x, y = y, x; - assert(x.used >= y.used); } min_used = y.used; max_used = x.used; old_used = dest.used; - if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } + if err = internal_grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } dest.used = max_used + 1; /* All parameters have been initialized. @@ -108,7 +107,7 @@ internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocat /* Adjust dest.used based on leading zeroes. */ - return clamp(dest); + return internal_clamp(dest); } internal_add_unsigned :: proc { internal_int_add_unsigned, }; @@ -133,7 +132,7 @@ internal_int_add_signed :: proc(dest, a, b: ^Int, allocator := context.allocator Subtract the one with the greater magnitude from the other. The result gets the sign of the one with the greater magnitude. */ - if c, _ := #force_inline cmp_mag(a, b); c == -1 { + if #force_inline internal_cmp_mag(a, b) == -1 { x, y = y, x; } @@ -161,7 +160,7 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context if dest.sign == .Zero_or_Positive && (dest.digit[0] + digit < _DIGIT_MAX) { dest.digit[0] += digit; dest.used += 1; - return clamp(dest); + return internal_clamp(dest); } /* Can be subtracted from dest.digit[0] without underflow. @@ -169,7 +168,7 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context if a.sign == .Negative && (dest.digit[0] > digit) { dest.digit[0] -= digit; dest.used += 1; - return clamp(dest); + return internal_clamp(dest); } } @@ -197,7 +196,7 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context a.sign = .Negative; dest.sign = .Negative; - return clamp(dest); + return internal_clamp(dest); } /* @@ -250,7 +249,7 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context /* Adjust dest.used based on leading zeroes. */ - return clamp(dest); + return internal_clamp(dest); } internal_add :: proc { internal_int_add_signed, internal_int_add_digit, }; @@ -317,7 +316,7 @@ internal_int_sub_unsigned :: proc(dest, number, decrease: ^Int, allocator := con /* Adjust dest.used based on leading zeroes. */ - return clamp(dest); + return internal_clamp(dest); } internal_sub_unsigned :: proc { internal_int_sub_unsigned, }; @@ -441,7 +440,7 @@ internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT, allocator := co /* Adjust dest.used based on leading zeroes. */ - return clamp(dest); + return internal_clamp(dest); } internal_sub :: proc { internal_int_sub_signed, internal_int_sub_digit, }; @@ -482,7 +481,7 @@ internal_int_shr1 :: proc(dest, src: ^Int) -> (err: Error) { Adjust dest.used based on leading zeroes. */ dest.sign = src.sign; - return clamp(dest); + return internal_clamp(dest); } /* @@ -514,7 +513,7 @@ internal_int_shl1 :: proc(dest, src: ^Int) -> (err: Error) { dest.digit[dest.used] = carry; dest.used += 1; } - return clamp(dest); + return internal_clamp(dest); } /* @@ -589,7 +588,7 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := */ internal_zero_unused(dest, old_used); - return clamp(dest); + return internal_clamp(dest); } /* @@ -663,17 +662,18 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc internal_mul :: proc { internal_int_mul, internal_int_mul_digit, }; -internal_sqr :: proc (dest, src: ^Int) -> (res: Error) { +internal_sqr :: proc (dest, src: ^Int, allocator := context.allocator) -> (res: Error) { /* We call `internal_mul` and not e.g. `_private_int_sqr` because the former will dispatch to the optimal implementation depending on the source. */ - return #force_inline internal_mul(dest, src, src); + return #force_inline internal_mul(dest, src, src, allocator); } /* divmod. Both the quotient and remainder are optional and may be passed a nil. + `numerator` and `denominator` are expected not to be `nil` and have been initialized. */ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { @@ -681,13 +681,12 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a /* If numerator < denominator then quotient = 0, remainder = numerator. */ - c: int; - if c, err = #force_inline cmp_mag(numerator, denominator); c == -1 { + if #force_inline internal_cmp_mag(numerator, denominator) == -1 { if remainder != nil { - if err = copy(remainder, numerator, false, allocator); err != nil { return err; } + if err = internal_copy(remainder, numerator, false, allocator); err != nil { return err; } } if quotient != nil { - zero(quotient); + internal_zero(quotient); } return nil; } @@ -798,8 +797,8 @@ internal_divmod :: proc { internal_int_divmod, internal_int_divmod_digit, }; /* Asssumes quotient, numerator and denominator to have been initialized and not to be nil. */ -internal_int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { - return #force_inline internal_int_divmod(quotient, nil, numerator, denominator); +internal_int_div :: proc(quotient, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { + return #force_inline internal_int_divmod(quotient, nil, numerator, denominator, allocator); } internal_div :: proc { internal_int_div, }; @@ -810,48 +809,48 @@ internal_div :: proc { internal_int_div, }; Asssumes quotient, numerator and denominator to have been initialized and not to be nil. */ -internal_int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { - if err = #force_inline internal_int_divmod(nil, remainder, numerator, denominator); err != nil { return err; } +internal_int_mod :: proc(remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { + if err = #force_inline internal_int_divmod(nil, remainder, numerator, denominator, allocator); err != nil { return err; } if remainder.used == 0 || denominator.sign == remainder.sign { return nil; } - return #force_inline internal_add(remainder, remainder, numerator); + return #force_inline internal_add(remainder, remainder, numerator, allocator); } internal_mod :: proc{ internal_int_mod, }; /* remainder = (number + addend) % modulus. */ -internal_int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { - if err = #force_inline internal_add(remainder, number, addend); err != nil { return err; } - return #force_inline internal_mod(remainder, remainder, modulus); +internal_int_addmod :: proc(remainder, number, addend, modulus: ^Int, allocator := context.allocator) -> (err: Error) { + if err = #force_inline internal_add(remainder, number, addend, allocator); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus, allocator); } internal_addmod :: proc { internal_int_addmod, }; /* remainder = (number - decrease) % modulus. */ -internal_int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { - if err = #force_inline internal_sub(remainder, number, decrease); err != nil { return err; } - return #force_inline internal_mod(remainder, remainder, modulus); +internal_int_submod :: proc(remainder, number, decrease, modulus: ^Int, allocator := context.allocator) -> (err: Error) { + if err = #force_inline internal_sub(remainder, number, decrease, allocator); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus, allocator); } internal_submod :: proc { internal_int_submod, }; /* remainder = (number * multiplicand) % modulus. */ -internal_int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { - if err = #force_inline internal_mul(remainder, number, multiplicand); err != nil { return err; } - return #force_inline internal_mod(remainder, remainder, modulus); +internal_int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int, allocator := context.allocator) -> (err: Error) { + if err = #force_inline internal_mul(remainder, number, multiplicand, allocator); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus, allocator); } internal_mulmod :: proc { internal_int_mulmod, }; /* remainder = (number * number) % modulus. */ -internal_int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { - if err = #force_inline internal_sqr(remainder, number); err != nil { return err; } - return #force_inline internal_mod(remainder, remainder, modulus); +internal_int_sqrmod :: proc(remainder, number, modulus: ^Int, allocator := context.allocator) -> (err: Error) { + if err = #force_inline internal_sqr(remainder, number, allocator); err != nil { return err; } + return #force_inline internal_mod(remainder, remainder, modulus, allocator); } internal_sqrmod :: proc { internal_int_sqrmod, }; @@ -861,19 +860,19 @@ internal_sqrmod :: proc { internal_int_sqrmod, }; TODO: Use Sterling's Approximation to estimate log2(N!) to size the result. This way we'll have to reallocate less, possibly not at all. */ -internal_int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { +internal_int_factorial :: proc(res: ^Int, n: int, allocator := context.allocator) -> (err: Error) { if n >= FACTORIAL_BINARY_SPLIT_CUTOFF { - return #force_inline _private_int_factorial_binary_split(res, n); + return #force_inline _private_int_factorial_binary_split(res, n, allocator); } i := len(_factorial_table); if n < i { - return #force_inline set(res, _factorial_table[n]); + return #force_inline internal_set(res, _factorial_table[n], true, allocator); } - if err = #force_inline set(res, _factorial_table[i - 1]); err != nil { return err; } + if err = #force_inline internal_set(res, _factorial_table[i - 1], true, allocator); err != nil { return err; } for { - if err = #force_inline internal_mul(res, res, DIGIT(i)); err != nil || i == n { return err; } + if err = #force_inline internal_mul(res, res, DIGIT(i), allocator); err != nil || i == n { return err; } i += 1; } @@ -901,12 +900,12 @@ internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Er /* Everything is divisible by 1 << 0 == 1, so this returns 0. */ - if bits == 0 { return zero(remainder); } + if bits == 0 { return internal_zero(remainder); } /* If the modulus is larger than the value, return the value. */ - err = copy(remainder, numerator); + err = internal_copy(remainder, numerator); if bits >= (numerator.used * _DIGIT_BITS) || err != nil { return; } @@ -918,7 +917,7 @@ internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Er zero_count += 0 if (bits % _DIGIT_BITS == 0) else 1; /* - Zero remainder. Special case, can't use `zero_unused`. + Zero remainder. Special case, can't use `internal_zero_unused`. */ if zero_count > 0 { mem.zero_slice(remainder.digit[zero_count:]); @@ -928,7 +927,7 @@ internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Er Clear the digit that is not completely outside/inside the modulus. */ remainder.digit[bits / _DIGIT_BITS] &= DIGIT(1 << DIGIT(bits % _DIGIT_BITS)) - DIGIT(1); - return clamp(remainder); + return internal_clamp(remainder); } /* @@ -1333,7 +1332,7 @@ internal_small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { This function is less generic than `root_n`, simpler and faster. Assumes `dest` and `src` not to be `nil` and to have been initialized. */ -internal_int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { +internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Must be positive. */ @@ -1348,13 +1347,12 @@ internal_int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { Set up temporaries. */ x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(x, y, t1, t2); + defer internal_destroy(x, y, t1, t2); - count: int; - if count, err = count_bits(src); err != nil { return err; } + count := #force_inline internal_count_bits(src); a, b := count >> 1, count & 1; - if err = power_of_two(x, a+b); err != nil { return err; } + if err = internal_int_power_of_two(x, a+b, allocator); err != nil { return err; } for { /* @@ -1362,16 +1360,16 @@ internal_int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { */ internal_div(t1, src, x); internal_add(t2, t1, x); - shr(y, t2, 1); + internal_shr(y, t2, 1); if c := internal_cmp(y, x); c == 0 || c == 1 { - swap(dest, x); + internal_swap(dest, x); return nil; } - swap(x, y); + internal_swap(x, y); } - swap(dest, x); + internal_swap(dest, x); return err; } internal_sqrt :: proc { internal_int_sqrt, }; @@ -1386,11 +1384,11 @@ internal_sqrt :: proc { internal_int_sqrt, }; Assumes `dest` and `src` not to be `nil` and have been initialized. */ -internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { +internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.allocator) -> (err: Error) { /* Fast path for n == 2 */ - if n == 2 { return #force_inline internal_sqrt(dest, src); } + if n == 2 { return #force_inline internal_sqrt(dest, src, allocator); } if n < 0 || n > int(_DIGIT_MAX) { return .Invalid_Argument; } @@ -1400,7 +1398,7 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { Set up temporaries. */ t1, t2, t3, a := &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(t1, t2, t3); + defer internal_destroy(t1, t2, t3); /* If `src` is negative fudge the sign but keep track. @@ -1423,21 +1421,20 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { /* Compute seed: 2^(log_2(src)/n + 2) */ - ilog2: int; - ilog2, err = count_bits(src); + ilog2 := internal_count_bits(src); /* "src" is smaller than max(int), we can cast safely. */ if ilog2 < n { - err = set(dest, 1); + err = internal_one(dest, true, allocator); dest.sign = a.sign; return err; } ilog2 /= n; if ilog2 == 0 { - err = set(dest, 1); + err = internal_one(dest, true, allocator); dest.sign = a.sign; return err; } @@ -1446,13 +1443,13 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { Start value must be larger than root. */ ilog2 += 2; - if err = power_of_two(t2, ilog2); err != nil { return err; } + if err = power_of_two(t2, ilog2, allocator); err != nil { return err; } c: int; iterations := 0; for { /* t1 = t2 */ - if err = copy(t1, t2); err != nil { return err; } + if err = copy(t1, t2, false, allocator); err != nil { return err; } /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ @@ -1461,27 +1458,26 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { /* numerator */ /* t2 = t1**b */ - if err = internal_mul(t2, t1, t3); err != nil { return err; } + if err = internal_mul(t2, t1, t3, allocator); err != nil { return err; } /* t2 = t1**b - a */ - if err = internal_sub(t2, t2, a); err != nil { return err; } + if err = internal_sub(t2, t2, a, allocator); err != nil { return err; } /* denominator */ /* t3 = t1**(b-1) * b */ - if err = internal_mul(t3, t3, DIGIT(n)); err != nil { return err; } + if err = internal_mul(t3, t3, DIGIT(n), allocator); err != nil { return err; } /* t3 = (t1**b - a)/(b * t1**(b-1)) */ - if err = internal_div(t3, t2, t3); err != nil { return err; } - if err = internal_sub(t2, t1, t3); err != nil { return err; } + if err = internal_div(t3, t2, t3, allocator); err != nil { return err; } + if err = internal_sub(t2, t1, t3, allocator); err != nil { return err; } /* Number of rounds is at most log_2(root). If it is more it got stuck, so break out of the loop and do the rest manually. */ - if ilog2 -= 1; ilog2 == 0 { - break; - } - if c = internal_cmp(t1, t2); c == 0 { break; } + if ilog2 -= 1; ilog2 == 0 { break; } + if internal_cmp(t1, t2) == 0 { break; } + iterations += 1; if iterations == MAX_ITERATIONS_ROOT_N { return .Max_Iterations_Reached; @@ -1512,16 +1508,15 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } iterations = 0; - /* Correct overshoot from above or from recurrence. */ + /* + Correct overshoot from above or from recurrence. + */ for { if err = internal_pow(t2, t1, n); err != nil { return err; } - - c = internal_cmp(t2, a); - if c == 1 { - if err = internal_sub(t1, t1, DIGIT(1)); err != nil { return err; } - } else { - break; - } + + if internal_cmp(t2, a) != 1 { break; } + + if err = internal_sub(t1, t1, DIGIT(1)); err != nil { return err; } iterations += 1; if iterations == MAX_ITERATIONS_ROOT_N { @@ -1529,10 +1524,14 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { } } - /* Set the result. */ - swap(dest, t1); + /* + Set the result. + */ + internal_swap(dest, t1); - /* set the sign of the result */ + /* + Set the sign of the result. + */ dest.sign = src.sign; return err; @@ -1543,7 +1542,7 @@ internal_root_n :: proc { internal_int_root_n, }; Internal implementation of log. Assumes `a` not to be `nil` and to have been initialized. */ -private_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { +private_int_log :: proc(a: ^Int, base: DIGIT, allocator := context.allocator) -> (res: int, err: Error) { bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); @@ -1552,10 +1551,11 @@ private_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { return 1 if ic == 0 else 0, nil; } - if err = set(bi_base, base); err != nil { return -1, err; } - if err = init_multi(bracket_mid, t); err != nil { return -1, err; } - if err = set(bracket_low, 1); err != nil { return -1, err; } - if err = set(bracket_high, base); err != nil { return -1, err; } + if err = internal_set(bi_base, base, true, allocator); err != nil { return -1, err; } + if err = internal_clear(bracket_mid, false, allocator); err != nil { return -1, err; } + if err = internal_clear(t, false, allocator); err != nil { return -1, err; } + if err = internal_one(bracket_low, false, allocator); err != nil { return -1, err; } + if err = internal_set(bracket_high, base, false, allocator); err != nil { return -1, err; } low := 0; high := 1; @@ -1570,10 +1570,10 @@ private_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { /* Iterate until `a` is bracketed between low + high. */ - if bc := #force_inline internal_cmp(bracket_high, a); bc != -1 { break; } + if #force_inline internal_cmp(bracket_high, a) != -1 { break; } low = high; - if err = copy(bracket_low, bracket_high); err != nil { return -1, err; } + if err = #force_inline internal_copy(bracket_low, bracket_high); err != nil { return -1, err; } high <<= 1; if err = #force_inline internal_sqr(bracket_high, bracket_high); err != nil { return -1, err; } } @@ -1586,15 +1586,16 @@ private_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { if err = #force_inline internal_mul(bracket_mid, bracket_low, t); err != nil { return -1, err; } mc := #force_inline internal_cmp(a, bracket_mid); - if mc == -1 { + switch mc { + case -1: high = mid; - swap(bracket_mid, bracket_high); - } - if mc == 1 { + internal_swap(bracket_mid, bracket_high); + case 0: + return mid, nil; + case 1: low = mid; - swap(bracket_mid, bracket_low); + internal_swap(bracket_mid, bracket_low); } - if mc == 0 { return mid, nil; } } fc := #force_inline internal_cmp(bracket_high, a); @@ -1632,21 +1633,25 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al where intrinsics.type_is_integer(T) { src := src; - if err = error_if_immutable(dest); err != nil { return err; } - if err = internal_clear_if_uninitialized_single(dest); err != nil { return err; } + if err = internal_error_if_immutable(dest); err != nil { return err; } + /* + Most internal procs asssume an Int to have already been initialize, + but as this is one of the procs that initializes, we have to check the following. + */ + if err = internal_clear_if_uninitialized_single(dest, allocator); err != nil { return err; } dest.flags = {}; // We're not -Inf, Inf, NaN or Immutable. dest.used = 0; dest.sign = .Zero_or_Positive if src >= 0 else .Negative; - src = abs(src); + src = internal_abs(src); - for src != 0 { + #no_bounds_check for src != 0 { dest.digit[dest.used] = DIGIT(src) & _MASK; dest.used += 1; src >>= _DIGIT_BITS; } - zero_unused(dest); + internal_zero_unused(dest); return nil; } @@ -1661,8 +1666,7 @@ internal_int_copy :: proc(dest, src: ^Int, minimize := false, allocator := conte */ if (dest == src) { return nil; } - if err = error_if_immutable(dest); err != nil { return err; } - if err = internal_clear_if_uninitialized_single(src); err != nil { return err; } + if err = internal_error_if_immutable(dest); err != nil { return err; } /* Grow `dest` to fit `src`. @@ -1670,21 +1674,19 @@ internal_int_copy :: proc(dest, src: ^Int, minimize := false, allocator := conte */ needed := src.used if minimize else max(src.used, _DEFAULT_DIGIT_COUNT); - if err = grow(dest, needed, minimize, allocator); err != nil { - return err; - } + if err = internal_grow(dest, needed, minimize, allocator); err != nil { return err; } /* Copy everything over and zero high digits. */ - for v, i in src.digit[:src.used] { + #no_bounds_check for v, i in src.digit[:src.used] { dest.digit[i] = v; } dest.used = src.used; dest.sign = src.sign; dest.flags = src.flags &~ {.Immutable}; - zero_unused(dest); + internal_zero_unused(dest); return nil; } internal_copy :: proc { internal_int_copy, }; @@ -1694,7 +1696,7 @@ internal_copy :: proc { internal_int_copy, }; However, that only swaps within the current scope. This helper swaps completely. */ -internal_int_swap :: proc(a, b: ^Int) { +internal_int_swap :: #force_inline proc(a, b: ^Int) { a := a; b := b; a.used, b.used = b.used, a.used; @@ -1707,12 +1709,6 @@ internal_swap :: proc { internal_int_swap, }; Set `dest` to |`src`|. */ internal_int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { - /* - Check that src is usable. - */ - if err = internal_clear_if_uninitialized_single(src); err != nil { - return err; - } /* If `dest == src`, just fix `dest`'s sign. */ @@ -1724,7 +1720,7 @@ internal_int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (er /* Copy `src` to `dest` */ - if err = copy(dest, src, false, allocator); err != nil { + if err = internal_copy(dest, src, false, allocator); err != nil { return err; } @@ -1744,32 +1740,21 @@ internal_abs :: proc{ internal_int_abs, internal_platform_abs, }; Set `dest` to `-src`. */ internal_int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { - /* - Check that src is usable. - */ - if err = internal_clear_if_uninitialized_single(src); err != nil { - return err; - } /* If `dest == src`, just fix `dest`'s sign. */ sign := Sign.Zero_or_Positive; - if z, _ := is_zero(src); z { + if #force_inline internal_is_zero(src) || #force_inline internal_is_negative(src) { sign = .Negative; } - if n, _ := is_neg(src); n { - sign = .Negative; - } - if (dest == src) { + if dest == src { dest.sign = sign; return nil; } /* Copy `src` to `dest` */ - if err = copy(dest, src, false, allocator); err != nil { - return err; - } + if err = internal_copy(dest, src, false, allocator); err != nil { return err; } /* Fix sign. @@ -1784,14 +1769,10 @@ internal_neg :: proc { internal_int_neg, }; Helpers to extract values from the `Int`. */ internal_int_bitfield_extract_single :: proc(a: ^Int, offset: int) -> (bit: _WORD, err: Error) { - return int_bitfield_extract(a, offset, 1); + return #force_inline int_bitfield_extract(a, offset, 1); } -internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { - /* - Check that `a` is usable. - */ - if err = internal_clear_if_uninitialized_single(a); err != nil { return 0, err; } +internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) #no_bounds_check { /* Early out for single bit. */ @@ -1855,9 +1836,7 @@ internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WOR internal_int_shrink :: proc(a: ^Int) -> (err: Error) { needed := max(_MIN_DIGIT_COUNT, a.used); - if a.used != needed { - return grow(a, needed); - } + if a.used != needed { return internal_grow(a, needed); } return nil; } internal_shrink :: proc { internal_int_shrink, }; @@ -1876,11 +1855,13 @@ internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator /* If not yet iniialized, initialize the `digit` backing with the allocator we were passed. - Otherwise, `[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing. */ if raw.cap == 0 { a.digit = mem.make_dynamic_array_len_cap([dynamic]DIGIT, needed, needed, allocator); } else if raw.cap != needed { + /* + `[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing. + */ resize(&a.digit, needed); } /* @@ -1922,7 +1903,7 @@ internal_one :: proc { internal_int_one, }; Set the `Int` to -1 and optionally shrink it to the minimum backing size. */ internal_int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - return internal_set(a, -1, minimize, allocator); + return internal_copy(a, INT_MINUS_ONE, minimize, allocator); } internal_minus_one :: proc { internal_int_minus_one, }; @@ -1930,9 +1911,7 @@ internal_minus_one :: proc { internal_int_minus_one, }; Set the `Int` to Inf and optionally shrink it to the minimum backing size. */ internal_int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - err = internal_set(a, 1, minimize, allocator); - a.flags |= { .Inf, }; - return err; + return internal_copy(a, INT_INF, minimize, allocator); } internal_inf :: proc { internal_int_inf, }; @@ -1940,9 +1919,7 @@ internal_inf :: proc { internal_int_inf, }; Set the `Int` to -Inf and optionally shrink it to the minimum backing size. */ internal_int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - err = internal_set(a, -1, minimize, allocator); - a.flags |= { .Inf, }; - return err; + return internal_copy(a, INT_MINUS_INF, minimize, allocator); } internal_minus_inf :: proc { internal_int_inf, }; @@ -1950,29 +1927,18 @@ internal_minus_inf :: proc { internal_int_inf, }; Set the `Int` to NaN and optionally shrink it to the minimum backing size. */ internal_int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { - err = internal_set(a, 1, minimize, allocator); - a.flags |= { .NaN, }; - return err; + return internal_copy(a, INT_NAN, minimize, allocator); } internal_nan :: proc { internal_int_nan, }; internal_int_power_of_two :: proc(a: ^Int, power: int, allocator := context.allocator) -> (err: Error) { - /* - Check that `a` is usable. - */ - assert_if_nil(a); - - if power < 0 || power > _MAX_BIT_COUNT { - return .Invalid_Argument; - } + if power < 0 || power > _MAX_BIT_COUNT { return .Invalid_Argument; } /* Grow to accomodate the single bit. */ a.used = (power / _DIGIT_BITS) + 1; - if err = internal_grow(a, a.used, false, allocator); err != nil { - return err; - } + if err = internal_grow(a, a.used, false, allocator); err != nil { return err; } /* Zero the entirety. */ @@ -2024,7 +1990,7 @@ internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intr i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS); i = min(int(a.used), i); - for ; i >= 0; i -= 1 { + #no_bounds_check for ; i >= 0; i -= 1 { res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS; res |= T(a.digit[i]); if size_in_bits <= _DIGIT_BITS { @@ -2040,24 +2006,18 @@ internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intr /* Set the sign. */ - if a.sign == .Negative { - res = -res; - } + if a.sign == .Negative { res = -res; } } return; } internal_get :: proc { internal_int_get, }; internal_int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { - if err = internal_clear_if_uninitialized_single(a); err != nil { - return 0, err; - } - l := min(a.used, 17); // log2(max(f64)) is approximately 1020, or 17 legs. fac := f64(1 << _DIGIT_BITS); d := 0.0; - for i := l; i >= 0; i -= 1 { + #no_bounds_check for i := l; i >= 0; i -= 1 { d = (d * fac) + f64(a.digit[i]); } @@ -2082,13 +2042,13 @@ internal_int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (e */ if err = internal_grow(dest, used, false, allocator); err != nil { return err; } - neg_a, _ := is_neg(a); - neg_b, _ := is_neg(b); - neg := neg_a && neg_b; + neg_a := #force_inline internal_is_negative(a); + neg_b := #force_inline internal_is_negative(b); + neg := neg_a && neg_b; ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - for i := 0; i < used; i += 1 { + #no_bounds_check for i := 0; i < used; i += 1 { x, y: DIGIT; /* @@ -2127,7 +2087,7 @@ internal_int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (e dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - return clamp(dest); + return internal_clamp(dest); } internal_and :: proc { internal_int_and, }; @@ -2139,15 +2099,15 @@ internal_int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (er /* Grow the destination to accomodate the result. */ - if err = grow(dest, used, false, allocator); err != nil { return err; } + if err = internal_grow(dest, used, false, allocator); err != nil { return err; } - neg_a, _ := is_neg(a); - neg_b, _ := is_neg(b); - neg := neg_a || neg_b; + neg_a := #force_inline internal_is_negative(a); + neg_b := #force_inline internal_is_negative(b); + neg := neg_a || neg_b; ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - for i := 0; i < used; i += 1 { + #no_bounds_check for i := 0; i < used; i += 1 { x, y: DIGIT; /* @@ -2186,7 +2146,7 @@ internal_int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (er dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - return clamp(dest); + return internal_clamp(dest); } internal_or :: proc { internal_int_or, }; @@ -2198,15 +2158,15 @@ internal_int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (e /* Grow the destination to accomodate the result. */ - if err = grow(dest, used, false, allocator); err != nil { return err; } + if err = internal_grow(dest, used, false, allocator); err != nil { return err; } - neg_a, _ := is_neg(a); - neg_b, _ := is_neg(b); - neg := neg_a != neg_b; + neg_a := #force_inline internal_is_negative(a); + neg_b := #force_inline internal_is_negative(b); + neg := neg_a != neg_b; ac, bc, cc := DIGIT(1), DIGIT(1), DIGIT(1); - for i := 0; i < used; i += 1 { + #no_bounds_check for i := 0; i < used; i += 1 { x, y: DIGIT; /* @@ -2245,7 +2205,7 @@ internal_int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (e dest.used = used; dest.sign = .Negative if neg else .Zero_or_Positive; - return clamp(dest); + return internal_clamp(dest); } internal_xor :: proc { internal_int_xor, }; @@ -2257,11 +2217,12 @@ internal_int_complement :: proc(dest, src: ^Int, allocator := context.allocator) 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; + neg := #force_inline internal_is_zero(src) || #force_inline internal_is_positive(src); - err = internal_sub(dest, src, 1); + src.sign = .Negative if neg else .Zero_or_Positive; + + err = #force_inline internal_sub(dest, src, 1); /* Restore sign. */ @@ -2286,14 +2247,14 @@ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, all `numerator` should not be used after this. */ if remainder != nil { - if err = mod_bits(remainder, numerator, bits); err != nil { return err; } + if err = internal_int_mod_bits(remainder, numerator, bits); err != nil { return err; } } /* Shift by as many digits in the bit count. */ if bits >= _DIGIT_BITS { - if err = shr_digit(quotient, bits / _DIGIT_BITS); err != nil { return err; } + if err = internal_shr_digit(quotient, bits / _DIGIT_BITS); err != nil { return err; } } /* @@ -2305,7 +2266,7 @@ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, all shift := DIGIT(_DIGIT_BITS - bits); carry := DIGIT(0); - for x := quotient.used - 1; x >= 0; x -= 1 { + #no_bounds_check for x := quotient.used - 1; x >= 0; x -= 1 { /* Get the lower bits of this word in a temp. */ @@ -2323,12 +2284,12 @@ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, all } } - return clamp(numerator); + return internal_clamp(numerator); } internal_shrmod :: proc { internal_int_shrmod, }; internal_int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { - return internal_shrmod(dest, nil, source, bits, allocator); + return #force_inline internal_shrmod(dest, nil, source, bits, allocator); } internal_shr :: proc { internal_int_shr, }; @@ -2341,9 +2302,7 @@ internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context /* If digits > used simply zero and return. */ - if digits > quotient.used { - return internal_zero(quotient, true, allocator); - } + if digits > quotient.used { return internal_zero(quotient, true, allocator); } /* Much like `int_shl_digit`, this is implemented using a sliding window, @@ -2354,12 +2313,12 @@ internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context \-------------------/ ----> */ - for x := 0; x < (quotient.used - digits); x += 1 { + #no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 { quotient.digit[x] = quotient.digit[x + digits]; } quotient.used -= digits; - zero_unused(quotient); - return clamp(quotient); + internal_zero_unused(quotient); + return internal_clamp(quotient); } internal_shr_digit :: proc { internal_int_shr_digit, }; @@ -2386,19 +2345,19 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca if bits < 0 { return .Invalid_Argument; } - if err = copy(dest, src, false, allocator); err != nil { return err; } + if err = internal_copy(dest, src, false, allocator); err != nil { return err; } /* Grow `dest` to accommodate the additional bits. */ digits_needed := dest.used + (bits / _DIGIT_BITS) + 1; - if err = grow(dest, digits_needed); err != nil { return err; } + if err = internal_grow(dest, digits_needed); err != nil { return err; } dest.used = digits_needed; /* Shift by as many digits in the bit count as we have. */ if bits >= _DIGIT_BITS { - if err = shl_digit(dest, bits / _DIGIT_BITS); err != nil { return err; } + if err = internal_shl_digit(dest, bits / _DIGIT_BITS); err != nil { return err; } } /* @@ -2410,7 +2369,7 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca shift := DIGIT(_DIGIT_BITS - bits); carry := DIGIT(0); - for x:= 0; x < dest.used; x+= 1 { + #no_bounds_check for x:= 0; x < dest.used; x+= 1 { fwd_carry := (dest.digit[x] >> shift) & mask; dest.digit[x] = (dest.digit[x] << uint(bits) | carry) & _MASK; carry = fwd_carry; @@ -2424,7 +2383,7 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca dest.used += 1; } } - return clamp(dest); + return internal_clamp(dest); } internal_shl :: proc { internal_int_shl, }; @@ -2438,13 +2397,12 @@ internal_int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { /* No need to shift a zero. */ - z: bool; - if z, err = is_zero(quotient); z || err != nil { return err; } + if #force_inline internal_is_zero(quotient) { return {}; } /* Resize `quotient` to accomodate extra digits. */ - if err = grow(quotient, quotient.used + digits); err != nil { return err; } + if err = #force_inline internal_grow(quotient, quotient.used + digits); err != nil { return err; } /* Increment the used by the shift amount then copy upwards. @@ -2454,7 +2412,7 @@ internal_int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { Much like `int_shr_digit`, this is implemented using a sliding window, except the window goes the other way around. */ - for x := quotient.used; x > 0; x -= 1 { + #no_bounds_check for x := quotient.used; x > 0; x -= 1 { quotient.digit[x+digits-1] = quotient.digit[x-1]; } @@ -2466,17 +2424,13 @@ internal_shl_digit :: proc { internal_int_shl_digit, }; /* Count bits in an `Int`. + Assumes `a` not to be `nil` and to have been initialized. */ -internal_count_bits :: proc(a: ^Int) -> (count: int, err: Error) { - if err = internal_clear_if_uninitialized_single(a); err != nil { - return 0, err; - } +internal_count_bits :: proc(a: ^Int) -> (count: int) { /* Fast path for zero. */ - if z, _ := is_zero(a); z { - return 0, nil; - } + if #force_inline internal_is_zero(a) { return {}; } /* Get the number of DIGITs and use it. */ @@ -2496,21 +2450,21 @@ internal_count_bits :: proc(a: ^Int) -> (count: int, err: Error) { internal_int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { if err = internal_clear_if_uninitialized_single(a); err != nil { return -1, err; } - _ctz :: intrinsics.count_trailing_zeros; /* Easy out. */ - if z, _ := is_zero(a); z { return 0, nil; } + if #force_inline internal_is_zero(a) { return {}, nil; } /* Scan lower digits until non-zero. */ x: int; - for x = 0; x < a.used && a.digit[x] == 0; x += 1 {} + #no_bounds_check for x = 0; x < a.used && a.digit[x] == 0; x += 1 {} q := a.digit[x]; x *= _DIGIT_BITS; - return x + count_lsb(q), nil; + x += internal_count_lsb(q); + return x, nil; } internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) @@ -2544,7 +2498,7 @@ internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator : digits += 1; } - if err = grow(dest, digits, true, allocator); err != nil { return err; } + if err = #force_inline internal_grow(dest, digits, true, allocator); err != nil { return err; } for i := 0; i < digits; i += 1 { dest.digit[i] = int_random_digit(r) & _MASK; @@ -2598,26 +2552,26 @@ internal_error_if_immutable :: proc {internal_error_if_immutable_single, interna /* Allocates several `Int`s at once. */ -internal_int_init_multi :: proc(integers: ..^Int) -> (err: Error) { +internal_int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) { integers := integers; for a in &integers { - if err = internal_clear(a); err != nil { return err; } + if err = internal_clear(a, false, allocator); err != nil { return err; } } return nil; } internal_init_multi :: proc { internal_int_init_multi, }; +/* + Copies DIGITs from `src` to `dest`. + Assumes `src` and `dest` to not be `nil` and have been initialized. +*/ _private_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { digits := digits; - if err = internal_clear_if_uninitialized_single(src); err != nil { return err; } - if err = internal_clear_if_uninitialized_single(dest); err != nil { return err; } /* If dest == src, do nothing */ - if (dest == src) { - return nil; - } + if dest == src { return nil; } digits = min(digits, len(src.digit), len(dest.digit)); mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); @@ -2631,16 +2585,10 @@ _private_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { Typically very fast. Also fixes the sign if there are no more leading digits. */ internal_clamp :: proc(a: ^Int) -> (err: Error) { - if err = internal_clear_if_uninitialized_single(a); err != nil { - return err; - } - for a.used > 0 && a.digit[a.used - 1] == 0 { - a.used -= 1; - } + for a.used > 0 && a.digit[a.used - 1] == 0 { a.used -= 1; } + + if #force_inline internal_is_zero(a) { a.sign = .Zero_or_Positive; } - if z, _ := is_zero(a); z { - a.sign = .Zero_or_Positive; - } return nil; } diff --git a/core/math/big/private.odin b/core/math/big/private.odin index 84e699364..111439e25 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -531,59 +531,59 @@ _private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int /* Binary split factorial algo due to: http://www.luschny.de/math/factorial/binarysplitfact.html */ -_private_int_factorial_binary_split :: proc(res: ^Int, n: int) -> (err: Error) { +_private_int_factorial_binary_split :: proc(res: ^Int, n: int, allocator := context.allocator) -> (err: Error) { inner, outer, start, stop, temp := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(inner, outer, start, stop, temp); + defer internal_destroy(inner, outer, start, stop, temp); - if err = set(inner, 1); err != nil { return err; } - if err = set(outer, 1); err != nil { return err; } + if err = internal_one(inner, false, allocator); err != nil { return err; } + if err = internal_one(outer, false, allocator); err != nil { return err; } bits_used := int(_DIGIT_TYPE_BITS - intrinsics.count_leading_zeros(n)); for i := bits_used; i >= 0; i -= 1 { start := (n >> (uint(i) + 1)) + 1 | 1; stop := (n >> uint(i)) + 1 | 1; - if err = _private_int_recursive_product(temp, start, stop); err != nil { return err; } - if err = internal_mul(inner, inner, temp); err != nil { return err; } - if err = internal_mul(outer, outer, inner); err != nil { return err; } + if err = _private_int_recursive_product(temp, start, stop, 0, allocator); err != nil { return err; } + if err = internal_mul(inner, inner, temp, allocator); err != nil { return err; } + if err = internal_mul(outer, outer, inner, allocator); err != nil { return err; } } shift := n - intrinsics.count_ones(n); - return shl(res, outer, int(shift)); + return internal_shl(res, outer, int(shift), allocator); } /* Recursive product used by binary split factorial algorithm. */ -_private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int(0)) -> (err: Error) { +_private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int(0), allocator := context.allocator) -> (err: Error) { t1, t2 := &Int{}, &Int{}; - defer destroy(t1, t2); + defer internal_destroy(t1, t2); if level > FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS { return .Max_Iterations_Reached; } num_factors := (stop - start) >> 1; if num_factors == 2 { - if err = set(t1, start); err != nil { return err; } + if err = internal_set(t1, start, false, allocator); err != nil { return err; } when true { - if err = grow(t2, t1.used + 1); err != nil { return err; } - if err = internal_add(t2, t1, 2); err != nil { return err; } + if err = internal_grow(t2, t1.used + 1, false, allocator); err != nil { return err; } + if err = internal_add(t2, t1, 2, allocator); err != nil { return err; } } else { if err = add(t2, t1, 2); err != nil { return err; } } - return internal_mul(res, t1, t2); + return internal_mul(res, t1, t2, allocator); } if num_factors > 1 { mid := (start + num_factors) | 1; - if err = _private_int_recursive_product(t1, start, mid, level + 1); err != nil { return err; } - if err = _private_int_recursive_product(t2, mid, stop, level + 1); err != nil { return err; } - return internal_mul(res, t1, t2); + if err = _private_int_recursive_product(t1, start, mid, level + 1, allocator); err != nil { return err; } + if err = _private_int_recursive_product(t2, mid, stop, level + 1, allocator); err != nil { return err; } + return internal_mul(res, t1, t2, allocator); } - if num_factors == 1 { return #force_inline set(res, start); } + if num_factors == 1 { return #force_inline internal_set(res, start, true, allocator); } - return #force_inline set(res, 1); + return #force_inline internal_one(res, true, allocator); } /* From 1f91a2fe653137337411aaab4959f51a2dd710e1 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 10 Aug 2021 17:17:22 +0200 Subject: [PATCH 098/105] big: Finish refactor. --- core/math/big/helpers.odin | 108 +++++------ core/math/big/internal.odin | 376 +++++++++++++++++------------------- core/math/big/logical.odin | 47 +++-- core/math/big/prime.odin | 4 +- core/math/big/private.odin | 252 +++++++++++++++++------- core/math/big/public.odin | 156 ++++++++++----- core/math/big/radix.odin | 62 +++--- core/math/big/test.odin | 38 ++-- core/math/big/test.py | 10 +- 9 files changed, 605 insertions(+), 448 deletions(-) diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index e6b73430e..c0aa0c8bb 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -35,6 +35,7 @@ int_destroy :: proc(integers: ..^Int) { */ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) where intrinsics.type_is_integer(T) { + context.allocator = allocator; src := src; /* @@ -43,7 +44,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : assert_if_nil(dest); if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } - return #force_inline internal_int_set_from_integer(dest, src, minimize, allocator); + return #force_inline internal_int_set_from_integer(dest, src, minimize); } set :: proc { int_set_from_integer, int_copy }; @@ -61,10 +62,12 @@ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.alloca Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest, src); - if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_copy(dest, src, minimize, allocator); + if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + + return #force_inline internal_int_copy(dest, src, minimize); } copy :: proc { int_copy, }; @@ -87,10 +90,12 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest, src); - if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_abs(dest, src, allocator); + if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + + return #force_inline internal_int_abs(dest, src); } platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) { @@ -106,10 +111,12 @@ int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest, src); - if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_neg(dest, src, allocator); + if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + + return #force_inline internal_int_neg(dest, src); } neg :: proc { int_neg, }; @@ -125,22 +132,24 @@ int_bitfield_extract :: proc(a: ^Int, offset, count: int, allocator := context.a Check that `a` is usable. */ assert_if_nil(a); - if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return {}, err; } + context.allocator = allocator; - return #force_inline internal_int_bitfield_extract(a, offset, count); + if err = #force_inline internal_clear_if_uninitialized(a); err != nil { return {}, err; } + return #force_inline internal_int_bitfield_extract(a, offset, count); } /* Resize backing store. */ -shrink :: proc(a: ^Int) -> (err: Error) { +shrink :: proc(a: ^Int, allocator := context.allocator) -> (err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - if err = #force_inline internal_clear_if_uninitialized(a); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_shrink(a); + if err = #force_inline internal_clear_if_uninitialized(a); err != nil { return err; } + return #force_inline internal_shrink(a); } int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) { @@ -148,7 +157,6 @@ int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := conte Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_int_grow(a, digits, allow_shrink, allocator); } grow :: proc { int_grow, }; @@ -161,7 +169,6 @@ int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_int_clear(a, minimize, allocator); } clear :: proc { int_clear, }; @@ -175,7 +182,6 @@ int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> ( Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_one(a, minimize, allocator); } one :: proc { int_one, }; @@ -188,7 +194,6 @@ int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_minus_one(a, minimize, allocator); } minus_one :: proc { int_minus_one, }; @@ -201,7 +206,6 @@ int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> ( Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_inf(a, minimize, allocator); } inf :: proc { int_inf, }; @@ -214,7 +218,6 @@ int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_minus_inf(a, minimize, allocator); } minus_inf :: proc { int_inf, }; @@ -227,7 +230,6 @@ int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> ( Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_nan(a, minimize, allocator); } nan :: proc { int_nan, }; @@ -237,67 +239,60 @@ power_of_two :: proc(a: ^Int, power: int, allocator := context.allocator) -> (er Check that `a` is usable. */ assert_if_nil(a); - return #force_inline internal_int_power_of_two(a, power, allocator); } -int_get_u128 :: proc(a: ^Int) -> (res: u128, err: Error) { +int_get_u128 :: proc(a: ^Int, allocator := context.allocator) -> (res: u128, err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - - return int_get(a, u128); + return int_get(a, u128, allocator); } get_u128 :: proc { int_get_u128, }; -int_get_i128 :: proc(a: ^Int) -> (res: i128, err: Error) { +int_get_i128 :: proc(a: ^Int, allocator := context.allocator) -> (res: i128, err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - - return int_get(a, i128); + return int_get(a, i128, allocator); } get_i128 :: proc { int_get_i128, }; -int_get_u64 :: proc(a: ^Int) -> (res: u64, err: Error) { +int_get_u64 :: proc(a: ^Int, allocator := context.allocator) -> (res: u64, err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - - return int_get(a, u64); + return int_get(a, u64, allocator); } get_u64 :: proc { int_get_u64, }; -int_get_i64 :: proc(a: ^Int) -> (res: i64, err: Error) { +int_get_i64 :: proc(a: ^Int, allocator := context.allocator) -> (res: i64, err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - - return int_get(a, i64); + return int_get(a, i64, allocator); } get_i64 :: proc { int_get_i64, }; -int_get_u32 :: proc(a: ^Int) -> (res: u32, err: Error) { +int_get_u32 :: proc(a: ^Int, allocator := context.allocator) -> (res: u32, err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - - return int_get(a, u32); + return int_get(a, u32, allocator); } get_u32 :: proc { int_get_u32, }; -int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) { +int_get_i32 :: proc(a: ^Int, allocator := context.allocator) -> (res: i32, err: Error) { /* Check that `a` is usable. */ assert_if_nil(a); - - return int_get(a, i32); + return int_get(a, i32, allocator); } get_i32 :: proc { int_get_i32, }; @@ -311,8 +306,7 @@ int_get :: proc(a: ^Int, $T: typeid, allocator := context.allocator) -> (res: T, */ assert_if_nil(a); if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return T{}, err; } - - return #force_inline internal_int_get(a, T); + return #force_inline internal_int_get(a, T); } get :: proc { int_get, }; @@ -322,8 +316,7 @@ int_get_float :: proc(a: ^Int, allocator := context.allocator) -> (res: f64, err */ assert_if_nil(a); if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return 0, err; } - - return #force_inline internal_int_get_float(a); + return #force_inline internal_int_get_float(a); } /* @@ -335,8 +328,7 @@ count_bits :: proc(a: ^Int, allocator := context.allocator) -> (count: int, err: */ assert_if_nil(a); if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return 0, err; } - - return #force_inline internal_count_bits(a), nil; + return #force_inline internal_count_bits(a), nil; } /* @@ -349,8 +341,7 @@ int_count_lsb :: proc(a: ^Int, allocator := context.allocator) -> (count: int, e */ assert_if_nil(a); if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return 0, err; } - - return #force_inline internal_int_count_lsb(a); + return #force_inline internal_int_count_lsb(a); } platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) @@ -429,35 +420,30 @@ error_if_immutable :: proc {error_if_immutable_single, error_if_immutable_multi, /* Allocates several `Int`s at once. */ -int_init_multi :: proc(integers: ..^Int) -> (err: Error) { +int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(..integers); integers := integers; for a in &integers { - if err = #force_inline internal_clear(a); err != nil { return err; } + if err = #force_inline internal_clear(a, true, allocator); err != nil { return err; } } return nil; } init_multi :: proc { int_init_multi, }; -_copy_digits :: proc(dest, src: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { +copy_digits :: proc(dest, src: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + digits := digits; /* Check that `src` is usable and `dest` isn't immutable. */ assert_if_nil(dest, src); - if err = #force_inline internal_clear_if_uninitialized(src, allocator); err != nil { return err; } - if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } - - /* - If dest == src, do nothing - */ - if (dest == src) { return nil; } + if err = #force_inline internal_clear_if_uninitialized(src); err != nil { return err; } digits = min(digits, len(src.digit), len(dest.digit)); - #force_inline mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); - return nil; + return #force_inline internal_copy_digits(dest, src, digits); } /* diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index e916f9115..b05aeaf2e 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -26,6 +26,9 @@ package big Check the comments above each `internal_*` implementation to see what constraints it expects to have met. + We pass the custom allocator to procedures by default using the pattern `context.allocator = allocator`. + This way we don't have to add `, allocator` at the end of each call. + TODO: Handle +/- Infinity and NaN. */ @@ -41,6 +44,7 @@ import rnd "core:math/rand" */ internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { dest := dest; x := a; y := b; + context.allocator = allocator; old_used, min_used, max_used, i: int; @@ -52,7 +56,7 @@ internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocat max_used = x.used; old_used = dest.used; - if err = internal_grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } + if err = internal_grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } dest.used = max_used + 1; /* All parameters have been initialized. @@ -119,12 +123,13 @@ internal_add_unsigned :: proc { internal_int_add_unsigned, }; */ internal_int_add_signed :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { x := a; y := b; + context.allocator = allocator; /* Handle both negative or both positive. */ if x.sign == y.sign { dest.sign = x.sign; - return #force_inline internal_int_add_unsigned(dest, x, y, allocator); + return #force_inline internal_int_add_unsigned(dest, x, y); } /* @@ -137,7 +142,7 @@ internal_int_add_signed :: proc(dest, a, b: ^Int, allocator := context.allocator } dest.sign = x.sign; - return #force_inline internal_int_sub_unsigned(dest, x, y, allocator); + return #force_inline internal_int_sub_unsigned(dest, x, y); } internal_add_signed :: proc { internal_int_add_signed, }; @@ -149,7 +154,9 @@ internal_add_signed :: proc { internal_int_add_signed, }; `dest` is large enough (a.used + 1) to fit result. */ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { - if err = internal_grow(dest, a.used + 1, false, allocator); err != nil { return err; } + context.allocator = allocator; + + if err = internal_grow(dest, a.used + 1); err != nil { return err; } /* Fast paths for destination and input Int being the same. */ @@ -183,7 +190,7 @@ internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context /* dest = |a| - digit */ - if err = #force_inline internal_int_add_digit(dest, a, digit, allocator); err != nil { + if err = #force_inline internal_int_add_digit(dest, a, digit); err != nil { /* Restore a's sign. */ @@ -261,13 +268,15 @@ internal_add :: proc { internal_int_add_signed, internal_int_add_digit, }; `dest`, `number` and `decrease` != `nil` and have been initalized. */ internal_int_sub_unsigned :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + dest := dest; x := number; y := decrease; old_used := dest.used; min_used := y.used; max_used := x.used; i: int; - if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } + if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } dest.used = max_used; /* All parameters have been initialized. @@ -328,6 +337,8 @@ internal_sub_unsigned :: proc { internal_int_sub_unsigned, }; `dest`, `number` and `decrease` != `nil` and have been initalized. */ internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + number := number; decrease := decrease; if number.sign != decrease.sign { /* @@ -335,14 +346,14 @@ internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := conte In either case, ADD their magnitudes and use the sign of the first number. */ dest.sign = number.sign; - return #force_inline internal_int_add_unsigned(dest, number, decrease, allocator); + return #force_inline internal_int_add_unsigned(dest, number, decrease); } /* Subtract a positive from a positive, OR negative from a negative. First, take the difference between their magnitudes, then... */ - if c, _ := #force_inline cmp_mag(number, decrease); c == -1 { + if #force_inline internal_cmp_mag(number, decrease) == -1 { /* The second has a larger magnitude. The result has the *opposite* sign from the first number. @@ -356,7 +367,7 @@ internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := conte */ dest.sign = number.sign; } - return #force_inline internal_int_sub_unsigned(dest, number, decrease, allocator); + return #force_inline internal_int_sub_unsigned(dest, number, decrease); } /* @@ -368,7 +379,9 @@ internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := conte `dest` is large enough (number.used + 1) to fit result. */ internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { - if err = internal_grow(dest, number.used + 1, false, allocator); err != nil { return err; } + context.allocator = allocator; + + if err = internal_grow(dest, number.used + 1); err != nil { return err; } dest := dest; digit := digit; /* @@ -403,7 +416,7 @@ internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT, allocator := co err = #force_inline internal_int_add_digit(dest, t, digit); dest.sign = .Negative; - clamp(dest); + internal_clamp(dest); return err; } @@ -448,6 +461,9 @@ internal_sub :: proc { internal_int_sub_signed, internal_int_sub_digit, }; /* dest = src / 2 dest = src >> 1 + + Assumes `dest` and `src` not to be `nil` and have been initialized. + We make no allocations here. */ internal_int_shr1 :: proc(dest, src: ^Int) -> (err: Error) { old_used := dest.used; dest.used = src.used; @@ -488,13 +504,15 @@ internal_int_shr1 :: proc(dest, src: ^Int) -> (err: Error) { dest = src * 2 dest = src << 1 */ -internal_int_shl1 :: proc(dest, src: ^Int) -> (err: Error) { - if err = copy(dest, src); err != nil { return err; } +internal_int_shl1 :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + + if err = internal_copy(dest, src); err != nil { return err; } /* Grow `dest` to accommodate the additional bits. */ digits_needed := dest.used + 1; - if err = grow(dest, digits_needed); err != nil { return err; } + if err = internal_grow(dest, digits_needed); err != nil { return err; } dest.used = digits_needed; mask := (DIGIT(1) << uint(1)) - DIGIT(1); @@ -520,13 +538,14 @@ internal_int_shl1 :: proc(dest, src: ^Int) -> (err: Error) { Multiply by a DIGIT. */ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) { - assert(dest != nil && src != nil); + context.allocator = allocator; + assert_if_nil(dest, src); if multiplier == 0 { - return zero(dest); + return internal_zero(dest); } if multiplier == 1 { - return copy(dest, src); + return internal_copy(dest, src); } /* @@ -535,16 +554,16 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := if multiplier == 2 { return #force_inline internal_int_shl1(dest, src); } - if is_power_of_two(int(multiplier)) { + if #force_inline platform_int_is_power_of_two(int(multiplier)) { ix: int; - if ix, err = log(multiplier, 2); err != nil { return err; } - return shl(dest, src, ix); + if ix, err = internal_log(multiplier, 2); err != nil { return err; } + return internal_shl(dest, src, ix); } /* Ensure `dest` is big enough to hold `src` * `multiplier`. */ - if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; } + if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } /* Save the original used count. @@ -562,7 +581,7 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := Compute columns. */ ix := 0; - for ; ix < src.used; ix += 1 { + #no_bounds_check for ; ix < src.used; ix += 1 { /* Compute product and carry sum for this term */ @@ -595,10 +614,11 @@ internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := High level multiplication (handles sign). */ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; /* Early out for `multiplier` is zero; Set `dest` to zero. */ - if multiplier.used == 0 || src.used == 0 { return zero(dest); } + if multiplier.used == 0 || src.used == 0 { return internal_zero(dest); } if src == multiplier { /* @@ -676,6 +696,7 @@ internal_sqr :: proc (dest, src: ^Int, allocator := context.allocator) -> (res: `numerator` and `denominator` are expected not to be `nil` and have been initialized. */ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; if denominator.used == 0 { return .Division_by_Zero; } /* @@ -683,7 +704,7 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a */ if #force_inline internal_cmp_mag(numerator, denominator) == -1 { if remainder != nil { - if err = internal_copy(remainder, numerator, false, allocator); err != nil { return err; } + if err = internal_copy(remainder, numerator); err != nil { return err; } } if quotient != nil { internal_zero(quotient); @@ -711,7 +732,9 @@ internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, a Single digit division (based on routine from MPI). The quotient is optional and may be passed a nil. */ -internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { +internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (remainder: DIGIT, err: Error) { + context.allocator = allocator; + /* Cannot divide by zero. */ @@ -722,7 +745,7 @@ internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) */ if denominator == 1 || numerator.used == 0 { if quotient != nil { - return 0, copy(quotient, numerator); + return 0, internal_copy(quotient, numerator); } return 0, err; } @@ -737,11 +760,11 @@ internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) if quotient == nil { return remainder, nil; } - return remainder, shr(quotient, numerator, 1); + return remainder, internal_shr(quotient, numerator, 1); } ix: int; - if is_power_of_two(int(denominator)) { + if platform_int_is_power_of_two(int(denominator)) { ix = 1; for ix < _DIGIT_BITS && denominator != (1 << uint(ix)) { ix += 1; @@ -751,7 +774,7 @@ internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) return remainder, nil; } - return remainder, shr(quotient, numerator, int(ix)); + return remainder, internal_shr(quotient, numerator, int(ix)); } /* @@ -766,7 +789,7 @@ internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) */ q := &Int{}; - if err = grow(q, numerator.used); err != nil { return 0, err; } + if err = internal_grow(q, numerator.used); err != nil { return 0, err; } q.used = numerator.used; q.sign = numerator.sign; @@ -785,10 +808,10 @@ internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) remainder = DIGIT(w); if quotient != nil { - clamp(q); - swap(q, quotient); + internal_clamp(q); + internal_swap(q, quotient); } - destroy(q); + internal_destroy(q); return remainder, nil; } @@ -861,18 +884,20 @@ internal_sqrmod :: proc { internal_int_sqrmod, }; This way we'll have to reallocate less, possibly not at all. */ internal_int_factorial :: proc(res: ^Int, n: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + if n >= FACTORIAL_BINARY_SPLIT_CUTOFF { - return #force_inline _private_int_factorial_binary_split(res, n, allocator); + return #force_inline _private_int_factorial_binary_split(res, n); } i := len(_factorial_table); if n < i { - return #force_inline internal_set(res, _factorial_table[n], true, allocator); + return #force_inline internal_set(res, _factorial_table[n]); } - if err = #force_inline internal_set(res, _factorial_table[i - 1], true, allocator); err != nil { return err; } + if err = #force_inline internal_set(res, _factorial_table[i - 1]); err != nil { return err; } for { - if err = #force_inline internal_mul(res, res, DIGIT(i), allocator); err != nil || i == n { return err; } + if err = #force_inline internal_mul(res, res, DIGIT(i)); err != nil || i == n { return err; } i += 1; } @@ -885,10 +910,10 @@ internal_int_factorial :: proc(res: ^Int, n: int, allocator := context.allocator Assumes `a` and `b` to have been initialized. `res_gcd` and `res_lcm` can be nil or ^Int depending on which results are desired. */ -internal_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { +internal_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.allocator) -> (err: Error) { if res_gcd == nil && res_lcm == nil { return nil; } - return #force_inline _private_int_gcd_lcm(res_gcd, res_lcm, a, b); + return #force_inline _private_int_gcd_lcm(res_gcd, res_lcm, a, b, allocator); } /* @@ -896,7 +921,7 @@ internal_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { Assumes `remainder` and `numerator` both not to be `nil` and `bits` to be >= 0. */ -internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { +internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { /* Everything is divisible by 1 << 0 == 1, so this returns 0. */ @@ -1139,14 +1164,14 @@ internal_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { /* Fast path for bases that are a power of two. */ - if platform_int_is_power_of_two(int(base)) { return private_log_power_of_two(a, base); } + if platform_int_is_power_of_two(int(base)) { return _private_log_power_of_two(a, base); } /* Fast path for `Int`s that fit within a single `DIGIT`. */ if a.used == 1 { return internal_log(a.digit[0], DIGIT(base)); } - return private_int_log(a, base); + return _private_int_log(a, base); } @@ -1207,7 +1232,9 @@ internal_log :: proc { internal_int_log, internal_digit_log, }; Calculate dest = base^power using a square-multiply algorithm. Assumes `dest` and `base` not to be `nil` and to have been initialized. */ -internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { +internal_int_pow :: proc(dest, base: ^Int, power: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + power := power; /* Early outs. @@ -1217,18 +1244,18 @@ internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { A zero base is a special case. */ if power < 0 { - if err = zero(dest); err != nil { return err; } + if err = internal_zero(dest); err != nil { return err; } return .Math_Domain_Error; } - if power == 0 { return set(dest, 1); } - if power > 0 { return zero(dest); } + if power == 0 { return internal_one(dest); } + if power > 0 { return internal_zero(dest); } } if power < 0 { /* Fraction, so we'll return zero. */ - return zero(dest); + return internal_zero(dest); } switch(power) { case 0: @@ -1246,19 +1273,19 @@ internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { } g := &Int{}; - if err = copy(g, base); err != nil { return err; } + if err = internal_copy(g, base); err != nil { return err; } /* Set initial result. */ - if err = set(dest, 1); err != nil { return err; } + if err = internal_one(dest); err != nil { return err; } loop: for power > 0 { /* If the bit is set, multiply. */ if power & 1 != 0 { - if err = mul(dest, g, dest); err != nil { + if err = internal_mul(dest, g, dest); err != nil { break loop; } } @@ -1275,7 +1302,7 @@ internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { power >>= 1; } - destroy(g); + internal_destroy(g); return err; } @@ -1283,11 +1310,13 @@ internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { Calculate `dest = base^power`. Assumes `dest` not to be `nil` and to have been initialized. */ -internal_int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { - base_t := &Int{}; - defer destroy(base_t); +internal_int_pow_int :: proc(dest: ^Int, base, power: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; - if err = set(base_t, base); err != nil { return err; } + base_t := &Int{}; + defer internal_destroy(base_t); + + if err = internal_set(base_t, base); err != nil { return err; } return #force_inline internal_int_pow(dest, base_t, power); } @@ -1295,22 +1324,6 @@ internal_int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { internal_pow :: proc { internal_int_pow, internal_int_pow_int, }; internal_exp :: pow; -/* - Returns the log2 of an `Int`. - Assumes `a` not to be `nil` and to have been initialized. - Also assumes `base` is a power of two. -*/ -private_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { - base := base; - y: int; - for y = 0; base & 1 == 0; { - y += 1; - base >>= 1; - } - log, err = count_bits(a); - return (log - 1) / y, err; -} - /* */ @@ -1333,6 +1346,8 @@ internal_small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { Assumes `dest` and `src` not to be `nil` and to have been initialized. */ internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* Must be positive. */ @@ -1341,7 +1356,7 @@ internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (e /* Easy out. If src is zero, so is dest. */ - if #force_inline internal_is_zero(src) { return zero(dest); } + if #force_inline internal_is_zero(src) { return internal_zero(dest); } /* Set up temporaries. @@ -1358,9 +1373,9 @@ internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (e /* y = (x + n // x) // 2 */ - internal_div(t1, src, x); - internal_add(t2, t1, x); - internal_shr(y, t2, 1); + if err = internal_div(t1, src, x); err != nil { return err; } + if err = internal_add(t2, t1, x); err != nil { return err; } + if err = internal_shr(y, t2, 1); err != nil { return err; } if c := internal_cmp(y, x); c == 0 || c == 1 { internal_swap(dest, x); @@ -1385,10 +1400,12 @@ internal_sqrt :: proc { internal_int_sqrt, }; Assumes `dest` and `src` not to be `nil` and have been initialized. */ internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* Fast path for n == 2 */ - if n == 2 { return #force_inline internal_sqrt(dest, src, allocator); } + if n == 2 { return #force_inline internal_sqrt(dest, src); } if n < 0 || n > int(_DIGIT_MAX) { return .Invalid_Argument; } @@ -1427,14 +1444,14 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.alloca "src" is smaller than max(int), we can cast safely. */ if ilog2 < n { - err = internal_one(dest, true, allocator); + err = internal_one(dest); dest.sign = a.sign; return err; } ilog2 /= n; if ilog2 == 0 { - err = internal_one(dest, true, allocator); + err = internal_one(dest); dest.sign = a.sign; return err; } @@ -1443,13 +1460,13 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.alloca Start value must be larger than root. */ ilog2 += 2; - if err = power_of_two(t2, ilog2, allocator); err != nil { return err; } + if err = internal_int_power_of_two(t2, ilog2); err != nil { return err; } c: int; iterations := 0; for { /* t1 = t2 */ - if err = copy(t1, t2, false, allocator); err != nil { return err; } + if err = internal_copy(t1, t2); err != nil { return err; } /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ @@ -1458,24 +1475,24 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.alloca /* numerator */ /* t2 = t1**b */ - if err = internal_mul(t2, t1, t3, allocator); err != nil { return err; } + if err = internal_mul(t2, t1, t3); err != nil { return err; } /* t2 = t1**b - a */ - if err = internal_sub(t2, t2, a, allocator); err != nil { return err; } + if err = internal_sub(t2, t2, a); err != nil { return err; } /* denominator */ /* t3 = t1**(b-1) * b */ - if err = internal_mul(t3, t3, DIGIT(n), allocator); err != nil { return err; } + if err = internal_mul(t3, t3, DIGIT(n)); err != nil { return err; } /* t3 = (t1**b - a)/(b * t1**(b-1)) */ - if err = internal_div(t3, t2, t3, allocator); err != nil { return err; } - if err = internal_sub(t2, t1, t3, allocator); err != nil { return err; } + if err = internal_div(t3, t2, t3); err != nil { return err; } + if err = internal_sub(t2, t1, t3); err != nil { return err; } /* Number of rounds is at most log_2(root). If it is more it got stuck, so break out of the loop and do the rest manually. */ - if ilog2 -= 1; ilog2 == 0 { break; } + if ilog2 -= 1; ilog2 == 0 { break; } if internal_cmp(t1, t2) == 0 { break; } iterations += 1; @@ -1538,72 +1555,6 @@ internal_int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.alloca } internal_root_n :: proc { internal_int_root_n, }; -/* - Internal implementation of log. - Assumes `a` not to be `nil` and to have been initialized. -*/ -private_int_log :: proc(a: ^Int, base: DIGIT, allocator := context.allocator) -> (res: int, err: Error) { - bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); - - ic := #force_inline internal_cmp(a, base); - if ic == -1 || ic == 0 { - return 1 if ic == 0 else 0, nil; - } - - if err = internal_set(bi_base, base, true, allocator); err != nil { return -1, err; } - if err = internal_clear(bracket_mid, false, allocator); err != nil { return -1, err; } - if err = internal_clear(t, false, allocator); err != nil { return -1, err; } - if err = internal_one(bracket_low, false, allocator); err != nil { return -1, err; } - if err = internal_set(bracket_high, base, false, allocator); err != nil { return -1, err; } - - low := 0; high := 1; - - /* - A kind of Giant-step/baby-step algorithm. - Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/ - The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped - for small n. - */ - - for { - /* - Iterate until `a` is bracketed between low + high. - */ - if #force_inline internal_cmp(bracket_high, a) != -1 { break; } - - low = high; - if err = #force_inline internal_copy(bracket_low, bracket_high); err != nil { return -1, err; } - high <<= 1; - if err = #force_inline internal_sqr(bracket_high, bracket_high); err != nil { return -1, err; } - } - - for (high - low) > 1 { - mid := (high + low) >> 1; - - if err = #force_inline internal_pow(t, bi_base, mid - low); err != nil { return -1, err; } - - if err = #force_inline internal_mul(bracket_mid, bracket_low, t); err != nil { return -1, err; } - - mc := #force_inline internal_cmp(a, bracket_mid); - switch mc { - case -1: - high = mid; - internal_swap(bracket_mid, bracket_high); - case 0: - return mid, nil; - case 1: - low = mid; - internal_swap(bracket_mid, bracket_low); - } - } - - fc := #force_inline internal_cmp(bracket_high, a); - res = high if fc == 0 else low; - - return; -} - /* Other internal helpers */ @@ -1616,9 +1567,9 @@ internal_int_destroy :: proc(integers: ..^Int) { integers := integers; for a in &integers { - mem.zero_slice(a.digit[:]); raw := transmute(mem.Raw_Dynamic_Array)a.digit; if raw.cap > 0 { + mem.zero_slice(a.digit[:]); free(&a.digit[0]); } a = &Int{}; @@ -1631,6 +1582,8 @@ internal_destroy :: proc{ internal_int_destroy, }; */ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error) where intrinsics.type_is_integer(T) { + context.allocator = allocator; + src := src; if err = internal_error_if_immutable(dest); err != nil { return err; } @@ -1638,7 +1591,7 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al Most internal procs asssume an Int to have already been initialize, but as this is one of the procs that initializes, we have to check the following. */ - if err = internal_clear_if_uninitialized_single(dest, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized_single(dest); err != nil { return err; } dest.flags = {}; // We're not -Inf, Inf, NaN or Immutable. @@ -1657,10 +1610,24 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al internal_set :: proc { internal_int_set_from_integer, internal_int_copy }; +internal_copy_digits :: #force_inline proc(dest, src: ^Int, digits: int) -> (err: Error) { + if err = #force_inline internal_error_if_immutable(dest); err != nil { return err; } + + /* + If dest == src, do nothing + */ + if (dest == src) { return nil; } + + #force_inline mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); + return nil; +} + /* Copy one `Int` to another. */ internal_int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* If dest == src, do nothing */ @@ -1674,14 +1641,13 @@ internal_int_copy :: proc(dest, src: ^Int, minimize := false, allocator := conte */ needed := src.used if minimize else max(src.used, _DEFAULT_DIGIT_COUNT); - if err = internal_grow(dest, needed, minimize, allocator); err != nil { return err; } + if err = internal_grow(dest, needed, minimize); err != nil { return err; } /* Copy everything over and zero high digits. */ - #no_bounds_check for v, i in src.digit[:src.used] { - dest.digit[i] = v; - } + internal_copy_digits(dest, src, src.used); + dest.used = src.used; dest.sign = src.sign; dest.flags = src.flags &~ {.Immutable}; @@ -1709,6 +1675,8 @@ internal_swap :: proc { internal_int_swap, }; Set `dest` to |`src`|. */ internal_int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* If `dest == src`, just fix `dest`'s sign. */ @@ -1720,7 +1688,7 @@ internal_int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (er /* Copy `src` to `dest` */ - if err = internal_copy(dest, src, false, allocator); err != nil { + if err = internal_copy(dest, src); err != nil { return err; } @@ -1740,6 +1708,8 @@ internal_abs :: proc{ internal_int_abs, internal_platform_abs, }; Set `dest` to `-src`. */ internal_int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* If `dest == src`, just fix `dest`'s sign. */ @@ -1754,7 +1724,7 @@ internal_int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (er /* Copy `src` to `dest` */ - if err = internal_copy(dest, src, false, allocator); err != nil { return err; } + if err = internal_copy(dest, src); err != nil { return err; } /* Fix sign. @@ -1836,7 +1806,7 @@ internal_int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WOR internal_int_shrink :: proc(a: ^Int) -> (err: Error) { needed := max(_MIN_DIGIT_COUNT, a.used); - if a.used != needed { return internal_grow(a, needed); } + if a.used != needed { return internal_grow(a, needed, true); } return nil; } internal_shrink :: proc { internal_int_shrink, }; @@ -1932,13 +1902,15 @@ internal_int_nan :: proc(a: ^Int, minimize := false, allocator := context.alloca internal_nan :: proc { internal_int_nan, }; internal_int_power_of_two :: proc(a: ^Int, power: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + if power < 0 || power > _MAX_BIT_COUNT { return .Invalid_Argument; } /* Grow to accomodate the single bit. */ a.used = (power / _DIGIT_BITS) + 1; - if err = internal_grow(a, a.used, false, allocator); err != nil { return err; } + if err = internal_grow(a, a.used); err != nil { return err; } /* Zero the entirety. */ @@ -2036,11 +2008,13 @@ internal_int_get_float :: proc(a: ^Int) -> (res: f64, err: Error) { 2's complement `and`, returns `dest = a & b;` */ internal_int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = internal_grow(dest, used, false, allocator); err != nil { return err; } + if err = internal_grow(dest, used); err != nil { return err; } neg_a := #force_inline internal_is_negative(a); neg_b := #force_inline internal_is_negative(b); @@ -2095,11 +2069,13 @@ internal_and :: proc { internal_int_and, }; 2's complement `or`, returns `dest = a | b;` */ internal_int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = internal_grow(dest, used, false, allocator); err != nil { return err; } + if err = internal_grow(dest, used); err != nil { return err; } neg_a := #force_inline internal_is_negative(a); neg_b := #force_inline internal_is_negative(b); @@ -2154,11 +2130,13 @@ internal_or :: proc { internal_int_or, }; 2's complement `xor`, returns `dest = a ~ b;` */ internal_int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + used := max(a.used, b.used) + 1; /* Grow the destination to accomodate the result. */ - if err = internal_grow(dest, used, false, allocator); err != nil { return err; } + if err = internal_grow(dest, used); err != nil { return err; } neg_a := #force_inline internal_is_negative(a); neg_b := #force_inline internal_is_negative(b); @@ -2213,6 +2191,8 @@ internal_xor :: proc { internal_int_xor, }; dest = ~src */ internal_int_complement :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* Temporarily fix sign. */ @@ -2237,10 +2217,12 @@ internal_complement :: proc { internal_int_complement, }; `remainder` is allowed to be passed a `nil`, in which case `mod` won't be computed. */ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + bits := bits; if bits < 0 { return .Invalid_Argument; } - if err = internal_copy(quotient, numerator, true, allocator); err != nil { return err; } + if err = internal_copy(quotient, numerator); err != nil { return err; } /* Shift right by a certain bit count (store quotient and optional remainder.) @@ -2297,12 +2279,14 @@ internal_shr :: proc { internal_int_shr, }; Shift right by `digits` * _DIGIT_BITS bits. */ internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + if digits <= 0 { return nil; } /* If digits > used simply zero and return. */ - if digits > quotient.used { return internal_zero(quotient, true, allocator); } + if digits > quotient.used { return internal_zero(quotient); } /* Much like `int_shl_digit`, this is implemented using a sliding window, @@ -2326,13 +2310,15 @@ internal_shr_digit :: proc { internal_int_shr_digit, }; Shift right by a certain bit count with sign extension. */ internal_int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { - if src.sign == .Zero_or_Positive { - return internal_shr(dest, src, bits, allocator); - } - if err = internal_int_add_digit(dest, src, DIGIT(1), allocator); err != nil { return err; } + context.allocator = allocator; - if err = internal_shr(dest, dest, bits, allocator); err != nil { return err; } - return internal_sub(dest, src, DIGIT(1), allocator); + if src.sign == .Zero_or_Positive { + return internal_shr(dest, src, bits); + } + if err = internal_int_add_digit(dest, src, DIGIT(1)); err != nil { return err; } + + if err = internal_shr(dest, dest, bits); err != nil { return err; } + return internal_sub(dest, src, DIGIT(1)); } internal_shr_signed :: proc { internal_int_shr_signed, }; @@ -2341,11 +2327,13 @@ internal_shr_signed :: proc { internal_int_shr_signed, }; Shift left by a certain bit count. */ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + bits := bits; if bits < 0 { return .Invalid_Argument; } - if err = internal_copy(dest, src, false, allocator); err != nil { return err; } + if err = internal_copy(dest, src); err != nil { return err; } /* Grow `dest` to accommodate the additional bits. @@ -2391,7 +2379,9 @@ internal_shl :: proc { internal_int_shl, }; /* Shift left by `digits` * _DIGIT_BITS bits. */ -internal_int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { +internal_int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + if digits <= 0 { return nil; } /* @@ -2446,10 +2436,10 @@ internal_count_bits :: proc(a: ^Int) -> (count: int) { /* Returns the number of trailing zeroes before the first one. Differs from regular `ctz` in that 0 returns 0. + + Assumes `a` not to be `nil` and have been initialized. */ internal_int_count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { - if err = internal_clear_if_uninitialized_single(a); err != nil { return -1, err; } - /* Easy out. */ @@ -2487,6 +2477,8 @@ internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { } internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + bits := bits; if bits <= 0 { return .Invalid_Argument; } @@ -2498,7 +2490,7 @@ internal_int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator : digits += 1; } - if err = #force_inline internal_grow(dest, digits, true, allocator); err != nil { return err; } + if err = #force_inline internal_grow(dest, digits); err != nil { return err; } for i := 0; i < digits; i += 1 { dest.digit[i] = int_random_digit(r) & _MASK; @@ -2519,16 +2511,20 @@ internal_assert_initialized :: proc(a: ^Int, loc := #caller_location) { } internal_clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + if ! #force_inline internal_is_initialized(arg) { - return #force_inline internal_grow(arg, _DEFAULT_DIGIT_COUNT, true, allocator); + return #force_inline internal_grow(arg, _DEFAULT_DIGIT_COUNT); } return err; } internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + for i in args { if ! #force_inline internal_is_initialized(i) { - e := #force_inline internal_grow(i, _DEFAULT_DIGIT_COUNT, true, allocator); + e := #force_inline internal_grow(i, _DEFAULT_DIGIT_COUNT); if e != nil { err = e; } } } @@ -2553,31 +2549,17 @@ internal_error_if_immutable :: proc {internal_error_if_immutable_single, interna Allocates several `Int`s at once. */ internal_int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + integers := integers; for a in &integers { - if err = internal_clear(a, false, allocator); err != nil { return err; } + if err = internal_clear(a); err != nil { return err; } } return nil; } internal_init_multi :: proc { internal_int_init_multi, }; -/* - Copies DIGITs from `src` to `dest`. - Assumes `src` and `dest` to not be `nil` and have been initialized. -*/ -_private_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { - digits := digits; - /* - If dest == src, do nothing - */ - if dest == src { return nil; } - - digits = min(digits, len(src.digit), len(dest.digit)); - mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); - return nil; -} - /* Trim unused digits. diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 57adf6f1e..1050de0c4 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -25,9 +25,10 @@ import "core:mem" */ int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a, b); - if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_and(dest, a, b, allocator); + if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } + return #force_inline internal_int_and(dest, a, b); } and :: proc { int_and, }; @@ -36,9 +37,10 @@ and :: proc { int_and, }; */ int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a, b); - if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_or(dest, a, b, allocator); + if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } + return #force_inline internal_int_or(dest, a, b); } or :: proc { int_or, }; @@ -47,9 +49,10 @@ or :: proc { int_or, }; */ int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a, b); - if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_xor(dest, a, b, allocator); + if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } + return #force_inline internal_int_xor(dest, a, b); } xor :: proc { int_xor, }; @@ -61,9 +64,9 @@ int_complement :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Check that `src` and `dest` are usable. */ assert_if_nil(dest, src); - if err = internal_clear_if_uninitialized(dest, allocator); err != nil { return err; } - if err = internal_clear_if_uninitialized(src, allocator); err != nil { return err; } + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } return #force_inline internal_int_complement(dest, src); } complement :: proc { int_complement, }; @@ -74,15 +77,15 @@ complement :: proc { int_complement, }; */ int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { assert_if_nil(quotient, numerator); - if err = internal_clear_if_uninitialized(quotient, allocator); err != nil { return err; } - if err = internal_clear_if_uninitialized(numerator, allocator); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_shrmod(quotient, remainder, numerator, bits, allocator); + if err = internal_clear_if_uninitialized(quotient, numerator); err != nil { return err; } + return #force_inline internal_int_shrmod(quotient, remainder, numerator, bits); } shrmod :: proc { int_shrmod, }; -int_shr :: proc(dest, source: ^Int, bits: int) -> (err: Error) { - return #force_inline shrmod(dest, nil, source, bits); +int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { + return #force_inline shrmod(dest, nil, source, bits, allocator); } shr :: proc { int_shr, }; @@ -94,9 +97,10 @@ int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocato Check that `quotient` is usable. */ assert_if_nil(quotient); - if err = internal_clear_if_uninitialized(quotient, allocator); err != nil { return err; } + context.allocator = allocator; - return #force_inline internal_int_shr_digit(quotient, digits, allocator); + if err = internal_clear_if_uninitialized(quotient); err != nil { return err; } + return #force_inline internal_int_shr_digit(quotient, digits); } shr_digit :: proc { int_shr_digit, }; @@ -105,9 +109,9 @@ shr_digit :: proc { int_shr_digit, }; */ int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src); - if err = internal_clear_if_uninitialized(dest, allocator); err != nil { return err; } - if err = internal_clear_if_uninitialized(src, allocator); err != nil { return err; } + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } return #force_inline internal_int_shr_signed(dest, src, bits); } @@ -118,9 +122,9 @@ shr_signed :: proc { int_shr_signed, }; */ int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src); - if err = internal_clear_if_uninitialized(dest, allocator); err != nil { return err; } - if err = internal_clear_if_uninitialized(src, allocator); err != nil { return err; } + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } return #force_inline internal_int_shl(dest, src, bits); } shl :: proc { int_shl, }; @@ -129,13 +133,14 @@ shl :: proc { int_shl, }; /* Shift left by `digits` * _DIGIT_BITS bits. */ -int_shl_digit :: proc(quotient: ^Int, digits: int) -> (err: Error) { +int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { /* Check that `quotient` is usable. */ assert_if_nil(quotient); - if err = internal_clear_if_uninitialized(quotient); err != nil { return err; } + context.allocator = allocator; + if err = internal_clear_if_uninitialized(quotient); err != nil { return err; } return #force_inline internal_int_shl_digit(quotient, digits); } shl_digit :: proc { int_shl_digit, }; \ No newline at end of file diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index 7041b8c39..25d5ddda3 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -17,7 +17,9 @@ package big */ int_prime_is_divisible :: proc(a: ^Int, allocator := context.allocator) -> (res: bool, err: Error) { assert_if_nil(a); - if err = internal_clear_if_uninitialized(a, allocator); err != nil { return {}, err; } + context.allocator = allocator; + + if err = internal_clear_if_uninitialized(a); err != nil { return {}, err; } rem: DIGIT; for prime in _private_prime_table { diff --git a/core/math/big/private.odin b/core/math/big/private.odin index 111439e25..fab63977c 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -19,13 +19,16 @@ package big */ import "core:intrinsics" +import "core:mem" /* Multiplies |a| * |b| and only computes upto digs digits of result. HAC pp. 595, Algorithm 14.12 Modified so you can control how many digits of output are created. */ -_private_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { +_private_int_mul :: proc(dest, a, b: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* Can we use the fast multiplier? */ @@ -39,7 +42,7 @@ _private_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { t := &Int{}; - if err = grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } + if err = internal_grow(t, max(digits, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } t.used = digits; /* @@ -81,9 +84,9 @@ _private_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { } } - swap(dest, t); - destroy(t); - return clamp(dest); + internal_swap(dest, t); + internal_destroy(t); + return internal_clamp(dest); } /* @@ -102,7 +105,9 @@ _private_int_mul :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { Based on Algorithm 14.12 on pp.595 of HAC. */ -_private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { +_private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* Set up array. */ @@ -111,7 +116,7 @@ _private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Grow the destination as required. */ - if err = grow(dest, digits); err != nil { return err; } + if err = internal_grow(dest, digits); err != nil { return err; } /* Number of output digits to produce. @@ -172,27 +177,28 @@ _private_int_mul_comba :: proc(dest, a, b: ^Int, digits: int) -> (err: Error) { /* Clear unused digits [that existed in the old copy of dest]. */ - zero_unused(dest, old_used); + internal_zero_unused(dest, old_used); /* Adjust dest.used based on leading zeroes. */ - return clamp(dest); + return internal_clamp(dest); } /* Low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 Assumes `dest` and `src` to not be `nil`, and `src` to have been initialized. */ -_private_int_sqr :: proc(dest, src: ^Int) -> (err: Error) { +_private_int_sqr :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; pa := src.used; t := &Int{}; ix, iy: int; /* Grow `t` to maximum needed size, or `_DEFAULT_DIGIT_COUNT`, whichever is bigger. */ - if err = grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } + if err = internal_grow(t, max((2 * pa) + 1, _DEFAULT_DIGIT_COUNT)); err != nil { return err; } t.used = (2 * pa) + 1; #no_bounds_check for ix = 0; ix < pa; ix += 1 { @@ -243,23 +249,25 @@ _private_int_sqr :: proc(dest, src: ^Int) -> (err: Error) { } } - err = clamp(t); - swap(dest, t); - destroy(t); + err = internal_clamp(t); + internal_swap(dest, t); + internal_destroy(t); return err; } /* Divide by three (based on routine from MPI and the GMP manual). */ -_private_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: Error) { +_private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.allocator) -> (remainder: DIGIT, err: Error) { + context.allocator = allocator; + /* b = 2^_DIGIT_BITS / 3 */ b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3); q := &Int{}; - if err = grow(q, numerator.used); err != nil { return 0, err; } + if err = internal_grow(q, numerator.used); err != nil { return 0, err; } q.used = numerator.used; q.sign = numerator.sign; @@ -296,9 +304,9 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: */ if quotient != nil { err = clamp(q); - swap(q, quotient); + internal_swap(q, quotient); } - destroy(q); + internal_destroy(q); return remainder, nil; } @@ -314,19 +322,20 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int) -> (remainder: DIGIT, err: It also doesn't consider the case that y has fewer than three digits, etc. The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. */ -_private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { - // if err = error_if_immutable(quotient, remainder); err != nil { return err; } - // if err = clear_if_uninitialized(quotient, numerator, denominator); err != nil { return err; } +_private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + + if err = error_if_immutable(quotient, remainder); err != nil { return err; } q, x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; - defer destroy(q, x, y, t1, t2); + defer internal_destroy(q, x, y, t1, t2); - if err = grow(q, numerator.used + 2); err != nil { return err; } + if err = internal_grow(q, numerator.used + 2); err != nil { return err; } q.used = numerator.used + 2; - if err = init_multi(t1, t2); err != nil { return err; } - if err = copy(x, numerator); err != nil { return err; } - if err = copy(y, denominator); err != nil { return err; } + if err = internal_init_multi(t1, t2); err != nil { return err; } + if err = internal_copy(x, numerator); err != nil { return err; } + if err = internal_copy(y, denominator); err != nil { return err; } /* Fix the sign. @@ -338,13 +347,12 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In /* Normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] */ - norm, _ := count_bits(y); - norm %= _DIGIT_BITS; + norm := internal_count_bits(y) % _DIGIT_BITS; if norm < _DIGIT_BITS - 1 { norm = (_DIGIT_BITS - 1) - norm; - if err = shl(x, x, norm); err != nil { return err; } - if err = shl(y, y, norm); err != nil { return err; } + if err = internal_shl(x, x, norm); err != nil { return err; } + if err = internal_shl(y, y, norm); err != nil { return err; } } else { norm = 0; } @@ -360,19 +368,19 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In y = y*b**{n-t} */ - if err = shl_digit(y, n - t); err != nil { return err; } + if err = internal_shl_digit(y, n - t); err != nil { return err; } - c, _ := cmp(x, y); + c := internal_cmp(x, y); for c != -1 { q.digit[n - t] += 1; - if err = sub(x, x, y); err != nil { return err; } - c, _ = cmp(x, y); + if err = internal_sub(x, x, y); err != nil { return err; } + c = internal_cmp(x, y); } /* Reset y by shifting it back down. */ - shr_digit(y, n - t); + internal_shr_digit(y, n - t); /* Step 3. for i from n down to (t + 1). @@ -411,11 +419,11 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In /* Find left hand. */ - zero(t1); + internal_zero(t1); t1.digit[0] = ((t - 1) < 0) ? 0 : y.digit[t - 1]; t1.digit[1] = y.digit[t]; t1.used = 2; - if err = mul(t1, t1, q.digit[(i - t) - 1]); err != nil { return err; } + if err = internal_mul(t1, t1, q.digit[(i - t) - 1]); err != nil { return err; } /* Find right hand. @@ -425,7 +433,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In t2.digit[2] = x.digit[i]; t2.used = 3; - if t1_t2, _ := cmp_mag(t1, t2); t1_t2 != 1 { + if t1_t2 := internal_cmp_mag(t1, t2); t1_t2 != 1 { break; } iter += 1; if iter > 100 { return .Max_Iterations_Reached; } @@ -435,16 +443,16 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ if err = int_mul_digit(t1, y, q.digit[(i - t) - 1]); err != nil { return err; } - if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } - if err = sub(x, x, t1); err != nil { return err; } + if err = internal_shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = internal_sub(x, x, t1); err != nil { return err; } /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ if x.sign == .Negative { - if err = copy(t1, y); err != nil { return err; } - if err = shl_digit(t1, (i - t) - 1); err != nil { return err; } - if err = add(x, x, t1); err != nil { return err; } + if err = internal_copy(t1, y); err != nil { return err; } + if err = internal_shl_digit(t1, (i - t) - 1); err != nil { return err; } + if err = internal_add(x, x, t1); err != nil { return err; } q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK; } @@ -458,14 +466,14 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In x.sign = .Zero_or_Positive if z else numerator.sign; if quotient != nil { - clamp(q); - swap(q, quotient); + internal_clamp(q); + internal_swap(q, quotient); quotient.sign = .Negative if neg else .Zero_or_Positive; } if remainder != nil { - if err = shr(x, x, norm); err != nil { return err; } - swap(x, remainder); + if err = internal_shr(x, x, norm); err != nil { return err; } + internal_swap(x, remainder); } return nil; @@ -601,7 +609,9 @@ _private_int_recursive_product :: proc(res: ^Int, start, stop: int, level := int If neither result is wanted, we have nothing to do. */ -_private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { +_private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + if res_gcd == nil && res_lcm == nil { return nil; } /* @@ -612,10 +622,10 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { GCD(0, 0) and LCM(0, 0) are both 0. */ if res_gcd != nil { - if err = zero(res_gcd); err != nil { return err; } + if err = internal_zero(res_gcd); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } + if err = internal_zero(res_lcm); err != nil { return err; } } return nil; } else if a.used == 0 { @@ -623,10 +633,10 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { We can early out with GCD = B and LCM = 0 */ if res_gcd != nil { - if err = abs(res_gcd, b); err != nil { return err; } + if err = internal_abs(res_gcd, b); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } + if err = internal_zero(res_lcm); err != nil { return err; } } return nil; } else if b.used == 0 { @@ -634,25 +644,25 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { We can early out with GCD = A and LCM = 0 */ if res_gcd != nil { - if err = abs(res_gcd, a); err != nil { return err; } + if err = internal_abs(res_gcd, a); err != nil { return err; } } if res_lcm != nil { - if err = zero(res_lcm); err != nil { return err; } + if err = internal_zero(res_lcm); err != nil { return err; } } return nil; } temp_gcd_res := &Int{}; - defer destroy(temp_gcd_res); + defer internal_destroy(temp_gcd_res); /* If neither `a` or `b` was zero, we need to compute `gcd`. Get copies of `a` and `b` we can modify. */ u, v := &Int{}, &Int{}; - defer destroy(u, v); - if err = copy(u, a); err != nil { return err; } - if err = copy(v, b); err != nil { return err; } + defer internal_destroy(u, v); + if err = internal_copy(u, a); err != nil { return err; } + if err = internal_copy(v, b); err != nil { return err; } /* Must be positive for the remainder of the algorithm. @@ -662,37 +672,37 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { /* B1. Find the common power of two for `u` and `v`. */ - u_lsb, _ := count_lsb(u); - v_lsb, _ := count_lsb(v); + u_lsb, _ := internal_count_lsb(u); + v_lsb, _ := internal_count_lsb(v); k := min(u_lsb, v_lsb); if k > 0 { /* Divide the power of two out. */ - if err = shr(u, u, k); err != nil { return err; } - if err = shr(v, v, k); err != nil { return err; } + if err = internal_shr(u, u, k); err != nil { return err; } + if err = internal_shr(v, v, k); err != nil { return err; } } /* Divide any remaining factors of two out. */ if u_lsb != k { - if err = shr(u, u, u_lsb - k); err != nil { return err; } + if err = internal_shr(u, u, u_lsb - k); err != nil { return err; } } if v_lsb != k { - if err = shr(v, v, v_lsb - k); err != nil { return err; } + if err = internal_shr(v, v, v_lsb - k); err != nil { return err; } } for v.used != 0 { /* Make sure `v` is the largest. */ - if c, _ := cmp_mag(u, v); c == 1 { + if internal_cmp_mag(u, v) == 1 { /* Swap `u` and `v` to make sure `v` is >= `u`. */ - swap(u, v); + internal_swap(u, v); } /* @@ -703,14 +713,14 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { /* Divide out all factors of two. */ - b, _ := count_lsb(v); - if err = shr(v, v, b); err != nil { return err; } + b, _ := internal_count_lsb(v); + if err = internal_shr(v, v, b); err != nil { return err; } } /* Multiply by 2**k which we divided out at the beginning. */ - if err = shl(temp_gcd_res, u, k); err != nil { return err; } + if err = internal_shl(temp_gcd_res, u, k); err != nil { return err; } temp_gcd_res.sign = .Zero_or_Positive; /* @@ -718,7 +728,7 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { If we don't want `lcm`, we're done. */ if res_lcm == nil { - swap(temp_gcd_res, res_gcd); + internal_swap(temp_gcd_res, res_gcd); return nil; } @@ -726,7 +736,7 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { Computes least common multiple as `|a*b|/gcd(a,b)` Divide the smallest by the GCD. */ - if c, _ := cmp_mag(a, b); c == -1 { + if internal_cmp_mag(a, b) == -1 { /* Store quotient in `t2` such that `t2 * b` is the LCM. */ @@ -741,7 +751,7 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { } if res_gcd != nil { - swap(temp_gcd_res, res_gcd); + internal_swap(temp_gcd_res, res_gcd); } /* @@ -751,6 +761,104 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) { return err; } +/* + Internal implementation of log. + Assumes `a` not to be `nil` and to have been initialized. +*/ +_private_int_log :: proc(a: ^Int, base: DIGIT, allocator := context.allocator) -> (res: int, err: Error) { + bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(bracket_low, bracket_high, bracket_mid, t, bi_base); + + ic := #force_inline internal_cmp(a, base); + if ic == -1 || ic == 0 { + return 1 if ic == 0 else 0, nil; + } + + if err = internal_set(bi_base, base, true, allocator); err != nil { return -1, err; } + if err = internal_clear(bracket_mid, false, allocator); err != nil { return -1, err; } + if err = internal_clear(t, false, allocator); err != nil { return -1, err; } + if err = internal_one(bracket_low, false, allocator); err != nil { return -1, err; } + if err = internal_set(bracket_high, base, false, allocator); err != nil { return -1, err; } + + low := 0; high := 1; + + /* + A kind of Giant-step/baby-step algorithm. + Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/ + The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped + for small n. + */ + + for { + /* + Iterate until `a` is bracketed between low + high. + */ + if #force_inline internal_cmp(bracket_high, a) != -1 { break; } + + low = high; + if err = #force_inline internal_copy(bracket_low, bracket_high); err != nil { return -1, err; } + high <<= 1; + if err = #force_inline internal_sqr(bracket_high, bracket_high); err != nil { return -1, err; } + } + + for (high - low) > 1 { + mid := (high + low) >> 1; + + if err = #force_inline internal_pow(t, bi_base, mid - low); err != nil { return -1, err; } + + if err = #force_inline internal_mul(bracket_mid, bracket_low, t); err != nil { return -1, err; } + + mc := #force_inline internal_cmp(a, bracket_mid); + switch mc { + case -1: + high = mid; + internal_swap(bracket_mid, bracket_high); + case 0: + return mid, nil; + case 1: + low = mid; + internal_swap(bracket_mid, bracket_low); + } + } + + fc := #force_inline internal_cmp(bracket_high, a); + res = high if fc == 0 else low; + + return; +} + +/* + Returns the log2 of an `Int`. + Assumes `a` not to be `nil` and to have been initialized. + Also assumes `base` is a power of two. +*/ +_private_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) { + base := base; + y: int; + for y = 0; base & 1 == 0; { + y += 1; + base >>= 1; + } + log = internal_count_bits(a); + return (log - 1) / y, err; +} + +/* + Copies DIGITs from `src` to `dest`. + Assumes `src` and `dest` to not be `nil` and have been initialized. +*/ +_private_copy_digits :: proc(dest, src: ^Int, digits: int) -> (err: Error) { + digits := digits; + /* + If dest == src, do nothing + */ + if dest == src { return nil; } + + digits = min(digits, len(src.digit), len(dest.digit)); + mem.copy_non_overlapping(&dest.digit[0], &src.digit[0], size_of(DIGIT) * digits); + return nil; +} + /* ======================== End of private procedures ======================= diff --git a/core/math/big/public.odin b/core/math/big/public.odin index f5dbbe06d..3ab5a8924 100644 --- a/core/math/big/public.odin +++ b/core/math/big/public.odin @@ -22,11 +22,13 @@ package big */ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a, b); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, a, b); err != nil { return err; } /* All parameters have been initialized. */ - return #force_inline internal_int_add_signed(dest, a, b, allocator); + return #force_inline internal_int_add_signed(dest, a, b); } /* @@ -37,11 +39,13 @@ int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error */ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. */ - if err = grow(dest, a.used + 1, false, allocator); err != nil { return err; } + if err = grow(dest, a.used + 1); err != nil { return err; } /* All parameters have been initialized. @@ -54,11 +58,13 @@ int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato */ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, number, decrease); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, number, decrease); err != nil { return err; } /* All parameters have been initialized. */ - return #force_inline internal_int_sub_signed(dest, number, decrease, allocator); + return #force_inline internal_int_sub_signed(dest, number, decrease); } /* @@ -69,11 +75,13 @@ int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> */ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return err; } /* Grow destination as required. */ - if err = grow(dest, a.used + 1, false, allocator); err != nil { return err; } + if err = grow(dest, a.used + 1); err != nil { return err; } /* All parameters have been initialized. @@ -85,8 +93,10 @@ int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocato dest = src / 2 dest = src >> 1 */ -int_halve :: proc(dest, src: ^Int) -> (err: Error) { +int_halve :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. @@ -102,8 +112,10 @@ shr1 :: halve; dest = src * 2 dest = src << 1 */ -int_double :: proc(dest, src: ^Int) -> (err: Error) { +int_double :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } /* Grow destination as required. @@ -120,9 +132,11 @@ shl1 :: double; */ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(src, dest); err != nil { return err; } - return #force_inline internal_int_mul_digit(dest, src, multiplier, allocator); + return #force_inline internal_int_mul_digit(dest, src, multiplier); } /* @@ -130,9 +144,11 @@ int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.a */ int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src, multiplier); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, src, multiplier); err != nil { return err; } - return #force_inline internal_int_mul(dest, src, multiplier, allocator); + return #force_inline internal_int_mul(dest, src, multiplier); } mul :: proc { int_mul, int_mul_digit, }; @@ -143,7 +159,9 @@ sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src); } divmod. Both the quotient and remainder are optional and may be passed a nil. */ -int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { +int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* Early out if neither of the results is wanted. */ @@ -153,23 +171,29 @@ int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: E return #force_inline internal_divmod(quotient, remainder, numerator, denominator); } -int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { +int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (remainder: DIGIT, err: Error) { assert_if_nil(quotient, numerator); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(numerator); err != nil { return 0, err; } return #force_inline internal_divmod(quotient, numerator, denominator); } divmod :: proc{ int_divmod, int_divmod_digit, }; -int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) { +int_div :: proc(quotient, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(quotient, numerator, denominator); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(numerator, denominator); err != nil { return err; } return #force_inline internal_divmod(quotient, nil, numerator, denominator); } -int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (err: Error) { +int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (err: Error) { assert_if_nil(quotient, numerator); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(numerator); err != nil { return err; } remainder: DIGIT; @@ -183,15 +207,17 @@ div :: proc { int_div, int_div_digit, }; 0 <= remainder < denominator if denominator > 0 denominator < remainder <= 0 if denominator < 0 */ -int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) { +int_mod :: proc(remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(remainder, numerator, denominator); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(numerator, denominator); err != nil { return err; } return #force_inline internal_int_mod(remainder, numerator, denominator); } -int_mod_digit :: proc(numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) { - return #force_inline internal_divmod(nil, numerator, denominator); +int_mod_digit :: proc(numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (remainder: DIGIT, err: Error) { + return #force_inline internal_divmod(nil, numerator, denominator, allocator); } mod :: proc { int_mod, int_mod_digit, }; @@ -199,8 +225,10 @@ mod :: proc { int_mod, int_mod_digit, }; /* remainder = (number + addend) % modulus. */ -int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) { +int_addmod :: proc(remainder, number, addend, modulus: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(remainder, number, addend); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(number, addend, modulus); err != nil { return err; } return #force_inline internal_addmod(remainder, number, addend, modulus); @@ -210,8 +238,10 @@ addmod :: proc { int_addmod, }; /* remainder = (number - decrease) % modulus. */ -int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) { +int_submod :: proc(remainder, number, decrease, modulus: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(remainder, number, decrease); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(number, decrease, modulus); err != nil { return err; } return #force_inline internal_submod(remainder, number, decrease, modulus); @@ -221,8 +251,10 @@ submod :: proc { int_submod, }; /* remainder = (number * multiplicand) % modulus. */ -int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) { +int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(remainder, number, multiplicand); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(number, multiplicand, modulus); err != nil { return err; } return #force_inline internal_mulmod(remainder, number, multiplicand, modulus); @@ -232,8 +264,10 @@ mulmod :: proc { int_mulmod, }; /* remainder = (number * number) % modulus. */ -int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { +int_sqrmod :: proc(remainder, number, modulus: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(remainder, number, modulus); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(number, modulus); err != nil { return err; } return #force_inline internal_sqrmod(remainder, number, modulus); @@ -241,11 +275,11 @@ int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) { sqrmod :: proc { int_sqrmod, }; -int_factorial :: proc(res: ^Int, n: int) -> (err: Error) { +int_factorial :: proc(res: ^Int, n: int, allocator := context.allocator) -> (err: Error) { if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; } assert_if_nil(res); - return #force_inline internal_int_factorial(res, n); + return #force_inline internal_int_factorial(res, n, allocator); } factorial :: proc { int_factorial, }; @@ -265,17 +299,18 @@ factorial :: proc { int_factorial, }; k, start from previous result */ -int_choose_digit :: proc(res: ^Int, n, k: int) -> (err: Error) { +int_choose_digit :: proc(res: ^Int, n, k: int, allocator := context.allocator) -> (err: Error) { assert_if_nil(res); - if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; } + context.allocator = allocator; - if k > n { return zero(res); } + if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; } + if k > n { return internal_zero(res); } /* res = n! / (k! * (n - k)!) */ n_fac, k_fac, n_minus_k_fac := &Int{}, &Int{}, &Int{}; - defer destroy(n_fac, k_fac, n_minus_k_fac); + defer internal_destroy(n_fac, k_fac, n_minus_k_fac); if err = #force_inline internal_int_factorial(n_minus_k_fac, n - k); err != nil { return err; } if err = #force_inline internal_int_factorial(k_fac, k); err != nil { return err; } @@ -294,9 +329,9 @@ choose :: proc { int_choose_digit, }; int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.allocator) -> (err: Error) { if res_gcd == nil && res_lcm == nil { return nil; } assert_if_nil(a, b); + context.allocator = allocator; - if err = internal_clear_if_uninitialized(a, allocator); err != nil { return err; } - if err = internal_clear_if_uninitialized(b, allocator); err != nil { return err; } + if err = internal_clear_if_uninitialized(a, b); err != nil { return err; } return #force_inline internal_int_gcd_lcm(res_gcd, res_lcm, a, b); } gcd_lcm :: proc { int_gcd_lcm, }; @@ -304,24 +339,25 @@ gcd_lcm :: proc { int_gcd_lcm, }; /* Greatest Common Divisor. */ -int_gcd :: proc(res, a, b: ^Int) -> (err: Error) { - return #force_inline int_gcd_lcm(res, nil, a, b); +int_gcd :: proc(res, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + return #force_inline int_gcd_lcm(res, nil, a, b, allocator); } gcd :: proc { int_gcd, }; /* Least Common Multiple. */ -int_lcm :: proc(res, a, b: ^Int) -> (err: Error) { - return #force_inline int_gcd_lcm(nil, res, a, b); +int_lcm :: proc(res, a, b: ^Int, allocator := context.allocator) -> (err: Error) { + return #force_inline int_gcd_lcm(nil, res, a, b, allocator); } lcm :: proc { int_lcm, }; /* remainder = numerator % (1 << bits) */ -int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) { +int_mod_bits :: proc(remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { assert_if_nil(remainder, numerator); + context.allocator = allocator; if err = internal_clear_if_uninitialized(remainder, numerator); err != nil { return err; } if bits < 0 { return .Invalid_Argument; } @@ -335,8 +371,10 @@ mod_bits :: proc { int_mod_bits, }; /* Logs and roots and such. */ -int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) { +int_log :: proc(a: ^Int, base: DIGIT, allocator := context.allocator) -> (res: int, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return 0, err; } return #force_inline internal_int_log(a, base); @@ -350,8 +388,10 @@ log :: proc { int_log, digit_log, }; /* Calculate `dest = base^power` using a square-multiply algorithm. */ -int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { +int_pow :: proc(dest, base: ^Int, power: int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, base); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, base); err != nil { return err; } return #force_inline internal_int_pow(dest, base, power); @@ -360,10 +400,10 @@ int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) { /* Calculate `dest = base^power` using a square-multiply algorithm. */ -int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) { +int_pow_int :: proc(dest: ^Int, base, power: int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest); - return #force_inline internal_pow(dest, base, power); + return #force_inline internal_pow(dest, base, power, allocator); } pow :: proc { int_pow, int_pow_int, small_pow, }; @@ -376,8 +416,10 @@ small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) { /* This function is less generic than `root_n`, simpler and faster. */ -int_sqrt :: proc(dest, src: ^Int) -> (err: Error) { +int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { assert_if_nil(dest, src); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(dest, src); err != nil { return err; } return #force_inline internal_int_sqrt(dest, src); @@ -392,7 +434,9 @@ sqrt :: proc { int_sqrt, }; This algorithm uses Newton's approximation `x[i+1] = x[i] - f(x[i])/f'(x[i])`, which will find the root in `log(n)` time where each step involves a fair bit. */ -int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) { +int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + /* Fast path for n == 2. */ @@ -418,36 +462,46 @@ int_is_initialized :: proc(a: ^Int) -> bool { return #force_inline internal_int_is_initialized(a); } -int_is_zero :: proc(a: ^Int) -> (zero: bool, err: Error) { +int_is_zero :: proc(a: ^Int, allocator := context.allocator) -> (zero: bool, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_zero(a), nil; } -int_is_positive :: proc(a: ^Int) -> (positive: bool, err: Error) { +int_is_positive :: proc(a: ^Int, allocator := context.allocator) -> (positive: bool, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_positive(a), nil; } -int_is_negative :: proc(a: ^Int) -> (negative: bool, err: Error) { +int_is_negative :: proc(a: ^Int, allocator := context.allocator) -> (negative: bool, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_negative(a), nil; } -int_is_even :: proc(a: ^Int) -> (even: bool, err: Error) { +int_is_even :: proc(a: ^Int, allocator := context.allocator) -> (even: bool, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_even(a), nil; } -int_is_odd :: proc(a: ^Int) -> (odd: bool, err: Error) { +int_is_odd :: proc(a: ^Int, allocator := context.allocator) -> (odd: bool, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_odd(a), nil; @@ -457,8 +511,10 @@ platform_int_is_power_of_two :: #force_inline proc(a: int) -> bool { return ((a) != 0) && (((a) & ((a) - 1)) == 0); } -int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { +int_is_power_of_two :: proc(a: ^Int, allocator := context.allocator) -> (res: bool, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return false, err; } return #force_inline internal_is_power_of_two(a), nil; @@ -467,8 +523,10 @@ int_is_power_of_two :: proc(a: ^Int) -> (res: bool, err: Error) { /* Compare two `Int`s, signed. */ -int_compare :: proc(a, b: ^Int) -> (comparison: int, err: Error) { +int_compare :: proc(a, b: ^Int, allocator := context.allocator) -> (comparison: int, err: Error) { assert_if_nil(a, b); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a, b); err != nil { return 0, err; } return #force_inline internal_cmp(a, b), nil; @@ -478,8 +536,10 @@ int_cmp :: int_compare; /* Compare an `Int` to an unsigned number upto the size of the backing type. */ -int_compare_digit :: proc(a: ^Int, b: DIGIT) -> (comparison: int, err: Error) { +int_compare_digit :: proc(a: ^Int, b: DIGIT, allocator := context.allocator) -> (comparison: int, err: Error) { assert_if_nil(a); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a); err != nil { return 0, err; } return #force_inline internal_cmp_digit(a, b), nil; @@ -489,8 +549,10 @@ int_cmp_digit :: int_compare_digit; /* Compare the magnitude of two `Int`s, unsigned. */ -int_compare_magnitude :: proc(a, b: ^Int) -> (res: int, err: Error) { +int_compare_magnitude :: proc(a, b: ^Int, allocator := context.allocator) -> (res: int, err: Error) { assert_if_nil(a, b); + context.allocator = allocator; + if err = internal_clear_if_uninitialized(a, b); err != nil { return 0, err; } return #force_inline internal_cmp_mag(a, b), nil; diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 28c5a2559..acfcbcf70 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -19,9 +19,10 @@ import "core:mem" */ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) { assert_if_nil(a); + context.allocator = allocator; a := a; radix := radix; - if err = clear_if_uninitialized(a, allocator); err != nil { return "", err; } + if err = clear_if_uninitialized(a); err != nil { return "", err; } /* Radix defaults to 10. */ @@ -46,7 +47,7 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc /* Allocate the buffer we need. */ - buffer := make([]u8, size, allocator); + buffer := make([]u8, size); /* Write the digits out into the buffer. @@ -62,16 +63,17 @@ int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, alloc */ int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) { assert_if_nil(a); + context.allocator = allocator; a := a; radix := radix; - if err = clear_if_uninitialized(a, allocator); err != nil { return "", err; } + if err = clear_if_uninitialized(a); err != nil { return "", err; } /* Radix defaults to 10. */ radix = radix if radix > 0 else 10; s: string; - s, err = int_itoa_string(a, radix, true, allocator); + s, err = int_itoa_string(a, radix, true); return cstring(raw_data(s)), err; } @@ -237,7 +239,10 @@ int_to_cstring :: int_itoa_cstring; Read a string [ASCII] in a given radix. */ int_atoi :: proc(res: ^Int, input: string, radix: i8, allocator := context.allocator) -> (err: Error) { + assert_if_nil(res); input := input; + context.allocator = allocator; + /* Make sure the radix is ok. */ @@ -247,7 +252,7 @@ int_atoi :: proc(res: ^Int, input: string, radix: i8, allocator := context.alloc /* Set the integer to the default of zero. */ - if err = zero(res, false, allocator); err != nil { return err; } + if err = internal_zero(res); err != nil { return err; } /* We'll interpret an empty string as zero. @@ -319,23 +324,21 @@ atoi :: proc { int_atoi, }; /* We size for `string` by default. */ -radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, err: Error) { +radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := context.allocator) -> (size: int, err: Error) { a := a; - if radix < 2 || radix > 64 { - return -1, .Invalid_Argument; - } - if err = clear_if_uninitialized(a); err != nil { - return 0, err; - } + assert_if_nil(a); - if z, _ := is_zero(a); z { + if radix < 2 || radix > 64 { return -1, .Invalid_Argument; } + if err = clear_if_uninitialized(a); err != nil { return {}, err; } + + if internal_is_zero(a) { if zero_terminate { return 2, nil; } return 1, nil; } - if pot, _ := is_power_of_two(a); pot { + if internal_is_power_of_two(a) { /* Calculate `log` on a temporary "copy" with its sign set to positive. */ @@ -345,28 +348,26 @@ radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false) -> (size: int, e digit = a.digit, }; - if size, err = log(t, DIGIT(radix)); err != nil { - return 0, err; - } + if size, err = internal_log(t, DIGIT(radix)); err != nil { return {}, err; } } else { la, k := &Int{}, &Int{}; - defer destroy(la, k); + defer internal_destroy(la, k); /* la = floor(log_2(a)) + 1 */ - bit_count, _ := count_bits(a); - err = set(la, bit_count); + bit_count := internal_count_bits(a); + if err = internal_set(la, bit_count); err != nil { return {}, err; } /* k = floor(2^29/log_2(radix)) + 1 */ lb := _log_bases; - err = set(k, lb[radix]); + if err = internal_set(k, lb[radix]); err != nil { return {}, err; } /* n = floor((la * k) / 2^29) + 1 */ - if err = mul(k, la, k); err != nil { return 0, err; } - if err = shr(k, k, _RADIX_SIZE_SCALE); err != nil { return 0, err; } + if err = internal_mul(k, la, k); err != nil { return 0, err; } + if err = internal_shr(k, k, _RADIX_SIZE_SCALE); err != nil { return {}, err; } /* The "+1" here is the "+1" in "floor((la * k) / 2^29) + 1" */ /* n = n + 1 + EOS + sign */ - size_, _ := get(k, u128); + size_, _ := internal_get(k, u128); size = int(size_); } @@ -429,11 +430,14 @@ RADIX_TABLE_REVERSE_SIZE :: 80; Stores a bignum as a ASCII string in a given radix (2..64) The buffer must be appropriately sized. This routine doesn't check. */ -_itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (written: int, err: Error) { +_itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false, allocator := context.allocator) -> (written: int, err: Error) { + assert_if_nil(a); + context.allocator = allocator; + temp, denominator := &Int{}, &Int{}; - if err = copy(temp, a); err != nil { return 0, err; } - if err = set(denominator, radix); err != nil { return 0, err; } + if err = internal_copy(temp, a); err != nil { return 0, err; } + if err = internal_set(denominator, radix); err != nil { return 0, err; } available := len(buffer); if zero_terminate { @@ -448,7 +452,7 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false remainder: DIGIT; for { if remainder, err = #force_inline internal_divmod(temp, temp, DIGIT(radix)); err != nil { - destroy(temp, denominator); + internal_destroy(temp, denominator); return len(buffer) - available, err; } available -= 1; @@ -463,7 +467,7 @@ _itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false buffer[available] = '-'; } - destroy(temp, denominator); + internal_destroy(temp, denominator); /* If we overestimated the size, we need to move the buffer left. diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 4aa0b93bf..aae3c1503 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -40,7 +40,7 @@ PyRes :: struct { err: Error; aa, bb, sum := &Int{}, &Int{}, &Int{}; - defer destroy(aa, bb, sum); + defer internal_destroy(aa, bb, sum); if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err}; } @@ -61,7 +61,7 @@ PyRes :: struct { err: Error; aa, bb, sum := &Int{}, &Int{}, &Int{}; - defer destroy(aa, bb, sum); + defer internal_destroy(aa, bb, sum); if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err}; } @@ -82,7 +82,7 @@ PyRes :: struct { err: Error; aa, bb, product := &Int{}, &Int{}, &Int{}; - defer destroy(aa, bb, product); + defer internal_destroy(aa, bb, product); if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err}; } @@ -102,7 +102,7 @@ PyRes :: struct { err: Error; aa, bb, quotient := &Int{}, &Int{}, &Int{}; - defer destroy(aa, bb, quotient); + defer internal_destroy(aa, bb, quotient); if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err}; } if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err}; } @@ -124,7 +124,7 @@ PyRes :: struct { l: int; aa := &Int{}; - defer destroy(aa); + defer internal_destroy(aa); if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err}; } if l, err = #force_inline internal_log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err}; } @@ -149,7 +149,7 @@ PyRes :: struct { err: Error; dest, bb := &Int{}, &Int{}; - defer destroy(dest, bb); + defer internal_destroy(dest, bb); if err = atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err}; } if err = #force_inline internal_pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err}; } @@ -168,7 +168,7 @@ PyRes :: struct { err: Error; src := &Int{}; - defer destroy(src); + defer internal_destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err}; } if err = #force_inline internal_sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err}; } @@ -187,7 +187,7 @@ PyRes :: struct { err: Error; src := &Int{}; - defer destroy(src); + defer internal_destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err}; } if err = #force_inline internal_root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err}; } @@ -206,7 +206,7 @@ PyRes :: struct { err: Error; src := &Int{}; - defer destroy(src); + defer internal_destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err}; } if err = #force_inline internal_shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; } @@ -225,7 +225,7 @@ PyRes :: struct { err: Error; src := &Int{}; - defer destroy(src); + defer internal_destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err}; } if err = #force_inline internal_shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; } @@ -244,7 +244,7 @@ PyRes :: struct { err: Error; src := &Int{}; - defer destroy(src); + defer internal_destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err}; } if err = #force_inline internal_shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err}; } @@ -263,7 +263,7 @@ PyRes :: struct { err: Error; src := &Int{}; - defer destroy(src); + defer internal_destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err}; } if err = #force_inline internal_shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; } @@ -282,7 +282,7 @@ PyRes :: struct { err: Error; src := &Int{}; - defer destroy(src); + defer internal_destroy(src); if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err}; } if err = #force_inline internal_shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err}; } @@ -301,9 +301,9 @@ PyRes :: struct { err: Error; dest := &Int{}; - defer destroy(dest); + defer internal_destroy(dest); - if err = #force_inline factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err}; } + if err = #force_inline internal_int_factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); @@ -319,11 +319,11 @@ PyRes :: struct { err: Error; ai, bi, dest := &Int{}, &Int{}, &Int{}; - defer destroy(ai, bi, dest); + defer internal_destroy(ai, bi, dest); if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err}; } if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err}; } - if err = #force_inline gcd(dest, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err}; } + if err = #force_inline internal_int_gcd_lcm(dest, nil, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); @@ -339,11 +339,11 @@ PyRes :: struct { err: Error; ai, bi, dest := &Int{}, &Int{}, &Int{}; - defer destroy(ai, bi, dest); + defer internal_destroy(ai, bi, dest); if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err}; } if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err}; } - if err = #force_inline lcm(dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err}; } + if err = #force_inline internal_int_gcd_lcm(nil, dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err}; } r: cstring; r, err = int_itoa_cstring(dest, 16, context.temp_allocator); diff --git a/core/math/big/test.py b/core/math/big/test.py index ca0ec98c3..72082c2db 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -530,6 +530,11 @@ if __name__ == '__main__': print("---- math/big with two random {bits:,} bit numbers ----".format(bits=BITS)) print() + # + # We've already tested up to the 10th root. + # + TEST_ROOT_N_PARAMS = [2, 3, 4, 5, 6] + for test_proc in RANDOM_TESTS: if BITS > 1_200 and test_proc in SKIP_LARGE: continue if BITS > 4_096 and test_proc in SKIP_LARGEST: continue @@ -545,6 +550,8 @@ if __name__ == '__main__': UNTIL_TIME = TOTAL_TIME + BITS / TIMED_BITS_PER_SECOND # We run each test for a second per 20k bits + index = 0 + while we_iterate(): a = randint(-(1 << BITS), 1 << BITS) b = randint(-(1 << BITS), 1 << BITS) @@ -566,7 +573,8 @@ if __name__ == '__main__': b = Error.Okay elif test_proc == test_root_n: a = randint(1, 1 << BITS) - b = randint(1, 10); + b = TEST_ROOT_N_PARAMS[index] + index = (index + 1) % len(TEST_ROOT_N_PARAMS) elif test_proc == test_shl_digit: b = randint(0, 10); elif test_proc == test_shr_digit: From 6c681b258c7118b4a6aa7bf27af38f5556ad7599 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 10 Aug 2021 20:52:55 +0200 Subject: [PATCH 099/105] big: Add `_private_int_sqr_comba`. --- core/math/big/example.odin | 23 +++++++-- core/math/big/helpers.odin | 2 - core/math/big/internal.odin | 19 ++++--- core/math/big/logical.odin | 2 - core/math/big/private.odin | 99 +++++++++++++++++++++++++++++++++++++ core/math/big/radix.odin | 4 ++ core/math/big/test.odin | 16 ++++++ core/math/big/test.py | 38 ++++++++++---- core/math/big/tune.odin | 1 + 9 files changed, 180 insertions(+), 24 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 02671a809..3d51ac400 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -52,10 +52,12 @@ FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS, } print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) { - as, err := itoa(a, base); + assert_if_nil(a); + as, err := itoa(a, base); defer delete(as); - cb, _ := count_bits(a); + + cb := internal_count_bits(a); if print_name { fmt.printf("%v", name); } @@ -64,7 +66,7 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline } fmt.printf("%v", as); if print_extra_info { - fmt.printf(" (base: %v, bits used: %v, flags: %v)", base, cb, a.flags); + fmt.printf(" (base: %v, bits: %v (digits: %v), flags: %v)", base, cb, a.used, a.flags); } if newline { fmt.println(); @@ -75,8 +77,19 @@ demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - err := set(a, 1); - fmt.printf("err: %v\n", err); + err: Error; + bs: string; + + if err = factorial(a, 500); err != nil { fmt.printf("factorial err: %v\n", err); return; } + { + SCOPED_TIMING(.sqr); + if err = sqr(b, a); err != nil { fmt.printf("sqr err: %v\n", err); return; } + } + + bs, err = itoa(b, 10); + defer delete(bs); + + assert(bs[:50] == "14887338741396604108836218987068397819515734169330"); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index c0aa0c8bb..d0262ba19 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -9,10 +9,8 @@ package big The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. */ -import "core:mem" import "core:intrinsics" import rnd "core:math/rand" -import "core:fmt" /* TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere. diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index b05aeaf2e..9b267176d 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -36,6 +36,8 @@ import "core:mem" import "core:intrinsics" import rnd "core:math/rand" +//import "core:fmt" + /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -624,16 +626,21 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc /* Do we need to square? */ - if false && src.used >= SQR_TOOM_CUTOFF { + if src.used >= SQR_TOOM_CUTOFF { /* Use Toom-Cook? */ // err = s_mp_sqr_toom(a, c); - } else if false && src.used >= SQR_KARATSUBA_CUTOFF { + // fmt.printf("_private_int_sqr_toom: %v\n", src.used); + err = #force_inline _private_int_sqr(dest, src); + } else if src.used >= SQR_KARATSUBA_CUTOFF { /* Karatsuba? */ // err = s_mp_sqr_karatsuba(a, c); - } else if false && ((src.used * 2) + 1) < _WARRAY && - src.used < (_MAX_COMBA / 2) { - /* Fast comba? */ - // err = s_mp_sqr_comba(a, c); + // fmt.printf("_private_int_sqr_karatsuba: %v\n", src.used); + err = #force_inline _private_int_sqr(dest, src); + } else if ((src.used * 2) + 1) < _WARRAY && src.used < (_MAX_COMBA / 2) { + /* + Fast comba? + */ + err = #force_inline _private_int_sqr_comba(dest, src); } else { err = #force_inline _private_int_sqr(dest, src); } diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index 1050de0c4..f72ecb18d 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -11,8 +11,6 @@ package big This file contains logical operations like `and`, `or` and `xor`. */ -import "core:mem" - /* The `and`, `or` and `xor` binops differ in two lines only. We could handle those with a switch, but that adds overhead. diff --git a/core/math/big/private.odin b/core/math/big/private.odin index fab63977c..f49cbc51a 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -255,6 +255,105 @@ _private_int_sqr :: proc(dest, src: ^Int, allocator := context.allocator) -> (er return err; } +/* + The jist of squaring... + You do like mult except the offset of the tmpx [one that starts closer to zero] can't equal the offset of tmpy. + So basically you set up iy like before then you min it with (ty-tx) so that it never happens. + You double all those you add in the inner loop. After that loop you do the squares and add them in. + + Assumes `dest` and `src` not to be `nil` and `src` to have been initialized. +*/ +_private_int_sqr_comba :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + + W: [_WARRAY]DIGIT = ---; + + /* + Grow the destination as required. + */ + pa := uint(src.used) + uint(src.used); + if err = internal_grow(dest, int(pa)); err != nil { return err; } + + /* + Number of output digits to produce. + */ + W1 := _WORD(0); + _W : _WORD = ---; + ix := uint(0); + + #no_bounds_check for ; ix < pa; ix += 1 { + /* + Clear counter. + */ + _W = {}; + + /* + Get offsets into the two bignums. + */ + ty := min(uint(src.used) - 1, ix); + tx := ix - ty; + + /* + This is the number of times the loop will iterate, + essentially while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy := min(uint(src.used) - tx, ty + 1); + + /* + Now for squaring, tx can never equal ty. + We halve the distance since they approach at a rate of 2x, + and we have to round because odd cases need to be executed. + */ + iy = min(iy, ((ty - tx) + 1) >> 1 ); + + /* + Execute loop. + */ + #no_bounds_check for iz := uint(0); iz < iy; iz += 1 { + _W += _WORD(src.digit[tx + iz]) * _WORD(src.digit[ty - iz]); + } + + /* + Double the inner product and add carry. + */ + _W = _W + _W + W1; + + /* + Even columns have the square term in them. + */ + if ix & 1 == 0 { + _W += _WORD(src.digit[ix >> 1]) * _WORD(src.digit[ix >> 1]); + } + + /* + Store it. + */ + W[ix] = DIGIT(_W & _WORD(_MASK)); + + /* + Make next carry. + */ + W1 = _W >> _DIGIT_BITS; + } + + /* + Setup dest. + */ + old_used := dest.used; + dest.used = src.used + src.used; + + #no_bounds_check for ix = 0; ix < pa; ix += 1 { + dest.digit[ix] = W[ix] & _MASK; + } + + /* + Clear unused digits [that existed in the old copy of dest]. + */ + internal_zero_unused(dest, old_used); + + return internal_clamp(dest); +} + /* Divide by three (based on routine from MPI and the GMP manual). */ diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index acfcbcf70..e8468851b 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -9,6 +9,10 @@ package big The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks. This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa). + + TODO: + - Use Barrett reduction for non-powers-of-two. + - Also look at extracting and splatting several digits at once. */ import "core:intrinsics" diff --git a/core/math/big/test.odin b/core/math/big/test.odin index aae3c1503..044a3329e 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -94,6 +94,22 @@ PyRes :: struct { return PyRes{res = r, err = nil}; } +@export test_sqr :: proc "c" (a: cstring) -> (res: PyRes) { + context = runtime.default_context(); + err: Error; + + aa, square := &Int{}, &Int{}; + defer internal_destroy(aa, square); + + if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sqr:atoi(a):", err=err}; } + if err = #force_inline internal_sqr(square, aa); err != nil { return PyRes{res=":sqr:sqr(square,a):", err=err}; } + + r: cstring; + r, err = int_itoa_cstring(square, 16, context.temp_allocator); + if err != nil { return PyRes{res=":sqr:itoa(square):", err=err}; } + return PyRes{res = r, err = nil}; +} + /* NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient. */ diff --git a/core/math/big/test.py b/core/math/big/test.py index 72082c2db..8da890e55 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -124,15 +124,16 @@ initialize_constants() error_string = load(l.test_error_string, [c_byte], c_char_p) -add = load(l.test_add, [c_char_p, c_char_p], Res) -sub = load(l.test_sub, [c_char_p, c_char_p], Res) -mul = load(l.test_mul, [c_char_p, c_char_p], Res) -div = load(l.test_div, [c_char_p, c_char_p], Res) +add = load(l.test_add, [c_char_p, c_char_p], Res) +sub = load(l.test_sub, [c_char_p, c_char_p], Res) +mul = load(l.test_mul, [c_char_p, c_char_p], Res) +sqr = load(l.test_sqr, [c_char_p ], Res) +div = load(l.test_div, [c_char_p, c_char_p], Res) # Powers and such -int_log = load(l.test_log, [c_char_p, c_longlong], Res) -int_pow = load(l.test_pow, [c_char_p, c_longlong], Res) -int_sqrt = load(l.test_sqrt, [c_char_p], Res) +int_log = load(l.test_log, [c_char_p, c_longlong], Res) +int_pow = load(l.test_pow, [c_char_p, c_longlong], Res) +int_sqrt = load(l.test_sqrt, [c_char_p ], Res) int_root_n = load(l.test_root_n, [c_char_p, c_longlong], Res) # Logical operations @@ -218,6 +219,20 @@ def test_mul(a = 0, b = 0, expected_error = Error.Okay): expected_result = a * b return test("test_mul", res, [a, b], expected_error, expected_result) +def test_sqr(a = 0, b = 0, expected_error = Error.Okay): + args = [arg_to_odin(a)] + try: + res = sqr(*args) + except OSError as e: + print("{} while trying to square {} x {}.".format(e, a)) + if EXIT_ON_FAIL: exit(3) + return False + + expected_result = None + if expected_error == Error.Okay: + expected_result = a * a + return test("test_sqr", res, [a], expected_error, expected_result) + def test_div(a = 0, b = 0, expected_error = Error.Okay): args = [arg_to_odin(a), arg_to_odin(b)] res = div(*args) @@ -390,7 +405,11 @@ TESTS = { ], test_mul: [ [ 1234, 5432], - [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7], + [ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7 ], + ], + test_sqr: [ + [ 5432], + [ 0xd3b4e926aaba3040e1c12b5ea553b5 ], ], test_div: [ [ 54321, 12345], @@ -482,7 +501,7 @@ total_failures = 0 # test_shr_signed also tests shr, so we're not going to test shr randomly. # RANDOM_TESTS = [ - test_add, test_sub, test_mul, test_div, + test_add, test_sub, test_mul, test_sqr, test_div, test_log, test_pow, test_sqrt, test_root_n, test_shl_digit, test_shr_digit, test_shl, test_shr_signed, test_gcd, test_lcm, @@ -592,6 +611,7 @@ if __name__ == '__main__': start = time.perf_counter() res = test_proc(a, b) diff = time.perf_counter() - start + TOTAL_TIME += diff if test_proc not in TIMINGS: diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin index fbd454002..59f533d44 100644 --- a/core/math/big/tune.odin +++ b/core/math/big/tune.odin @@ -21,6 +21,7 @@ Category :: enum { choose, lsb, ctz, + sqr, bitfield_extract, }; From 2b274fefbb214533399b804acce8977ecaa3afeb Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 11 Aug 2021 00:37:34 +0200 Subject: [PATCH 100/105] big: Add `_private_int_sqr_karatsuba`. --- core/math/big/example.odin | 19 ++++++++--- core/math/big/internal.odin | 18 +++++----- core/math/big/private.odin | 66 +++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 12 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 3d51ac400..1d9a4cb3c 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -80,16 +80,27 @@ demo :: proc() { err: Error; bs: string; - if err = factorial(a, 500); err != nil { fmt.printf("factorial err: %v\n", err); return; } + // if err = factorial(a, 850); err != nil { fmt.printf("factorial err: %v\n", err); return; } + + foo := "615037959146039477924633848896619112832171971562900618409305032006863881436080"; + if err = atoi(a, foo, 10); err != nil { return; } + print("a: ", a, 10, true, true, true); + fmt.println(); + { SCOPED_TIMING(.sqr); - if err = sqr(b, a); err != nil { fmt.printf("sqr err: %v\n", err); return; } + if err = sqr(b, a); err != nil { fmt.printf("sqr err: %v\n", err); return; } } + fmt.println(); + print("b _sqr_karatsuba: ", b); + fmt.println(); - bs, err = itoa(b, 10); + bs, err = itoa(b, 16); defer delete(bs); - assert(bs[:50] == "14887338741396604108836218987068397819515734169330"); + if bs[:50] != "1C367982F3050A8A3C62A8A7906D165438B54B287AF3F15D36" { + fmt.println("sqr failed"); + } } main :: proc() { diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 9b267176d..00b1ed7bf 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -36,7 +36,7 @@ import "core:mem" import "core:intrinsics" import rnd "core:math/rand" -//import "core:fmt" +// import "core:fmt" /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -627,20 +627,22 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc Do we need to square? */ if src.used >= SQR_TOOM_CUTOFF { - /* Use Toom-Cook? */ - // err = s_mp_sqr_toom(a, c); + /* + Use Toom-Cook? + */ // fmt.printf("_private_int_sqr_toom: %v\n", src.used); - err = #force_inline _private_int_sqr(dest, src); + err = #force_inline _private_int_sqr_karatsuba(dest, src); } else if src.used >= SQR_KARATSUBA_CUTOFF { - /* Karatsuba? */ - // err = s_mp_sqr_karatsuba(a, c); - // fmt.printf("_private_int_sqr_karatsuba: %v\n", src.used); - err = #force_inline _private_int_sqr(dest, src); + /* + Karatsuba? + */ + err = #force_inline _private_int_sqr_karatsuba(dest, src); } else if ((src.used * 2) + 1) < _WARRAY && src.used < (_MAX_COMBA / 2) { /* Fast comba? */ err = #force_inline _private_int_sqr_comba(dest, src); + //err = #force_inline _private_int_sqr(dest, src); } else { err = #force_inline _private_int_sqr(dest, src); } diff --git a/core/math/big/private.odin b/core/math/big/private.odin index f49cbc51a..3d8497c72 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -354,6 +354,72 @@ _private_int_sqr_comba :: proc(dest, src: ^Int, allocator := context.allocator) return internal_clamp(dest); } +/* + Karatsuba squaring, computes `dest` = `src` * `src` using three half-size squarings. + + See comments of `_private_int_mul_karatsuba` for details. + It is essentially the same algorithm but merely tuned to perform recursive squarings. +*/ +_private_int_sqr_karatsuba :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + + x0, x1, t1, t2, x0x0, x1x1 := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; + defer internal_destroy(x0, x1, t1, t2, x0x0, x1x1); + + /* + Min # of digits, divided by two. + */ + B := src.used >> 1; + + /* + Init temps. + */ + if err = internal_grow(x0, B); err != nil { return err; } + if err = internal_grow(x1, src.used - B); err != nil { return err; } + if err = internal_grow(t1, src.used * 2); err != nil { return err; } + if err = internal_grow(t2, src.used * 2); err != nil { return err; } + if err = internal_grow(x0x0, B * 2 ); err != nil { return err; } + if err = internal_grow(x1x1, (src.used - B) * 2); err != nil { return err; } + + /* + Now shift the digits. + */ + x0.used = B; + x1.used = src.used - B; + + internal_copy_digits(x0, src, x0.used); + #force_inline mem.copy_non_overlapping(&x1.digit[0], &src.digit[B], size_of(DIGIT) * x1.used); + internal_clamp(x0); + + /* + Now calc the products x0*x0 and x1*x1. + */ + if err = internal_sqr(x0x0, x0); err != nil { return err; } + if err = internal_sqr(x1x1, x1); err != nil { return err; } + + /* + Now calc (x1+x0)^2 + */ + if err = internal_add(t1, x0, x1); err != nil { return err; } + if err = internal_sqr(t1, t1); err != nil { return err; } + + /* + Add x0y0 + */ + if err = internal_add(t2, x0x0, x1x1); err != nil { return err; } + if err = internal_sub(t1, t1, t2); err != nil { return err; } + + /* + Shift by B. + */ + if err = internal_shl_digit(t1, B); err != nil { return err; } + if err = internal_shl_digit(x1x1, B * 2); err != nil { return err; } + if err = internal_add(t1, t1, x0x0); err != nil { return err; } + if err = internal_add(dest, t1, x1x1); err != nil { return err; } + + return internal_clamp(dest); +} + /* Divide by three (based on routine from MPI and the GMP manual). */ From 5f34ff9f9f6bdbc62d6111f85167229ac8440747 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 11 Aug 2021 10:50:59 +0200 Subject: [PATCH 101/105] big: Add `_private_int_sqr_toom`. --- core/math/big/common.odin | 3 + core/math/big/example.odin | 14 ++-- core/math/big/internal.odin | 5 +- core/math/big/private.odin | 134 ++++++++++++++++++++++++++++++++---- 4 files changed, 131 insertions(+), 25 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 56b8a1083..a0cddb88e 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -40,6 +40,9 @@ SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF; It would also be cool if we collected some data across various processor families. This would let uss set reasonable defaults at runtime as this library initializes itself by using `cpuid` or the ARM equivalent. + + IMPORTANT: The 32_BIT path has largely gone untested. It needs to be tested and + debugged where necessary. */ _DEFAULT_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, 80); diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 1d9a4cb3c..467c4d517 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -82,24 +82,22 @@ demo :: proc() { // if err = factorial(a, 850); err != nil { fmt.printf("factorial err: %v\n", err); return; } - foo := "615037959146039477924633848896619112832171971562900618409305032006863881436080"; - if err = atoi(a, foo, 10); err != nil { return; } - print("a: ", a, 10, true, true, true); - fmt.println(); + foo := "54fc32611b510b653a608aaf41ca6d8e6a927520c87124d6bc9df29e29dcafbb0096428ca4a48905a3b6c9f02c56983d6d14711e7f5ce433feca8fcf382cdbd76cd627c45bc55423b8aea7f1bf638e81a3182ccd8b937467ca3916b37e67d0d4a1f3a0400360e8b02211a61071549525c4a1d4b32bfc83381e00d7d977bcf8f76e74d7a5a9532b75adfe67b6511cb377fa2828f3d9f989b3a532e2ded695796052ec3073267c11270fd393087a0ddf02f480f31149ee0c889811a8e43c25b906c9be5627bab8ba8eeba80ebdbfa0c6fe988542398d17f9df13887ddf5b109fc70033b325ec79340bd3e8d0e9217d0095fa1d5ed8750b479e2f85dd15bba5ce8ab9376d7fba183435d6d7b67e244358464efea9f5f3311efca81e36a5875e484cba3bce9536c87a038a16b85817812afde4ac8592af4f0a34ecb2e35ec160755ae17c9ad5ebee8f8a3687a06ce6a8385c161c275d1f1c1e10b64eeab5a56d76d5574c7a19a177c4018ec61f85f636697f177160452535de3a751c61f2156cd33a61e4b290b79429286397f6e58ee4936d4f1fd911b6511215fc9690e21b6f0f0c315341d5a192c40d6543c330a92c2161639f915ae50751ecdfcf6b5a489b4a874867a559e962c5464f69e50916c7621d8c7941357883e0ac5ba44dcf5cfccaa6a83ae035d66d9f00de1f115ceae4bb87133ab03d960b1c29ec801c2ac880b65d6ffd676b2c0a0f32282c47cfa977fa03ba94939cc6ec8666be46e8ce6d8a473089fd313dad75bdf99b024ad2b2bec434a2bde6102adee4c10fc53836fcade7e5eeef09a1dbeec913e535ba39b05035316e81723b93d465bd952bf1faada1564111836f022c482be52568f0c061bf0666db8c33e7a4f05c5792d0914b4ef7b654735bc1d363ca15af78ad32c0db6247b5721517393d47ce267f3a7e888642c7f595d7c8e84f680d766551bde0f9002f040a688973d3498c0ab4443b33652639018d33e0e9fe97831ba1bf7531b4d69f84e1a99cd58105756dd78ba9524ed4d236387ce9211ecab98048a728f5526c44372e44ed7a6853eeacda09d659119b0082cec1c1a6295fd9c6439f95dc60a9467e4296c0babb0138dae22ef64716fbaf0cbcd4c3047b976ae923f75fc6f4303e2ffe2792ff367d48b82b162182700bd08622a0b3304a6b0e1154ae66dc3a1607fc03026a1b81a7ec904aa98c201551baf5da48d78916ab5acb88df7bc57d7b4f1e96716dbfe56366c20cd28c2ae9d007c7c65c7fb4b6ccf108d6f23215f27913bb57ed13f9a75fb017ca5242a46ef2248da0b60ca1cb5cd80219367ed61082b3ca6c07ae581430c334de073e241993cc7458be8017c4d8c1a2b9d9d2e4e72fff048c2a70b2f57e8259a0dac6cdab9dd4d7bcf69b401d52a67a656d62730672fa3b05aa83fbc97a2362bb6c218d9c659dbfd64e20cc7f0977ba2c2ad695202fee68aca07698d3e4a9677d8ea69099d1ccb113ddb695017d6ce0da36fa3c8f3fd22ad400f53335e1e965d3f3323575927273987cb9e798b222e125c4290a4bf751b8e5329a6690b32bedf6f90786511d55da1dabe963cff83686f454b1b1fa28c46abc20c4b500d9ecad4b3f3c446fb74eebb596aea1c6f079b43f167f228cab3ceab8965c34d5b0c760221ca441b6d1d75b5c39d7443fc58933bc2cadb1c5d1b92ac70179677feef4ee63a8f8fb76eeb20e705b58f138867db80bfaf4e2edba0a7f68e56e4650b2d6e8fdf634d74f7ba8df527fd3c3a03a987e9b73d2e50aea3020251c06d5629dd32790bc36f02c638b757441a813c101f3d81eb6a4d1edd147ba9dbbc2d3c419fa11bda7f3602dda5dc2f9a19fc7306660d20ecb1da3bb87b8415e340f2a3d1e843bc19059734e2417f1783c7859f00c6801dc51acf068d57777de7d100ae77bca7614a69efe557d574abb2b79d5d90ba621aa2b6460cdce64012cad33bf374989044bba0d280c26b71a1a2d7abf7bd75009a71db9f488c4b3b58db217689bc355d68817f6153736f597e3586780175960edf4fded6513aa8c6154cee94e278e9967d0f256a4b14a2cd188e4800f146118a35633476c665edc7460658dc7877f107bcd3108d"; + if err = atoi(a, foo, 16); err != nil { return; } + //print("a: ", a, 16, true, true, true); + //fmt.println(); { SCOPED_TIMING(.sqr); - if err = sqr(b, a); err != nil { fmt.printf("sqr err: %v\n", err); return; } + if err = _private_int_sqr_toom(b, a); err != nil { fmt.printf("sqr err: %v\n", err); return; } } fmt.println(); - print("b _sqr_karatsuba: ", b); - fmt.println(); bs, err = itoa(b, 16); defer delete(bs); if bs[:50] != "1C367982F3050A8A3C62A8A7906D165438B54B287AF3F15D36" { - fmt.println("sqr failed"); + fmt.println("sqr failed"); } } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 00b1ed7bf..aac2db9ec 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -36,8 +36,6 @@ import "core:mem" import "core:intrinsics" import rnd "core:math/rand" -// import "core:fmt" - /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -630,8 +628,7 @@ internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.alloc /* Use Toom-Cook? */ - // fmt.printf("_private_int_sqr_toom: %v\n", src.used); - err = #force_inline _private_int_sqr_karatsuba(dest, src); + err = #force_inline _private_int_sqr_toom(dest, src); } else if src.used >= SQR_KARATSUBA_CUTOFF { /* Karatsuba? diff --git a/core/math/big/private.odin b/core/math/big/private.odin index 3d8497c72..9b5677bb0 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -387,37 +387,145 @@ _private_int_sqr_karatsuba :: proc(dest, src: ^Int, allocator := context.allocat x0.used = B; x1.used = src.used - B; - internal_copy_digits(x0, src, x0.used); + #force_inline internal_copy_digits(x0, src, x0.used); #force_inline mem.copy_non_overlapping(&x1.digit[0], &src.digit[B], size_of(DIGIT) * x1.used); - internal_clamp(x0); + #force_inline internal_clamp(x0); /* Now calc the products x0*x0 and x1*x1. */ - if err = internal_sqr(x0x0, x0); err != nil { return err; } - if err = internal_sqr(x1x1, x1); err != nil { return err; } + if err = internal_sqr(x0x0, x0); err != nil { return err; } + if err = internal_sqr(x1x1, x1); err != nil { return err; } /* Now calc (x1+x0)^2 */ - if err = internal_add(t1, x0, x1); err != nil { return err; } - if err = internal_sqr(t1, t1); err != nil { return err; } + if err = internal_add(t1, x0, x1); err != nil { return err; } + if err = internal_sqr(t1, t1); err != nil { return err; } /* Add x0y0 */ - if err = internal_add(t2, x0x0, x1x1); err != nil { return err; } - if err = internal_sub(t1, t1, t2); err != nil { return err; } + if err = internal_add(t2, x0x0, x1x1); err != nil { return err; } + if err = internal_sub(t1, t1, t2); err != nil { return err; } /* Shift by B. */ - if err = internal_shl_digit(t1, B); err != nil { return err; } - if err = internal_shl_digit(x1x1, B * 2); err != nil { return err; } - if err = internal_add(t1, t1, x0x0); err != nil { return err; } - if err = internal_add(dest, t1, x1x1); err != nil { return err; } + if err = internal_shl_digit(t1, B); err != nil { return err; } + if err = internal_shl_digit(x1x1, B * 2); err != nil { return err; } + if err = internal_add(t1, t1, x0x0); err != nil { return err; } + if err = internal_add(dest, t1, x1x1); err != nil { return err; } - return internal_clamp(dest); + return #force_inline internal_clamp(dest); +} + +/* + Squaring using Toom-Cook 3-way algorithm. + + Setup and interpolation from algorithm SQR_3 in Chung, Jaewook, and M. Anwar Hasan. "Asymmetric squaring formulae." + 18th IEEE Symposium on Computer Arithmetic (ARITH'07). IEEE, 2007. +*/ +_private_int_sqr_toom :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator; + + S0, a0, a1, a2 := &Int{}, &Int{}, &Int{}, &Int{}; + defer destroy(S0, a0, a1, a2); + + /* + Init temps. + */ + if err = internal_zero(S0); err != nil { return err; } + + /* + B + */ + B := src.used / 3; + + /* + a = a2 * x^2 + a1 * x + a0; + */ + if err = internal_grow(a0, B); err != nil { return err; } + if err = internal_grow(a1, B); err != nil { return err; } + if err = internal_grow(a2, src.used - (2 * B)); err != nil { return err; } + + a0.used = B; + a1.used = B; + a2.used = src.used - 2 * B; + + #force_inline mem.copy_non_overlapping(&a0.digit[0], &src.digit[ 0], size_of(DIGIT) * a0.used); + #force_inline mem.copy_non_overlapping(&a1.digit[0], &src.digit[ B], size_of(DIGIT) * a1.used); + #force_inline mem.copy_non_overlapping(&a2.digit[0], &src.digit[2 * B], size_of(DIGIT) * a2.used); + + internal_clamp(a0); + internal_clamp(a1); + internal_clamp(a2); + + /** S0 = a0^2; */ + if err = internal_sqr(S0, a0); err != nil { return err; } + + /** \\S1 = (a2 + a1 + a0)^2 */ + /** \\S2 = (a2 - a1 + a0)^2 */ + /** \\S1 = a0 + a2; */ + /** a0 = a0 + a2; */ + if err = internal_add(a0, a0, a2); err != nil { return err; } + /** \\S2 = S1 - a1; */ + /** b = a0 - a1; */ + if err = internal_sub(dest, a0, a1); err != nil { return err; } + /** \\S1 = S1 + a1; */ + /** a0 = a0 + a1; */ + if err = internal_add(a0, a0, a1); err != nil { return err; } + /** \\S1 = S1^2; */ + /** a0 = a0^2; */ + if err = internal_sqr(a0, a0); err != nil { return err; } + /** \\S2 = S2^2; */ + /** b = b^2; */ + if err = internal_sqr(dest, dest); err != nil { return err; } + /** \\ S3 = 2 * a1 * a2 */ + /** \\S3 = a1 * a2; */ + /** a1 = a1 * a2; */ + if err = internal_mul(a1, a1, a2); err != nil { return err; } + /** \\S3 = S3 << 1; */ + /** a1 = a1 << 1; */ + if err = internal_shl(a1, a1, 1); err != nil { return err; } + /** \\S4 = a2^2; */ + /** a2 = a2^2; */ + if err = internal_sqr(a2, a2); err != nil { return err; } + /** \\ tmp = (S1 + S2)/2 */ + /** \\tmp = S1 + S2; */ + /** b = a0 + b; */ + if err = internal_add(dest, a0, dest); err != nil { return err; } + /** \\tmp = tmp >> 1; */ + /** b = b >> 1; */ + if err = internal_shr(dest, dest, 1); err != nil { return err; } + /** \\ S1 = S1 - tmp - S3 */ + /** \\S1 = S1 - tmp; */ + /** a0 = a0 - b; */ + if err = internal_sub(a0, a0, dest); err != nil { return err; } + /** \\S1 = S1 - S3; */ + /** a0 = a0 - a1; */ + if err = internal_sub(a0, a0, a1); err != nil { return err; } + /** \\S2 = tmp - S4 -S0 */ + /** \\S2 = tmp - S4; */ + /** b = b - a2; */ + if err = internal_sub(dest, dest, a2); err != nil { return err; } + /** \\S2 = S2 - S0; */ + /** b = b - S0; */ + if err = internal_sub(dest, dest, S0); err != nil { return err; } + /** \\P = S4*x^4 + S3*x^3 + S2*x^2 + S1*x + S0; */ + /** P = a2*x^4 + a1*x^3 + b*x^2 + a0*x + S0; */ + if err = internal_shl_digit( a2, 4 * B); err != nil { return err; } + if err = internal_shl_digit( a1, 3 * B); err != nil { return err; } + if err = internal_shl_digit(dest, 2 * B); err != nil { return err; } + if err = internal_shl_digit( a0, 1 * B); err != nil { return err; } + + if err = internal_add(a2, a2, a1); err != nil { return err; } + if err = internal_add(dest, dest, a2); err != nil { return err; } + if err = internal_add(dest, dest, a0); err != nil { return err; } + if err = internal_add(dest, dest, S0); err != nil { return err; } + /** a^2 - P */ + + return #force_inline internal_clamp(dest); } /* From 851780b8f449847953c49e59d7ce401f2d53c779 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 11 Aug 2021 12:31:55 +0200 Subject: [PATCH 102/105] big: Add arguments and usage to test.py. --- core/math/big/api.odin | 4 +- core/math/big/common.odin | 2 +- core/math/big/example.odin | 2 +- core/math/big/helpers.odin | 2 +- core/math/big/internal.odin | 2 +- core/math/big/logical.odin | 2 +- core/math/big/prime.odin | 4 +- core/math/big/private.odin | 2 +- core/math/big/public.odin | 2 +- core/math/big/radix.odin | 2 +- core/math/big/test.odin | 2 +- core/math/big/test.py | 82 ++++++++++++++++++++++++++----------- core/math/big/tune.odin | 2 +- 13 files changed, 72 insertions(+), 38 deletions(-) diff --git a/core/math/big/api.odin b/core/math/big/api.odin index 047e91335..e1b687c14 100644 --- a/core/math/big/api.odin +++ b/core/math/big/api.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . @@ -14,7 +14,7 @@ package big */ === === === === === === === === === === === === === === === === === === === === === === === === Basic arithmetic. - See `basic.odin`. + See `public.odin`. === === === === === === === === === === === === === === === === === === === === === === === === */ diff --git a/core/math/big/common.odin b/core/math/big/common.odin index a0cddb88e..bc9f189cf 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 467c4d517..5b9fbb481 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -1,5 +1,5 @@ //+ignore -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index d0262ba19..14c88be5f 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index aac2db9ec..fb4cd6572 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1,5 +1,5 @@ //+ignore -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index f72ecb18d..1eb26332a 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index 25d5ddda3..388ac3f98 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . @@ -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`, `sub`, `mul`, `div`, ... + This file contains prime finding operations. */ /* diff --git a/core/math/big/private.odin b/core/math/big/private.odin index 9b5677bb0..0a5cd6163 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/public.odin b/core/math/big/public.odin index 3ab5a8924..b7a00fbc0 100644 --- a/core/math/big/public.odin +++ b/core/math/big/public.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index e8468851b..01c0489d8 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -1,4 +1,4 @@ -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/test.odin b/core/math/big/test.odin index 044a3329e..2846dac6d 100644 --- a/core/math/big/test.odin +++ b/core/math/big/test.odin @@ -1,5 +1,5 @@ //+ignore -package big +package math_big /* Copyright 2021 Jeroen van Rijn . diff --git a/core/math/big/test.py b/core/math/big/test.py index 8da890e55..b60698881 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -6,32 +6,66 @@ import platform import time import gc from enum import Enum +import argparse -# -# Normally, we report the number of passes and fails. -# With EXIT_ON_FAIL set, we exit at the first fail. -# -EXIT_ON_FAIL = True -EXIT_ON_FAIL = False +parser = argparse.ArgumentParser( + description = "Odin core:math/big test suite", + epilog = "By default we run regression and random tests with preset parameters.", + formatter_class = argparse.ArgumentDefaultsHelpFormatter, +) # -# We skip randomized tests altogether if NO_RANDOM_TESTS is set. +# Normally, we report the number of passes and fails. With this option set, we exit at first fail. +# +parser.add_argument( + "-exit-on-fail", + help = "Exit when a test fails", + action = "store_true", +) +# +# We skip randomized tests altogether if this is set. # -NO_RANDOM_TESTS = True -NO_RANDOM_TESTS = False -# -# If TIMED_TESTS == False and FAST_TESTS == True, we cut down the number of iterations. -# See below. -# -FAST_TESTS = True +no_random = parser.add_mutually_exclusive_group() +no_random.add_argument( + "-no-random", + help = "No random tests", + action = "store_true", +) +# +# Normally we run a given number of cycles on each test. +# Timed tests budget 1 second per 20_000 bits instead. # # For timed tests we budget a second per `n` bits and iterate until we hit that time. -# Otherwise, we specify the number of iterations per bit depth in BITS_AND_ITERATIONS. # -TIMED_TESTS = False -TIMED_BITS_PER_SECOND = 20_000 + +timed_or_fast = no_random.add_mutually_exclusive_group() + +timed_or_fast.add_argument( + "-timed", + type = bool, + default = False, + help = "Timed tests instead of a preset number of iterations.", +) +parser.add_argument( + "-timed-bits", + type = int, + metavar = "BITS", + default = 20_000, + help = "Timed tests. Every `BITS` worth of input is given a second of running time.", +) +# +# For normal tests (non-timed), `-fast-tests` cuts down on the number of iterations. +# +timed_or_fast.add_argument( + "-fast-tests", + help = "Cut down on the number of iterations of each test", + default = True, + action = "store_true", +) + +args = parser.parse_args() # # How many iterations of each random test do we want to run? @@ -43,12 +77,12 @@ BITS_AND_ITERATIONS = [ (12_000, 10), ] -if FAST_TESTS: +if args.fast_tests: for k in range(len(BITS_AND_ITERATIONS)): b, i = BITS_AND_ITERATIONS[k] BITS_AND_ITERATIONS[k] = (b, i // 10 if i >= 100 else 5) -if NO_RANDOM_TESTS: +if args.no_random: BITS_AND_ITERATIONS = [] # @@ -70,7 +104,7 @@ UNTIL_TIME = 0 UNTIL_ITERS = 0 def we_iterate(): - if TIMED_TESTS: + if args.timed: return TOTAL_TIME < UNTIL_TIME else: global UNTIL_ITERS @@ -178,7 +212,7 @@ def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expecte print(error, flush=True) passed = False - if EXIT_ON_FAIL and not passed: exit(res.err) + if args.exit_on_fail and not passed: exit(res.err) return passed @@ -488,7 +522,7 @@ TESTS = { ], } -if not FAST_TESTS: +if not args.fast_tests: TESTS[test_factorial].append( # This one on its own takes around 800ms, so we exclude it for FAST_TESTS [ 100_000 ], @@ -517,7 +551,7 @@ for test_proc in TESTS: res = test_proc(*t) if __name__ == '__main__': - print("---- math/big tests ----") + print("\n---- math/big tests ----") print() for test_proc in TESTS: @@ -566,7 +600,7 @@ if __name__ == '__main__': if test_proc == test_root_n and BITS == 1_200: UNTIL_ITERS /= 10 - UNTIL_TIME = TOTAL_TIME + BITS / TIMED_BITS_PER_SECOND + UNTIL_TIME = TOTAL_TIME + BITS / args.timed_bits # We run each test for a second per 20k bits index = 0 diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin index 59f533d44..a51c04a78 100644 --- a/core/math/big/tune.odin +++ b/core/math/big/tune.odin @@ -1,5 +1,5 @@ //+ignore -package big +package math_big /* Copyright 2021 Jeroen van Rijn . From 12f9b6db63fbbc0fe57ce93145d8c55b8f1f0db8 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 11 Aug 2021 15:48:20 +0200 Subject: [PATCH 103/105] big: Add `int_to_bytes_{big, little}` + Python compatible variants. --- core/math/big/build.bat | 12 +-- core/math/big/common.odin | 4 +- core/math/big/example.odin | 63 ++++++++++++---- core/math/big/helpers.odin | 144 +++++++++++++++++++++++++++++++++++- core/math/big/internal.odin | 4 +- core/math/big/test.py | 1 - 6 files changed, 202 insertions(+), 26 deletions(-) diff --git a/core/math/big/build.bat b/core/math/big/build.bat index e98ac7015..b1ff7303a 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -1,8 +1,8 @@ @echo off -:odin run . -vet +odin run . -vet : -o:size -:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check && python test.py -:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check && python test.py -:odin build . -build-mode:shared -show-timings -o:size && python test.py -odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check && python test.py -:odin build . -build-mode:shared -show-timings -o:speed && python test.py \ No newline at end of file +:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check && python test.py -fast-tests +:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check && python test.py -fast-tests +:odin build . -build-mode:shared -show-timings -o:size && python test.py -fast-tests +:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check && python test.py -fast-tests +:odin build . -build-mode:shared -show-timings -o:speed && python test.py -fast-tests \ No newline at end of file diff --git a/core/math/big/common.odin b/core/math/big/common.odin index bc9f189cf..5e2fe4d8c 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -114,7 +114,7 @@ Flags :: bit_set[Flag; u8]; Error :: enum int { Okay = 0, Out_Of_Memory = 1, - // Invalid_Pointer = 2, + Invalid_Pointer = 2, Invalid_Argument = 3, Assignment_To_Immutable = 4, @@ -130,7 +130,7 @@ Error :: enum int { Error_String :: #partial [Error]string{ .Out_Of_Memory = "Out of memory", - // .Invalid_Pointer = "Invalid pointer", + .Invalid_Pointer = "Invalid pointer", .Invalid_Argument = "Invalid argument", .Assignment_To_Immutable = "Assignment to immutable", diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 5b9fbb481..552ba69f6 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -78,26 +78,61 @@ demo :: proc() { defer destroy(a, b, c, d, e, f); err: Error; - bs: string; - // if err = factorial(a, 850); err != nil { fmt.printf("factorial err: %v\n", err); return; } + foo := "2291194942392555914538479778530519876003906024854260006581638127590953"; + if err = atoi(a, foo, 10); err != nil { return; } + // print("a: ", a, 10, true, true, true); - foo := "54fc32611b510b653a608aaf41ca6d8e6a927520c87124d6bc9df29e29dcafbb0096428ca4a48905a3b6c9f02c56983d6d14711e7f5ce433feca8fcf382cdbd76cd627c45bc55423b8aea7f1bf638e81a3182ccd8b937467ca3916b37e67d0d4a1f3a0400360e8b02211a61071549525c4a1d4b32bfc83381e00d7d977bcf8f76e74d7a5a9532b75adfe67b6511cb377fa2828f3d9f989b3a532e2ded695796052ec3073267c11270fd393087a0ddf02f480f31149ee0c889811a8e43c25b906c9be5627bab8ba8eeba80ebdbfa0c6fe988542398d17f9df13887ddf5b109fc70033b325ec79340bd3e8d0e9217d0095fa1d5ed8750b479e2f85dd15bba5ce8ab9376d7fba183435d6d7b67e244358464efea9f5f3311efca81e36a5875e484cba3bce9536c87a038a16b85817812afde4ac8592af4f0a34ecb2e35ec160755ae17c9ad5ebee8f8a3687a06ce6a8385c161c275d1f1c1e10b64eeab5a56d76d5574c7a19a177c4018ec61f85f636697f177160452535de3a751c61f2156cd33a61e4b290b79429286397f6e58ee4936d4f1fd911b6511215fc9690e21b6f0f0c315341d5a192c40d6543c330a92c2161639f915ae50751ecdfcf6b5a489b4a874867a559e962c5464f69e50916c7621d8c7941357883e0ac5ba44dcf5cfccaa6a83ae035d66d9f00de1f115ceae4bb87133ab03d960b1c29ec801c2ac880b65d6ffd676b2c0a0f32282c47cfa977fa03ba94939cc6ec8666be46e8ce6d8a473089fd313dad75bdf99b024ad2b2bec434a2bde6102adee4c10fc53836fcade7e5eeef09a1dbeec913e535ba39b05035316e81723b93d465bd952bf1faada1564111836f022c482be52568f0c061bf0666db8c33e7a4f05c5792d0914b4ef7b654735bc1d363ca15af78ad32c0db6247b5721517393d47ce267f3a7e888642c7f595d7c8e84f680d766551bde0f9002f040a688973d3498c0ab4443b33652639018d33e0e9fe97831ba1bf7531b4d69f84e1a99cd58105756dd78ba9524ed4d236387ce9211ecab98048a728f5526c44372e44ed7a6853eeacda09d659119b0082cec1c1a6295fd9c6439f95dc60a9467e4296c0babb0138dae22ef64716fbaf0cbcd4c3047b976ae923f75fc6f4303e2ffe2792ff367d48b82b162182700bd08622a0b3304a6b0e1154ae66dc3a1607fc03026a1b81a7ec904aa98c201551baf5da48d78916ab5acb88df7bc57d7b4f1e96716dbfe56366c20cd28c2ae9d007c7c65c7fb4b6ccf108d6f23215f27913bb57ed13f9a75fb017ca5242a46ef2248da0b60ca1cb5cd80219367ed61082b3ca6c07ae581430c334de073e241993cc7458be8017c4d8c1a2b9d9d2e4e72fff048c2a70b2f57e8259a0dac6cdab9dd4d7bcf69b401d52a67a656d62730672fa3b05aa83fbc97a2362bb6c218d9c659dbfd64e20cc7f0977ba2c2ad695202fee68aca07698d3e4a9677d8ea69099d1ccb113ddb695017d6ce0da36fa3c8f3fd22ad400f53335e1e965d3f3323575927273987cb9e798b222e125c4290a4bf751b8e5329a6690b32bedf6f90786511d55da1dabe963cff83686f454b1b1fa28c46abc20c4b500d9ecad4b3f3c446fb74eebb596aea1c6f079b43f167f228cab3ceab8965c34d5b0c760221ca441b6d1d75b5c39d7443fc58933bc2cadb1c5d1b92ac70179677feef4ee63a8f8fb76eeb20e705b58f138867db80bfaf4e2edba0a7f68e56e4650b2d6e8fdf634d74f7ba8df527fd3c3a03a987e9b73d2e50aea3020251c06d5629dd32790bc36f02c638b757441a813c101f3d81eb6a4d1edd147ba9dbbc2d3c419fa11bda7f3602dda5dc2f9a19fc7306660d20ecb1da3bb87b8415e340f2a3d1e843bc19059734e2417f1783c7859f00c6801dc51acf068d57777de7d100ae77bca7614a69efe557d574abb2b79d5d90ba621aa2b6460cdce64012cad33bf374989044bba0d280c26b71a1a2d7abf7bd75009a71db9f488c4b3b58db217689bc355d68817f6153736f597e3586780175960edf4fded6513aa8c6154cee94e278e9967d0f256a4b14a2cd188e4800f146118a35633476c665edc7460658dc7877f107bcd3108d"; - if err = atoi(a, foo, 16); err != nil { return; } - //print("a: ", a, 16, true, true, true); - //fmt.println(); + byte_length, _ := int_to_bytes_size(a); - { - SCOPED_TIMING(.sqr); - if err = _private_int_sqr_toom(b, a); err != nil { fmt.printf("sqr err: %v\n", err); return; } + fmt.printf("byte_length(a): %v\n", byte_length); + + buf := make([]u8, byte_length); + defer delete(buf); + + err = int_to_bytes_big(a, buf); + + python_big := []u8{ + 84, 252, 50, 97, 27, 81, 11, 101, 58, 96, 138, 175, 65, 202, 109, + 142, 106, 146, 117, 32, 200, 113, 36, 214, 188, 157, 242, 158, 41, + }; + + if mem.compare(python_big, buf) == 0 { + fmt.printf("int_to_bytes_big: pass\n"); + } else { + fmt.printf("int_to_bytes_big: fail | %v\n", buf); } - fmt.println(); + python_little := []u8{ + 41, 158, 242, 157, 188, 214, 36, 113, 200, 32, 117, 146, 106, 142, 109, + 202, 65, 175, 138, 96, 58, 101, 11, 81, 27, 97, 50, 252, 84, + }; - bs, err = itoa(b, 16); - defer delete(bs); + err = int_to_bytes_little(a, buf); + if mem.compare(python_little, buf) == 0 { + fmt.printf("int_to_bytes_little: pass\n"); + } else { + fmt.printf("int_to_bytes_little: fail | %v\n", buf); + } - if bs[:50] != "1C367982F3050A8A3C62A8A7906D165438B54B287AF3F15D36" { - fmt.println("sqr failed"); + _ = neg(b, a); + + python_little_neg := []u8{ + 215, 97, 13, 98, 67, 41, 219, 142, 55, 223, 138, 109, 149, 113, 146, + 53, 190, 80, 117, 159, 197, 154, 244, 174, 228, 158, 205, 3, 171, + }; + + byte_length, _ = int_to_bytes_size_python(b, true); + + fmt.printf("byte_length(a): %v\n", byte_length); + + buf2 := make([]u8, byte_length); + defer delete(buf2); + + err = int_to_bytes_little_python(b, buf, true); + if mem.compare(python_little_neg, buf) == 0 { + fmt.printf("int_to_bytes_little: pass\n"); + } else { + fmt.printf("int_to_bytes_little: %v | %v\n", err, buf); } } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 14c88be5f..058836df9 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -45,7 +45,7 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator : return #force_inline internal_int_set_from_integer(dest, src, minimize); } -set :: proc { int_set_from_integer, int_copy }; +set :: proc { int_set_from_integer, int_copy, int_atoi, }; /* Copy one `Int` to another. @@ -464,6 +464,148 @@ clamp :: proc(a: ^Int, allocator := context.allocator) -> (err: Error) { return nil; } + +/* + Size binary representation +*/ +int_to_bytes_size :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) { + assert_if_nil(a); + if err = #force_inline internal_clear_if_uninitialized(a, allocator); err != nil { return {}, err; } + + size_in_bits := internal_count_bits(a); + + size_in_bytes = (size_in_bits / 8); + size_in_bytes += 0 if size_in_bits % 8 == 0 else 1; + size_in_bytes += 1 if signed else 0; + return; +} + +/* + Size binary representation, Python `._to_bytes(num_bytes, "endianness", signed=Bool)` compatible. +*/ +int_to_bytes_size_python :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) { + assert_if_nil(a); + size_in_bytes, err = int_to_bytes_size(a, signed, allocator); + + /* + Python uses a complement representation of negative numbers and doesn't add a prefix byte. + */ + if signed { + size_in_bytes -= 1; + } + return; +} + +/* + Return Little Endian binary representation of `a`, either signed or unsigned. + If `a` is negative and we ask for the default unsigned representation, we return abs(a). +*/ +int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + size_in_bytes: int; + + if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; } + if size_in_bytes > len(buf) { return .Buffer_Overflow; } + + size_in_bits := internal_count_bits(a); + i := 0; + if signed { + i += 1; + buf[0] = 1 if a.sign == .Negative else 0; + } + for offset := 0; offset < size_in_bits; offset += 8 { + bits, _ := internal_int_bitfield_extract(a, offset, 8); + buf[i] = u8(bits & 255); i += 1; + } + return; +} + +/* + Return Big Endian binary representation of `a`, either signed or unsigned. + If `a` is negative and we ask for the default unsigned representation, we return abs(a). +*/ +int_to_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + size_in_bytes: int; + + if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; } + l := len(buf); + if size_in_bytes > l { return .Buffer_Overflow; } + + size_in_bits := internal_count_bits(a); + i := l - 1; + + if signed { + buf[0] = 1 if a.sign == .Negative else 0; + } + for offset := 0; offset < size_in_bits; offset += 8 { + bits, _ := internal_int_bitfield_extract(a, offset, 8); + buf[i] = u8(bits & 255); i -= 1; + } + return; +} + +/* + Return Python 3.x compatible Little Endian binary representation of `a`, either signed or unsigned. + If `a` is negative when asking for an unsigned number, we return an error like Python does. +*/ +int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + size_in_bytes: int; + + if a.sign == .Zero_or_Positive { + return int_to_bytes_little(a, buf, signed, allocator); + } + if a.sign == .Negative && !signed { return .Invalid_Argument; } + + l := len(buf); + if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; } + if size_in_bytes > l { return .Buffer_Overflow; } + + t := &Int{}; + defer destroy(t); + if err = complement(t, a, allocator); err != nil { return err; } + + size_in_bits := internal_count_bits(t); + i := 0; + for offset := 0; offset < size_in_bits; offset += 8 { + bits, _ := internal_int_bitfield_extract(t, offset, 8); + buf[i] = 255 - u8(bits & 255); i += 1; + } + return; +} + +/* + Return Python 3.x compatible Big Endian binary representation of `a`, either signed or unsigned. + If `a` is negative when asking for an unsigned number, we return an error like Python does. +*/ +int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + size_in_bytes: int; + + if a.sign == .Zero_or_Positive { + return int_to_bytes_big(a, buf, signed, allocator); + } + if a.sign == .Negative && !signed { return .Invalid_Argument; } + + l := len(buf); + if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; } + if size_in_bytes > l { return .Buffer_Overflow; } + + t := &Int{}; + defer destroy(t); + if err = complement(t, a, allocator); err != nil { return err; } + + size_in_bits := internal_count_bits(t); + i := l - 1; + + for offset := 0; offset < size_in_bits; offset += 8 { + bits, _ := internal_int_bitfield_extract(a, offset, 8); + buf[i] = 255 - u8(bits & 255); i -= 1; + } + return; +} + /* Initialize constants. */ diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index fb4cd6572..695dca04d 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -1719,9 +1719,9 @@ internal_int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (er /* If `dest == src`, just fix `dest`'s sign. */ - sign := Sign.Zero_or_Positive; + sign := Sign.Negative; if #force_inline internal_is_zero(src) || #force_inline internal_is_negative(src) { - sign = .Negative; + sign = .Zero_or_Positive; } if dest == src { dest.sign = sign; diff --git a/core/math/big/test.py b/core/math/big/test.py index b60698881..f9398389c 100644 --- a/core/math/big/test.py +++ b/core/math/big/test.py @@ -61,7 +61,6 @@ parser.add_argument( timed_or_fast.add_argument( "-fast-tests", help = "Cut down on the number of iterations of each test", - default = True, action = "store_true", ) From ee24f2dd37540a65fa68b86d90d3bd49a3fdb655 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 11 Aug 2021 18:48:31 +0200 Subject: [PATCH 104/105] big: Improve `int_to_bytes_*`. --- core/math/big/example.odin | 160 ++++++++++++++++++++++++------------- core/math/big/helpers.odin | 106 ++++++++++++++---------- 2 files changed, 170 insertions(+), 96 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index 552ba69f6..e8793ba23 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -73,67 +73,115 @@ print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline } } +int_to_byte :: proc(v: ^Int) { + err: Error; + size: int; + print("v: ", v); + fmt.println(); + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b1 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big(v, b1); + fmt.printf("big: %v | err: %v\n", b1, err); + + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b2 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big_python(v, b2); + fmt.printf("big python: %v | err: %v\n", b2, err); + + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b3 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big(v, b3, true); + fmt.printf("big signed: %v | err: %v\n", b3, err); + + t := &Int{}; + int_from_bytes_big(t, b3, true); + defer destroy(t); + print("t: ", t); + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b4 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_big_python(v, b4, true); + fmt.printf("big signed python: %v | err: %v\n", b4, err); +} + +int_to_byte_little :: proc(v: ^Int) { + err: Error; + size: int; + print("v: ", v); + fmt.println(); + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b1 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little(v, b1); + fmt.printf("little: %v | err: %v\n", b1, err); + + + if size, err = int_to_bytes_size(v); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b2 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little_python(v, b2); + fmt.printf("little python: %v | err: %v\n", b2, err); + + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b3 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little(v, b3, true); + fmt.printf("little signed: %v | err: %v\n", b3, err); + + // t := &Int{}; + // int_from_bytes_little(t, b3, true); + // defer destroy(t); + // print("t: ", t); + + if size, err = int_to_bytes_size(v, true); err != nil { + fmt.printf("int_to_bytes_size returned: %v\n", err); + return; + } + b4 := make([]u8, size, context.temp_allocator); + err = int_to_bytes_little_python(v, b4, true); + fmt.printf("little signed python: %v | err: %v\n", b4, err); +} + demo :: proc() { a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(a, b, c, d, e, f); - err: Error; + set(a, 64336); + fmt.println("--- --- --- ---"); + int_to_byte(a); + fmt.println("--- --- --- ---"); + int_to_byte_little(a); + fmt.println("--- --- --- ---"); - foo := "2291194942392555914538479778530519876003906024854260006581638127590953"; - if err = atoi(a, foo, 10); err != nil { return; } - // print("a: ", a, 10, true, true, true); - - byte_length, _ := int_to_bytes_size(a); - - fmt.printf("byte_length(a): %v\n", byte_length); - - buf := make([]u8, byte_length); - defer delete(buf); - - err = int_to_bytes_big(a, buf); - - python_big := []u8{ - 84, 252, 50, 97, 27, 81, 11, 101, 58, 96, 138, 175, 65, 202, 109, - 142, 106, 146, 117, 32, 200, 113, 36, 214, 188, 157, 242, 158, 41, - }; - - if mem.compare(python_big, buf) == 0 { - fmt.printf("int_to_bytes_big: pass\n"); - } else { - fmt.printf("int_to_bytes_big: fail | %v\n", buf); - } - python_little := []u8{ - 41, 158, 242, 157, 188, 214, 36, 113, 200, 32, 117, 146, 106, 142, 109, - 202, 65, 175, 138, 96, 58, 101, 11, 81, 27, 97, 50, 252, 84, - }; - - err = int_to_bytes_little(a, buf); - if mem.compare(python_little, buf) == 0 { - fmt.printf("int_to_bytes_little: pass\n"); - } else { - fmt.printf("int_to_bytes_little: fail | %v\n", buf); - } - - _ = neg(b, a); - - python_little_neg := []u8{ - 215, 97, 13, 98, 67, 41, 219, 142, 55, 223, 138, 109, 149, 113, 146, - 53, 190, 80, 117, 159, 197, 154, 244, 174, 228, 158, 205, 3, 171, - }; - - byte_length, _ = int_to_bytes_size_python(b, true); - - fmt.printf("byte_length(a): %v\n", byte_length); - - buf2 := make([]u8, byte_length); - defer delete(buf2); - - err = int_to_bytes_little_python(b, buf, true); - if mem.compare(python_little_neg, buf) == 0 { - fmt.printf("int_to_bytes_little: pass\n"); - } else { - fmt.printf("int_to_bytes_little: %v | %v\n", err, buf); - } + set(b, -64336); + fmt.println("--- --- --- ---"); + int_to_byte(b); + fmt.println("--- --- --- ---"); + int_to_byte_little(b); + fmt.println("--- --- --- ---"); } main :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 058836df9..cdd1e3bcb 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -480,22 +480,6 @@ int_to_bytes_size :: proc(a: ^Int, signed := false, allocator := context.allocat return; } -/* - Size binary representation, Python `._to_bytes(num_bytes, "endianness", signed=Bool)` compatible. -*/ -int_to_bytes_size_python :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) { - assert_if_nil(a); - size_in_bytes, err = int_to_bytes_size(a, signed, allocator); - - /* - Python uses a complement representation of negative numbers and doesn't add a prefix byte. - */ - if signed { - size_in_bytes -= 1; - } - return; -} - /* Return Little Endian binary representation of `a`, either signed or unsigned. If `a` is negative and we ask for the default unsigned representation, we return abs(a). @@ -505,13 +489,13 @@ int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := co size_in_bytes: int; if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; } - if size_in_bytes > len(buf) { return .Buffer_Overflow; } + l := len(buf); + if size_in_bytes > l { return .Buffer_Overflow; } size_in_bits := internal_count_bits(a); i := 0; if signed { - i += 1; - buf[0] = 1 if a.sign == .Negative else 0; + buf[l - 1] = 1 if a.sign == .Negative else 0; } for offset := 0; offset < size_in_bits; offset += 8 { bits, _ := internal_int_bitfield_extract(a, offset, 8); @@ -553,24 +537,31 @@ int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocato assert_if_nil(a); size_in_bytes: int; - if a.sign == .Zero_or_Positive { - return int_to_bytes_little(a, buf, signed, allocator); - } - if a.sign == .Negative && !signed { return .Invalid_Argument; } + if !signed && a.sign == .Negative { return .Invalid_Argument; } l := len(buf); - if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; } + if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; } if size_in_bytes > l { return .Buffer_Overflow; } - t := &Int{}; - defer destroy(t); - if err = complement(t, a, allocator); err != nil { return err; } + if a.sign == .Negative { + t := &Int{}; + defer destroy(t); + if err = internal_complement(t, a, allocator); err != nil { return err; } - size_in_bits := internal_count_bits(t); - i := 0; - for offset := 0; offset < size_in_bits; offset += 8 { - bits, _ := internal_int_bitfield_extract(t, offset, 8); - buf[i] = 255 - u8(bits & 255); i += 1; + size_in_bits := internal_count_bits(t); + i := 0; + for offset := 0; offset < size_in_bits; offset += 8 { + bits, _ := internal_int_bitfield_extract(t, offset, 8); + buf[i] = 255 - u8(bits & 255); i += 1; + } + buf[l-1] = 255; + } else { + size_in_bits := internal_count_bits(a); + i := 0; + for offset := 0; offset < size_in_bits; offset += 8 { + bits, _ := internal_int_bitfield_extract(a, offset, 8); + buf[i] = u8(bits & 255); i += 1; + } } return; } @@ -583,29 +574,64 @@ int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator : assert_if_nil(a); size_in_bytes: int; - if a.sign == .Zero_or_Positive { - return int_to_bytes_big(a, buf, signed, allocator); - } - if a.sign == .Negative && !signed { return .Invalid_Argument; } + if !signed && a.sign == .Negative { return .Invalid_Argument; } + if a.sign == .Zero_or_Positive { return int_to_bytes_big(a, buf, signed, allocator); } l := len(buf); - if size_in_bytes, err = int_to_bytes_size_python(a, signed, allocator); err != nil { return err; } + if size_in_bytes, err = int_to_bytes_size(a, signed, allocator); err != nil { return err; } if size_in_bytes > l { return .Buffer_Overflow; } t := &Int{}; defer destroy(t); - if err = complement(t, a, allocator); err != nil { return err; } + + if err = internal_complement(t, a, allocator); err != nil { return err; } size_in_bits := internal_count_bits(t); i := l - 1; - for offset := 0; offset < size_in_bits; offset += 8 { - bits, _ := internal_int_bitfield_extract(a, offset, 8); + bits, _ := internal_int_bitfield_extract(t, offset, 8); buf[i] = 255 - u8(bits & 255); i -= 1; } + buf[0] = 255; + return; } +/* + Read `Int` from a Big Endian binary representation. + Sign is detected from the first byte if `signed` is true. +*/ +int_from_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + buf := buf; + l := len(buf); + if l == 0 { return .Invalid_Argument; } + + sign: Sign; + size_in_bits := l * 8; + if signed { + /* + First byte denotes the sign. + */ + size_in_bits -= 8; + } + size_in_digits := size_in_bits / _DIGIT_BITS; + size_in_digits += 0 if size_in_bits % 8 == 0 else 1; + if err = internal_grow(a, size_in_digits); err != nil { return err; } + + if signed { + sign = .Zero_or_Positive if buf[0] == 0 else .Negative; + buf = buf[1:]; + } + + for v in buf { + if err = internal_shl(a, a, 8); err != nil { return err; } + a.digit[0] |= DIGIT(v); + } + a.sign = sign; + return internal_clamp(a); +} + /* Initialize constants. */ From eb22a49b020acd4b3538b651d1ce86563e5f95d5 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 11 Aug 2021 20:54:15 +0200 Subject: [PATCH 105/105] big: Add `int_from_bytes_*`. --- core/math/big/example.odin | 51 ++++++++++++-- core/math/big/helpers.odin | 141 ++++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 10 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index e8793ba23..ae900d0c9 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -79,14 +79,22 @@ int_to_byte :: proc(v: ^Int) { print("v: ", v); fmt.println(); + t := &Int{}; + defer destroy(t); + if size, err = int_to_bytes_size(v); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); return; } b1 := make([]u8, size, context.temp_allocator); err = int_to_bytes_big(v, b1); + int_from_bytes_big(t, b1); fmt.printf("big: %v | err: %v\n", b1, err); + int_from_bytes_big(t, b1); + if internal_cmp_mag(t, v) != 0 { + print("\tError parsing t: ", t); + } if size, err = int_to_bytes_size(v); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); @@ -96,6 +104,12 @@ int_to_byte :: proc(v: ^Int) { err = int_to_bytes_big_python(v, b2); fmt.printf("big python: %v | err: %v\n", b2, err); + if err == nil { + int_from_bytes_big_python(t, b2); + if internal_cmp_mag(t, v) != 0 { + print("\tError parsing t: ", t); + } + } if size, err = int_to_bytes_size(v, true); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); @@ -105,10 +119,10 @@ int_to_byte :: proc(v: ^Int) { err = int_to_bytes_big(v, b3, true); fmt.printf("big signed: %v | err: %v\n", b3, err); - t := &Int{}; int_from_bytes_big(t, b3, true); - defer destroy(t); - print("t: ", t); + if internal_cmp(t, v) != 0 { + print("\tError parsing t: ", t); + } if size, err = int_to_bytes_size(v, true); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); @@ -117,6 +131,11 @@ int_to_byte :: proc(v: ^Int) { b4 := make([]u8, size, context.temp_allocator); err = int_to_bytes_big_python(v, b4, true); fmt.printf("big signed python: %v | err: %v\n", b4, err); + + int_from_bytes_big_python(t, b4, true); + if internal_cmp(t, v) != 0 { + print("\tError parsing t: ", t); + } } int_to_byte_little :: proc(v: ^Int) { @@ -125,6 +144,9 @@ int_to_byte_little :: proc(v: ^Int) { print("v: ", v); fmt.println(); + t := &Int{}; + defer destroy(t); + if size, err = int_to_bytes_size(v); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); return; @@ -133,6 +155,10 @@ int_to_byte_little :: proc(v: ^Int) { err = int_to_bytes_little(v, b1); fmt.printf("little: %v | err: %v\n", b1, err); + int_from_bytes_little(t, b1); + if internal_cmp_mag(t, v) != 0 { + print("\tError parsing t: ", t); + } if size, err = int_to_bytes_size(v); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); @@ -142,6 +168,12 @@ int_to_byte_little :: proc(v: ^Int) { err = int_to_bytes_little_python(v, b2); fmt.printf("little python: %v | err: %v\n", b2, err); + if err == nil { + int_from_bytes_little_python(t, b2); + if internal_cmp_mag(t, v) != 0 { + print("\tError parsing t: ", t); + } + } if size, err = int_to_bytes_size(v, true); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); @@ -151,10 +183,10 @@ int_to_byte_little :: proc(v: ^Int) { err = int_to_bytes_little(v, b3, true); fmt.printf("little signed: %v | err: %v\n", b3, err); - // t := &Int{}; - // int_from_bytes_little(t, b3, true); - // defer destroy(t); - // print("t: ", t); + int_from_bytes_little(t, b3, true); + if internal_cmp(t, v) != 0 { + print("\tError parsing t: ", t); + } if size, err = int_to_bytes_size(v, true); err != nil { fmt.printf("int_to_bytes_size returned: %v\n", err); @@ -163,6 +195,11 @@ int_to_byte_little :: proc(v: ^Int) { b4 := make([]u8, size, context.temp_allocator); err = int_to_bytes_little_python(v, b4, true); fmt.printf("little signed python: %v | err: %v\n", b4, err); + + int_from_bytes_little_python(t, b4, true); + if internal_cmp(t, v) != 0 { + print("\tError parsing t: ", t); + } } demo :: proc() { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index cdd1e3bcb..81a95a215 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -12,6 +12,8 @@ package math_big import "core:intrinsics" import rnd "core:math/rand" +// import "core:fmt" + /* TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere. */ @@ -608,16 +610,17 @@ int_from_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := con if l == 0 { return .Invalid_Argument; } sign: Sign; - size_in_bits := l * 8; + size_in_bits := l * 8; if signed { /* First byte denotes the sign. */ size_in_bits -= 8; } - size_in_digits := size_in_bits / _DIGIT_BITS; + size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS; size_in_digits += 0 if size_in_bits % 8 == 0 else 1; - if err = internal_grow(a, size_in_digits); err != nil { return err; } + if err = internal_zero(a, false, allocator); err != nil { return err; } + if err = internal_grow(a, size_in_digits, false, allocator); err != nil { return err; } if signed { sign = .Zero_or_Positive if buf[0] == 0 else .Negative; @@ -629,9 +632,141 @@ int_from_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := con a.digit[0] |= DIGIT(v); } a.sign = sign; + a.used = size_in_digits; return internal_clamp(a); } +/* + Read `Int` from a Big Endian Python binary representation. + Sign is detected from the first byte if `signed` is true. +*/ +int_from_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + buf := buf; + l := len(buf); + if l == 0 { return .Invalid_Argument; } + + sign: Sign; + size_in_bits := l * 8; + if signed { + /* + First byte denotes the sign. + */ + size_in_bits -= 8; + } + size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS; + size_in_digits += 0 if size_in_bits % 8 == 0 else 1; + if err = internal_zero(a, false, allocator); err != nil { return err; } + if err = internal_grow(a, size_in_digits, false, allocator); err != nil { return err; } + + if signed { + sign = .Zero_or_Positive if buf[0] == 0 else .Negative; + buf = buf[1:]; + } + + for v in buf { + if err = internal_shl(a, a, 8); err != nil { return err; } + if signed && sign == .Negative { + a.digit[0] |= DIGIT(255 - v); + } else { + a.digit[0] |= DIGIT(v); + } + } + a.sign = sign; + a.used = size_in_digits; + if err = internal_clamp(a); err != nil { return err; } + + if signed && sign == .Negative { + return internal_sub(a, a, 1); + } + return nil; +} + +/* + Read `Int` from a Little Endian binary representation. + Sign is detected from the last byte if `signed` is true. +*/ +int_from_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + buf := buf; + l := len(buf); + if l == 0 { return .Invalid_Argument; } + + sign: Sign; + size_in_bits := l * 8; + if signed { + /* + First byte denotes the sign. + */ + size_in_bits -= 8; + } + size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS; + size_in_digits += 0 if size_in_bits % 8 == 0 else 1; + if err = internal_zero(a, false, allocator); err != nil { return err; } + if err = internal_grow(a, size_in_digits, false, allocator); err != nil { return err; } + + if signed { + sign = .Zero_or_Positive if buf[l-1] == 0 else .Negative; + buf = buf[:l-1]; + l -= 1; + } + + for _, i in buf { + if err = internal_shl(a, a, 8); err != nil { return err; } + a.digit[0] |= DIGIT(buf[l-i-1]); + } + a.sign = sign; + a.used = size_in_digits; + return internal_clamp(a); +} + +/* + Read `Int` from a Little Endian Python binary representation. + Sign is detected from the first byte if `signed` is true. +*/ +int_from_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) { + assert_if_nil(a); + buf := buf; + l := len(buf); + if l == 0 { return .Invalid_Argument; } + + sign: Sign; + size_in_bits := l * 8; + if signed { + /* + First byte denotes the sign. + */ + size_in_bits -= 8; + } + size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS; + size_in_digits += 0 if size_in_bits % 8 == 0 else 1; + if err = internal_zero(a, false, allocator); err != nil { return err; } + if err = internal_grow(a, size_in_digits, false, allocator); err != nil { return err; } + + if signed { + sign = .Zero_or_Positive if buf[l-1] == 0 else .Negative; + buf = buf[:l-1]; + l -= 1; + } + + for _, i in buf { + if err = internal_shl(a, a, 8); err != nil { return err; } + if signed && sign == .Negative { + a.digit[0] |= DIGIT(255 - buf[l-i-1]); + } else { + a.digit[0] |= DIGIT(buf[l-i-1]); + } + } + a.sign = sign; + a.used = size_in_digits; + if err = internal_clamp(a); err != nil { return err; } + + if signed && sign == .Negative { + return internal_sub(a, a, 1); + } + return nil; +} + /* Initialize constants. */