Browse Source

big: Remove `core:fmt` usage + Add a little demo to examples/demo.

Jeroen van Rijn 3 years ago
parent
commit
5619b349be

+ 1 - 1
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%

+ 1 - 0
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",

+ 0 - 152
core/math/big/example.odin

@@ -1,152 +0,0 @@
-//+ignore
-/*
-	Copyright 2021 Jeroen van Rijn <[email protected]>.
-	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)
-		}
-	}
-}

+ 0 - 2
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.
 */

+ 100 - 24
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)

+ 77 - 1
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()
 	}
 }