aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2021-07-29 17:29:32 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2021-08-11 20:59:52 +0200
commitc1a001c331b47e3f49241896deb48c2130b4abbb (patch)
tree2f843d5cd79a0bf392352a4aa29e12262e38b3b0 /core
parent13fab366394c4c1bd4f1e8eb2eefea81208764a6 (diff)
big: Add randomized testing.
Diffstat (limited to 'core')
-rw-r--r--core/math/big/build.bat3
-rw-r--r--core/math/big/example.odin5
-rw-r--r--core/math/big/logical.odin2
-rw-r--r--core/math/big/test.py94
4 files changed, 81 insertions, 23 deletions
diff --git a/core/math/big/build.bat b/core/math/big/build.bat
index 7df001c43..b4f340399 100644
--- a/core/math/big/build.bat
+++ b/core/math/big/build.bat
@@ -1,6 +1,7 @@
@echo off
:odin run . -vet
-odin build . -build-mode:dll
+odin build . -build-mode:dll -show-timings -opt:3
+:odin build . -build-mode:dll -show-timings
:dumpbin /EXPORTS big.dll
python test.py \ No newline at end of file
diff --git a/core/math/big/example.odin b/core/math/big/example.odin
index 5857c0a2e..e677af8b9 100644
--- a/core/math/big/example.odin
+++ b/core/math/big/example.odin
@@ -70,6 +70,11 @@ demo :: proc() {
// r := &rnd.Rand{};
// rnd.init(r, 12345);
+ // as := cstring("596360079055148742691396559496540363");
+ // bs := cstring("159671292010002348397151706347412301");
+
+ // res := test_div_two(as, bs);
+ // fmt.print(res);
// destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
// defer destroy(destination, source, quotient, remainder, numerator, denominator);
}
diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin
index 9b6654a5c..1d2ba0895 100644
--- a/core/math/big/logical.odin
+++ b/core/math/big/logical.odin
@@ -402,7 +402,7 @@ int_shl :: proc(dest, src: ^Int, bits: int) -> (err: Error) {
shift := DIGIT(_DIGIT_BITS - bits);
carry := DIGIT(0);
- for x:= 0; x <= dest.used; x+= 1 {
+ for x:= 0; x < dest.used; x+= 1 {
fwd_carry := (dest.digit[x] >> shift) & mask;
dest.digit[x] = (dest.digit[x] << uint(bits) | carry) & _MASK;
carry = fwd_carry;
diff --git a/core/math/big/test.py b/core/math/big/test.py
index ad5cc9f35..bd363caf0 100644
--- a/core/math/big/test.py
+++ b/core/math/big/test.py
@@ -2,6 +2,7 @@ from math import *
from ctypes import *
from random import *
import os
+import time
#
# Where is the DLL? If missing, build using: `odin build . -build-mode:dll`
@@ -9,6 +10,11 @@ import os
LIB_PATH = os.getcwd() + os.sep + "big.dll"
#
+# How many iterations of each random test do we want to run?
+#
+RANDOM_ITERATIONS = 10_000
+
+#
# Result values will be passed in a struct { res: cstring, err: Error }
#
class Res(Structure):
@@ -128,28 +134,48 @@ def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_re
return passed
def test_add_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None):
- res = add_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
+ sa = str(a)
+ sb = str(b)
+ sa_c = sa.encode('utf-8')
+ sb_c = sb.encode('utf-8')
+ res = add_two(sa_c, sb_c, radix)
if expected_result == None:
expected_result = a + b
- return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result)
+ return test("test_add_two", res, [sa, sb, radix], expected_error, expected_result)
def test_sub_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None):
- res = sub_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
+ sa = str(a)
+ sb = str(b)
+ sa_c = sa.encode('utf-8')
+ sb_c = sb.encode('utf-8')
+ res = sub_two(sa_c, sb_c, radix)
if expected_result == None:
expected_result = a - b
- return test("test_sub_two", res, [str(a), str(b), radix], expected_error, expected_result)
+ 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):
- res = mul_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
+ sa = str(a)
+ sb = str(b)
+ sa_c = sa.encode('utf-8')
+ sb_c = sb.encode('utf-8')
+ res = mul_two(sa_c, sb_c, radix)
if expected_result == None:
expected_result = a * b
- return test("test_mul_two", res, [str(a), str(b), radix], expected_error, expected_result)
+ 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):
- res = div_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
+ sa = str(a)
+ sb = str(b)
+ sa_c = sa.encode('utf-8')
+ sb_c = sb.encode('utf-8')
+ try:
+ res = div_two(sa_c, sb_c, radix)
+ except:
+ print("Exception with arguments:", a, b, radix)
+ return False
if expected_result == None:
expected_result = a // b if b != 0 else None
- return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result)
+ return test("test_div_two", res, [sa_c, sb_c, radix], expected_error, expected_result)
# TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on.
#
@@ -177,6 +203,8 @@ TESTS = {
],
}
+TIMINGS = {}
+
if __name__ == '__main__':
print()
print("---- core:math/big tests ----")
@@ -186,12 +214,20 @@ if __name__ == '__main__':
count_pass = 0
count_fail = 0
for t in TESTS[test_proc]:
- if test_proc(*t):
+ start = time.perf_counter()
+ res = test_proc(*t)
+ diff = time.perf_counter() - start
+ if test_proc not in TIMINGS:
+ TIMINGS[test_proc] = diff
+ else:
+ TIMINGS[test_proc] += diff
+
+ if res:
count_pass += 1
else:
count_fail += 1
- print("{}: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail))
+ print("{name}: {count_pass:,} passes, {count_fail:,} failures.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail))
print()
print("---- core:math/big random tests ----")
@@ -201,17 +237,33 @@ if __name__ == '__main__':
count_pass = 0
count_fail = 0
- a = randint(0, 1 << 120)
- b = randint(0, 1 << 120)
- res = None
+ for i in range(RANDOM_ITERATIONS):
+ a = randint(0, 1 << 120)
+ b = randint(0, 1 << 120)
+ res = None
- # We've already tested division by zero above.
- if b == 0 and test_proc == test_div_two:
- b = b + 1
+ # We've already tested division by zero above.
+ if b == 0 and test_proc == test_div_two:
+ b = b + 1
- if test_proc(a, b):
- count_pass += 1
- else:
- count_fail += 1
+ start = time.perf_counter()
+ res = test_proc(a, b)
+ diff = time.perf_counter() - start
+ if test_proc not in TIMINGS:
+ TIMINGS[test_proc] = diff
+ else:
+ TIMINGS[test_proc] += diff
- print("{} random: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail)) \ No newline at end of file
+ if res:
+ count_pass += 1
+ else:
+ count_fail += 1
+
+ print("{name}: {count_pass:,} passes, {count_fail:,} failures.".format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail))
+
+ print()
+ total = 0
+ for k in TIMINGS:
+ print("{name}: {total:.3f} ms in {calls:,} calls".format(name=k.__name__, total=TIMINGS[k] * 1_000, calls=RANDOM_ITERATIONS + len(TESTS[k])))
+ total += TIMINGS[k]
+ print("\ntotal: {0:.3f} ms".format(total * 1_000)) \ No newline at end of file