From 85a2a8815e2ae06d23ec23224e00084e143363e2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 4 Aug 2021 13:43:16 +0200 Subject: [PATCH] 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); }