From 85aa4dd670ce308eaf51b6eefd10dd62a227c998 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 21:09:52 +0200 Subject: [PATCH] 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