Browse Source

big: Start test suite.

Jeroen van Rijn 4 years ago
parent
commit
85aa4dd670
4 changed files with 146 additions and 21 deletions
  1. 17 0
      core/math/big/common.odin
  2. 6 21
      core/math/big/example.odin
  3. 44 0
      core/math/big/test.odin
  4. 79 0
      core/math/big/test.py

+ 17 - 0
core/math/big/common.odin

@@ -66,6 +66,23 @@ Error :: enum byte {
 	Unimplemented          = 127,
 	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 {
 Primality_Flag :: enum u8 {
 	Blum_Blum_Shub = 0,	/* BBS style prime */
 	Blum_Blum_Shub = 0,	/* BBS style prime */
 	Safe           = 1,	/* Safe prime (p-1)/2 == prime */
 	Safe           = 1,	/* Safe prime (p-1)/2 == prime */

+ 6 - 21
core/math/big/example.odin

@@ -13,7 +13,7 @@ package big
 import "core:fmt"
 import "core:fmt"
 import "core:mem"
 import "core:mem"
 import "core:time"
 import "core:time"
-import rnd "core:math/rand"
+// import rnd "core:math/rand"
 
 
 print_configation :: proc() {
 print_configation :: proc() {
 	fmt.printf(
 	fmt.printf(
@@ -66,28 +66,13 @@ print :: proc(name: string, a: ^Int, base := i8(10)) {
 }
 }
 
 
 demo :: proc() {
 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() {
 main :: proc() {

+ 44 - 0
core/math/big/test.odin

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

+ 79 - 0
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)