From 5619b349be2b77532e74c28ba05cf913ff4d8c78 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 6 Sep 2021 23:26:19 +0200 Subject: [PATCH 1/2] big: Remove `core:fmt` usage + Add a little demo to examples/demo. --- core/math/big/build.bat | 2 +- core/math/big/common.odin | 1 + core/math/big/example.odin | 152 ------------------------------------- core/math/big/helpers.odin | 2 - core/math/big/tune.odin | 124 ++++++++++++++++++++++++------ examples/demo/demo.odin | 78 ++++++++++++++++++- 6 files changed, 179 insertions(+), 180 deletions(-) delete mode 100644 core/math/big/example.odin diff --git a/core/math/big/build.bat b/core/math/big/build.bat index b41694523..308f45d31 100644 --- a/core/math/big/build.bat +++ b/core/math/big/build.bat @@ -5,7 +5,7 @@ set TEST_ARGS=-fast-tests :set TEST_ARGS= :odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS% -odin build . -build-mode:shared -show-timings -o:size -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS% +:odin build . -build-mode:shared -show-timings -o:size -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS% :odin build . -build-mode:shared -show-timings -o:size -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS% :odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS% :odin build . -build-mode:shared -show-timings -o:speed -define:MATH_BIG_EXE=false && python test.py -fast-tests %TEST_ARGS% \ No newline at end of file diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 3fb7601c0..5b7d162bc 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -174,6 +174,7 @@ Error :: enum int { } Error_String :: #partial [Error]string{ + .Okay = "Okay", .Out_Of_Memory = "Out of memory", .Invalid_Pointer = "Invalid pointer", .Invalid_Argument = "Invalid argument", diff --git a/core/math/big/example.odin b/core/math/big/example.odin deleted file mode 100644 index 695f6a72f..000000000 --- a/core/math/big/example.odin +++ /dev/null @@ -1,152 +0,0 @@ -//+ignore -/* - Copyright 2021 Jeroen van Rijn . - Made available under Odin's BSD-3 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. -*/ -package math_big - - -import "core:fmt" -import "core:mem" - -print_configation :: proc() { - fmt.printf( -` -Configuration: - _DIGIT_BITS %v - _SMALL_MEMORY %v - _MIN_DIGIT_COUNT %v - _MAX_DIGIT_COUNT %v - _DEFAULT_DIGIT_COUNT %v - _MAX_COMBA %v - _WARRAY %v - _TAB_SIZE %v - _MAX_WIN_SIZE %v - MATH_BIG_USE_LUCAS_SELFRIDGE_TEST %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 - USE_MILLER_RABIN_ONLY %v - MAX_ITERATIONS_RANDOM_PRIME %v - -`, _DIGIT_BITS, -_LOW_MEMORY, -_MIN_DIGIT_COUNT, -_MAX_DIGIT_COUNT, -_DEFAULT_DIGIT_COUNT, -_MAX_COMBA, -_WARRAY, -_TAB_SIZE, -_MAX_WIN_SIZE, -MATH_BIG_USE_LUCAS_SELFRIDGE_TEST, - -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, -USE_MILLER_RABIN_ONLY, -MAX_ITERATIONS_RANDOM_PRIME, -) - -} - -print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) { - assert_if_nil(a) - - as, err := itoa(a, base) - defer delete(as) - - cb := internal_count_bits(a) - if print_name { - 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: %v (digits: %v), flags: %v)", base, cb, a.used, a.flags) - } - if newline { - fmt.println() - } -} - -// printf :: fmt.printf; - -demo :: proc() { - a, b, c, d, e, f, res := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{} - defer destroy(a, b, c, d, e, f, res) - - bits := 111 - trials := -1 - - flags := Primality_Flags{} - fmt.printf("Trying to generate a %v bit prime using %v Miller-Rabin trials and options %v.\n", bits, trials, flags) - - err: Error - { - SCOPED_TIMING(.random_prime) - err = internal_random_prime(a, bits, trials, flags) - } - - print("a(10): ", a, 10, true, true, true) - fmt.printf("err: %v\n", err) - fmt.printf("RANDOM_PRIME_ITERATIONS_USED: %v\n", RANDOM_PRIME_ITERATIONS_USED) - - nails := 0 - - count := internal_int_pack_count(a, u8, nails) - buf := make([]u8, count) - defer delete(buf) - - written: int - order := Order.LSB_First - - fmt.printf("\na.digit: %v\n", a.digit[:a.used]) - written, err = internal_int_pack(a, buf, nails, order) - fmt.printf("\nPacked into buf: %v | err: %v | written: %v\n", buf, err, written) - - err = internal_int_unpack(b, buf, nails, order) - print("\nUnpacked into b: ", b) - fmt.printf("err: %v\n", err) - fmt.printf("b.digit: %v\n", b.digit[:b.used]) -} - -main :: proc() { - ta := mem.Tracking_Allocator{} - mem.tracking_allocator_init(&ta, context.allocator) - context.allocator = mem.tracking_allocator(&ta) - - demo() - - print_configation() - - 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) - } - } - 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/big/helpers.odin b/core/math/big/helpers.odin index ac45c241d..10c9f9e13 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -11,8 +11,6 @@ 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. */ diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin index a268f541d..d67ff61b4 100644 --- a/core/math/big/tune.odin +++ b/core/math/big/tune.odin @@ -9,8 +9,84 @@ */ package math_big -import "core:fmt" import "core:time" +import "core:runtime" + +print_value :: proc(name: string, value: i64) { + runtime.print_string("\t") + runtime.print_string(name) + runtime.print_string(": ") + runtime.print_i64(value) + runtime.print_string("\n") +} + +print_bool :: proc(name: string, value: bool) { + runtime.print_string("\t") + runtime.print_string(name) + if value { + runtime.print_string(": true\n") + } else { + runtime.print_string(": false\n") + } +} + +print_configation :: proc() { + runtime.print_string("Configuration:\n") + print_value("_DIGIT_BITS ", _DIGIT_BITS) + print_bool ("MATH_BIG_SMALL_MEMORY ", _LOW_MEMORY) + print_value("_MIN_DIGIT_COUNT ", _MIN_DIGIT_COUNT) + print_value("_MAX_DIGIT_COUNT ", i64(_MAX_DIGIT_COUNT)) + print_value("_DEFAULT_DIGIT_COUNT ", _DEFAULT_DIGIT_COUNT) + print_value("_MAX_COMBA ", _MAX_COMBA) + print_value("_WARRAY ", _WARRAY) + print_value("_TAB_SIZE ", _TAB_SIZE) + print_value("_MAX_WIN_SIZE ", _MAX_WIN_SIZE) + print_bool ("MATH_BIG_USE_LUCAS_SELFRIDGE_TEST ", MATH_BIG_USE_LUCAS_SELFRIDGE_TEST) + + runtime.print_string("\nRuntime tunable:\n") + print_value("MUL_KARATSUBA_CUTOFF ", i64(MUL_KARATSUBA_CUTOFF)) + print_value("SQR_KARATSUBA_CUTOFF ", i64(SQR_KARATSUBA_CUTOFF)) + print_value("MUL_TOOM_CUTOFF ", i64(MUL_TOOM_CUTOFF)) + print_value("SQR_TOOM_CUTOFF ", i64(SQR_TOOM_CUTOFF)) + print_value("MAX_ITERATIONS_ROOT_N ", i64(MAX_ITERATIONS_ROOT_N)) + print_value("FACTORIAL_MAX_N ", i64(FACTORIAL_MAX_N)) + print_value("FACTORIAL_BINARY_SPLIT_CUTOFF ", i64(FACTORIAL_BINARY_SPLIT_CUTOFF)) + print_value("FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS", i64(FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS)) + print_value("FACTORIAL_BINARY_SPLIT_CUTOFF ", i64(FACTORIAL_BINARY_SPLIT_CUTOFF)) + print_bool ("USE_MILLER_RABIN_ONLY ", USE_MILLER_RABIN_ONLY) + print_value("MAX_ITERATIONS_RANDOM_PRIME ", i64(MAX_ITERATIONS_RANDOM_PRIME)) +} + +print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) { + assert_if_nil(a) + + as, err := itoa(a, base) + defer delete(as) + + cb := internal_count_bits(a) + if print_name { + runtime.print_string(name) + } + if err != nil { + runtime.print_string("(Error: ") + es := Error_String + runtime.print_string(es[err]) + runtime.print_string(")") + } + runtime.print_string(as) + if print_extra_info { + runtime.print_string(" (base: ") + runtime.print_i64(i64(base)) + runtime.print_string(", bits: ") + runtime.print_i64(i64(cb)) + runtime.print_string(", digits: ") + runtime.print_i64(i64(a.used)) + runtime.print_string(")") + } + if newline { + runtime.print_string("\n") + } +} Category :: enum { itoa, @@ -35,32 +111,32 @@ Event :: struct { 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)) - } - } + // 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)) + // } + // } - for v in Timings { - if v.count > 0 { - fmt.println("\nTimings:") - break - } - } + // for v in Timings { + // if v.count > 0 { + // fmt.println("\nTimings:") + // break + // } + // } - 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) + // 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) - } - } + // 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) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index c64c54887..5fa2af987 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -8,7 +8,7 @@ import "core:time" import "core:reflect" import "core:runtime" import "core:intrinsics" - +import "core:math/big" /* The Odin programming language is fast, concise, readable, pragmatic and open sourced. @@ -2127,6 +2127,81 @@ or_return_operator :: proc() { foo_2() } +arbitrary_precision_maths :: proc() { + fmt.println("\n# core:math/big") + + print_bigint :: proc(name: string, a: ^big.Int, base := i8(10), print_name := true, newline := true, print_extra_info := true) { + big.assert_if_nil(a) + + as, err := big.itoa(a, base) + defer delete(as) + + cb := big.internal_count_bits(a) + if print_name { + fmt.printf(name) + } + if err != nil { + fmt.printf(" (Error: %v) ", err) + } + fmt.printf(as) + if print_extra_info { + fmt.printf(" (base: %v, bits: %v, digits: %v)", base, cb, a.used) + } + if newline { + fmt.println() + } + } + + a, b, c, d, e, f, res := &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{} + defer big.destroy(a, b, c, d, e, f, res) + + // How many bits should the random prime be? + bits := 64 + // Number of Rabin-Miller trials, -1 for automatic. + trials := -1 + + // Default prime generation flags + flags := big.Primality_Flags{} + + err := big.internal_random_prime(a, bits, trials, flags) + if err != nil { + fmt.printf("Error %v while generating random prime.\n", err) + } else { + print_bigint("Random Prime A: ", a, 10) + fmt.printf("Random number iterations until prime found: %v\n", big.RANDOM_PRIME_ITERATIONS_USED) + } + + // If we want to pack this Int into a buffer of u32, how many do we need? + count := big.internal_int_pack_count(a, u32) + buf := make([]u32, count) + defer delete(buf) + + written: int + written, err = big.internal_int_pack(a, buf) + fmt.printf("\nPacked into u32 buf: %v | err: %v | written: %v\n", buf, err, written) + + // If we want to pack this Int into a buffer of bytes of which only the bottom 6 bits are used, how many do we need? + nails := 2 + + count = big.internal_int_pack_count(a, u8, nails) + byte_buf := make([]u8, count) + defer delete(buf) + + written, err = big.internal_int_pack(a, byte_buf, nails) + fmt.printf("\nPacked into buf of 6-bit bytes: %v | err: %v | written: %v\n", byte_buf, err, written) + + + + // Pick another random big Int, not necesssarily prime. + err = big.random(b, 2048) + print_bigint("\n2048 bit random number: ", b) + + // Calculate GCD + LCM in one fell swoop + big.gcd_lcm(c, d, a, b) + + print_bigint("\nGCD of random prime A and random number B: ", c) + print_bigint("\nLCM of random prime A and random number B (in base 36): ", d, 36) +} main :: proc() { when true { @@ -2162,5 +2237,6 @@ main :: proc() { relative_data_types() or_else_operator() or_return_operator() + arbitrary_precision_maths() } } From 48bfce2efcf99d2b79bdbb19ae451973a757d006 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 6 Sep 2021 23:32:32 +0200 Subject: [PATCH 2/2] demo: Fix typo. --- examples/demo/demo.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 5fa2af987..b3471fe87 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -2185,7 +2185,7 @@ arbitrary_precision_maths :: proc() { count = big.internal_int_pack_count(a, u8, nails) byte_buf := make([]u8, count) - defer delete(buf) + defer delete(byte_buf) written, err = big.internal_int_pack(a, byte_buf, nails) fmt.printf("\nPacked into buf of 6-bit bytes: %v | err: %v | written: %v\n", byte_buf, err, written)