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))