mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge pull request #5311 from Kelimion/big-tests
Turn `core:math/bìg` tests into regular `core:testing` tests.
This commit is contained in:
@@ -257,12 +257,6 @@ jobs:
|
|||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\documentation
|
cd tests\documentation
|
||||||
call build.bat
|
call build.bat
|
||||||
- name: core:math/big tests
|
|
||||||
shell: cmd
|
|
||||||
run: |
|
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
|
||||||
cd tests\core\math\big
|
|
||||||
call build.bat
|
|
||||||
- name: Odin check examples/all for Windows 32bits
|
- name: Odin check examples/all for Windows 32bits
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1660,13 +1660,13 @@ internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (e
|
|||||||
|
|
||||||
if internal_gte(y, x) {
|
if internal_gte(y, x) {
|
||||||
internal_swap(dest, x)
|
internal_swap(dest, x)
|
||||||
return nil
|
return internal_clamp(dest)
|
||||||
}
|
}
|
||||||
internal_swap(x, y)
|
internal_swap(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal_swap(dest, x)
|
internal_swap(dest, x)
|
||||||
return err
|
return internal_clamp(dest)
|
||||||
}
|
}
|
||||||
internal_sqrt :: proc { internal_int_sqrt, }
|
internal_sqrt :: proc { internal_int_sqrt, }
|
||||||
|
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ int_atoi :: proc(res: ^Int, input: string, radix := i8(10), allocator := context
|
|||||||
res.sign = sign
|
res.sign = sign
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return internal_clamp(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
@echo off
|
|
||||||
rem math/big tests
|
|
||||||
set PATH_TO_ODIN==..\..\..\..\odin
|
|
||||||
set TEST_ARGS=-fast-tests
|
|
||||||
set TEST_ARGS=-no-random
|
|
||||||
set TEST_ARGS=
|
|
||||||
set OUT_NAME=math_big_test_library.dll
|
|
||||||
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style
|
|
||||||
echo ---
|
|
||||||
echo Running core:math/big tests
|
|
||||||
echo ---
|
|
||||||
|
|
||||||
%PATH_TO_ODIN% build . %COMMON% -o:speed -out:%OUT_NAME%
|
|
||||||
python3 test.py %TEST_ARGS%
|
|
||||||
|
|
||||||
%PATH_TO_ODIN% test test_core_math_big.odin -file
|
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
# The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
# The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||||
#
|
#
|
||||||
|
|
||||||
from ctypes import *
|
|
||||||
from random import *
|
from random import *
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@@ -17,19 +16,53 @@ import gc
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
LEG_BITS = 60
|
||||||
description = "Odin core:math/big test suite",
|
|
||||||
epilog = "By default we run regression and random tests with preset parameters.",
|
|
||||||
formatter_class = argparse.ArgumentDefaultsHelpFormatter,
|
|
||||||
)
|
|
||||||
|
|
||||||
#
|
vectors = open('../test_vectors.odin', 'w')
|
||||||
# Normally, we report the number of passes and fails. With this option set, we exit at first fail.
|
vectors.write("""package test_core_math_big
|
||||||
#
|
|
||||||
parser.add_argument(
|
import "core:math/big"
|
||||||
"-exit-on-fail",
|
|
||||||
help = "Exit when a test fails",
|
// GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED
|
||||||
action = "store_true",
|
//
|
||||||
|
// This file is generated using `test_generator.py`
|
||||||
|
//
|
||||||
|
// GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED -=- GENERATED
|
||||||
|
|
||||||
|
Big_Test_Operation :: enum {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Sqr,
|
||||||
|
Log,
|
||||||
|
Sqrt,
|
||||||
|
Pow,
|
||||||
|
Root,
|
||||||
|
Shl,
|
||||||
|
Shr,
|
||||||
|
Shr_Signed,
|
||||||
|
Factorial,
|
||||||
|
Gcd,
|
||||||
|
Lcm,
|
||||||
|
Is_Square,
|
||||||
|
}
|
||||||
|
|
||||||
|
Big_Test_Vector :: struct {
|
||||||
|
op: Big_Test_Operation,
|
||||||
|
a: string,
|
||||||
|
b: string,
|
||||||
|
exp: string,
|
||||||
|
err: big.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
big_test_vectors := []Big_Test_Vector{
|
||||||
|
""")
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description = "Odin core:math/big test suite generator",
|
||||||
|
epilog = "By default we generate regression and random tests with preset parameters.",
|
||||||
|
formatter_class = argparse.ArgumentDefaultsHelpFormatter,
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -39,7 +72,7 @@ no_random = parser.add_mutually_exclusive_group()
|
|||||||
|
|
||||||
no_random.add_argument(
|
no_random.add_argument(
|
||||||
"-no-random",
|
"-no-random",
|
||||||
help = "No random tests",
|
help = "Don't generate random tests",
|
||||||
action = "store_true",
|
action = "store_true",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -76,14 +109,12 @@ timed_or_fast.add_argument(
|
|||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
EXIT_ON_FAIL = args.exit_on_fail
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# How many iterations of each random test do we want to run?
|
# How many iterations of each random test do we want to run?
|
||||||
#
|
#
|
||||||
BITS_AND_ITERATIONS = [
|
BITS_AND_ITERATIONS = [
|
||||||
( 120, 10_000),
|
( 120, 100),
|
||||||
( 1_200, 1_000),
|
( 1_200, 100),
|
||||||
( 4_096, 100),
|
( 4_096, 100),
|
||||||
(12_000, 10),
|
(12_000, 10),
|
||||||
]
|
]
|
||||||
@@ -96,20 +127,6 @@ if args.fast_tests:
|
|||||||
if args.no_random:
|
if args.no_random:
|
||||||
BITS_AND_ITERATIONS = []
|
BITS_AND_ITERATIONS = []
|
||||||
|
|
||||||
#
|
|
||||||
# Where is the DLL? If missing, build using: `odin build . -build-mode:shared`
|
|
||||||
#
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
LIB_PATH = os.getcwd() + os.sep + "math_big_test_library.dll"
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
LIB_PATH = os.getcwd() + os.sep + "math_big_test_library.so"
|
|
||||||
elif platform.system() == "Darwin":
|
|
||||||
LIB_PATH = os.getcwd() + os.sep + "math_big_test_library.dylib"
|
|
||||||
else:
|
|
||||||
print("Platform is unsupported.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
TOTAL_TIME = 0
|
TOTAL_TIME = 0
|
||||||
UNTIL_TIME = 0
|
UNTIL_TIME = 0
|
||||||
UNTIL_ITERS = 0
|
UNTIL_ITERS = 0
|
||||||
@@ -148,96 +165,6 @@ class Error(Enum):
|
|||||||
#
|
#
|
||||||
gc.disable()
|
gc.disable()
|
||||||
|
|
||||||
#
|
|
||||||
# Set up exported procedures
|
|
||||||
#
|
|
||||||
try:
|
|
||||||
l = cdll.LoadLibrary(LIB_PATH)
|
|
||||||
except:
|
|
||||||
print("Couldn't find or load " + LIB_PATH + ".")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
def load(export_name, args, res):
|
|
||||||
export_name.argtypes = args
|
|
||||||
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)]
|
|
||||||
|
|
||||||
initialize_constants = load(l.test_initialize_constants, [], c_uint64)
|
|
||||||
|
|
||||||
NAILS = initialize_constants()
|
|
||||||
LEG_BITS = 64 - NAILS
|
|
||||||
|
|
||||||
print("LEG BITS: ", LEG_BITS)
|
|
||||||
|
|
||||||
error_string = load(l.test_error_string, [c_byte], c_char_p)
|
|
||||||
|
|
||||||
add = load(l.test_add, [c_char_p, c_char_p ], Res)
|
|
||||||
sub = load(l.test_sub, [c_char_p, c_char_p ], Res)
|
|
||||||
mul = load(l.test_mul, [c_char_p, c_char_p ], Res)
|
|
||||||
sqr = load(l.test_sqr, [c_char_p ], Res)
|
|
||||||
div = load(l.test_div, [c_char_p, c_char_p ], Res)
|
|
||||||
|
|
||||||
# Powers and such
|
|
||||||
int_log = load(l.test_log, [c_char_p, c_longlong], Res)
|
|
||||||
int_pow = load(l.test_pow, [c_char_p, c_longlong], Res)
|
|
||||||
int_sqrt = load(l.test_sqrt, [c_char_p ], Res)
|
|
||||||
int_root_n = load(l.test_root_n, [c_char_p, c_longlong], Res)
|
|
||||||
|
|
||||||
# Logical operations
|
|
||||||
int_shl_leg = load(l.test_shl_leg, [c_char_p, c_longlong], Res)
|
|
||||||
int_shr_leg = load(l.test_shr_leg, [c_char_p, c_longlong], Res)
|
|
||||||
int_shl = load(l.test_shl, [c_char_p, c_longlong], Res)
|
|
||||||
int_shr = load(l.test_shr, [c_char_p, c_longlong], Res)
|
|
||||||
int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res)
|
|
||||||
|
|
||||||
int_factorial = load(l.test_factorial, [c_uint64 ], Res)
|
|
||||||
int_gcd = load(l.test_gcd, [c_char_p, c_char_p ], Res)
|
|
||||||
int_lcm = load(l.test_lcm, [c_char_p, c_char_p ], Res)
|
|
||||||
|
|
||||||
is_square = load(l.test_is_square, [c_char_p ], Res)
|
|
||||||
|
|
||||||
def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16):
|
|
||||||
passed = True
|
|
||||||
r = None
|
|
||||||
err = Error(res.err)
|
|
||||||
|
|
||||||
if err != expected_error:
|
|
||||||
error_loc = res.res.decode('utf-8')
|
|
||||||
error = "{}: {} in '{}'".format(test_name, err, error_loc)
|
|
||||||
|
|
||||||
if len(param):
|
|
||||||
error += " with params {}".format(param)
|
|
||||||
|
|
||||||
print(error, flush=True)
|
|
||||||
passed = False
|
|
||||||
elif err == Error.Okay:
|
|
||||||
r = None
|
|
||||||
try:
|
|
||||||
r = res.res.decode('utf-8')
|
|
||||||
r = int(res.res, radix)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if r != expected_result:
|
|
||||||
error = "{}: Result was '{}', expected '{}'".format(test_name, r, expected_result)
|
|
||||||
if len(param):
|
|
||||||
error += " with params {}".format(param)
|
|
||||||
|
|
||||||
print(error, flush=True)
|
|
||||||
passed = False
|
|
||||||
|
|
||||||
if EXIT_ON_FAIL and not passed: exit(res.err)
|
|
||||||
|
|
||||||
return passed
|
|
||||||
|
|
||||||
def arg_to_odin(a):
|
def arg_to_odin(a):
|
||||||
if a >= 0:
|
if a >= 0:
|
||||||
s = hex(a)[2:]
|
s = hex(a)[2:]
|
||||||
@@ -281,58 +208,58 @@ def big_integer_lcm(a, b):
|
|||||||
lcm = b // math.gcd(a, b)
|
lcm = b // math.gcd(a, b)
|
||||||
return abs(a * lcm)
|
return abs(a * lcm)
|
||||||
|
|
||||||
|
def write_test_case(op = "", a = 0, b = 0, expected_result = 0, expected_error = Error.Okay):
|
||||||
|
def test_arg_to_odin(a):
|
||||||
|
if a >= 0:
|
||||||
|
s = hex(a)[2:]
|
||||||
|
else:
|
||||||
|
s = '-' + hex(a)[3:]
|
||||||
|
return s
|
||||||
|
|
||||||
|
vectors.write("\t{{{}, ".format(op))
|
||||||
|
vectors.write("\"{}\", ".format(test_arg_to_odin(a)))
|
||||||
|
vectors.write("\"{}\", ".format(test_arg_to_odin(b)))
|
||||||
|
if expected_result == None:
|
||||||
|
vectors.write("\"0\", ")
|
||||||
|
else:
|
||||||
|
vectors.write("\"{}\", ".format(test_arg_to_odin(expected_result)))
|
||||||
|
|
||||||
|
vectors.write("{}}},\n".format(expected_error)[5:])
|
||||||
|
|
||||||
def test_add(a = 0, b = 0, expected_error = Error.Okay):
|
def test_add(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), arg_to_odin(b)]
|
args = [arg_to_odin(a), arg_to_odin(b)]
|
||||||
res = add(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = a + b
|
expected_result = a + b
|
||||||
return test("test_add", res, [a, b], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Add", a, b, expected_result, expected_error)
|
||||||
|
|
||||||
def test_sub(a = 0, b = 0, expected_error = Error.Okay):
|
def test_sub(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), arg_to_odin(b)]
|
args = [arg_to_odin(a), arg_to_odin(b)]
|
||||||
res = sub(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = a - b
|
expected_result = a - b
|
||||||
return test("test_sub", res, [a, b], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Sub", a, b, expected_result, expected_error)
|
||||||
|
|
||||||
def test_mul(a = 0, b = 0, expected_error = Error.Okay):
|
def test_mul(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), arg_to_odin(b)]
|
args = [arg_to_odin(a), arg_to_odin(b)]
|
||||||
try:
|
|
||||||
res = mul(*args)
|
|
||||||
except OSError as e:
|
|
||||||
print("{} while trying to multiply {} x {}.".format(e, a, b))
|
|
||||||
if EXIT_ON_FAIL: exit(3)
|
|
||||||
return False
|
|
||||||
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = a * b
|
expected_result = a * b
|
||||||
return test("test_mul", res, [a, b], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Mul", a, b, expected_result, expected_error)
|
||||||
|
|
||||||
def test_sqr(a = 0, b = 0, expected_error = Error.Okay):
|
def test_sqr(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a)]
|
args = [arg_to_odin(a)]
|
||||||
try:
|
|
||||||
res = sqr(*args)
|
|
||||||
except OSError as e:
|
|
||||||
print("{} while trying to square {}.".format(e, a))
|
|
||||||
if EXIT_ON_FAIL: exit(3)
|
|
||||||
return False
|
|
||||||
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = a * a
|
expected_result = a * a
|
||||||
return test("test_sqr", res, [a], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Sqr", a, b, expected_result, expected_error)
|
||||||
|
|
||||||
def test_div(a = 0, b = 0, expected_error = Error.Okay):
|
def test_div(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), arg_to_odin(b)]
|
args = [arg_to_odin(a), arg_to_odin(b)]
|
||||||
try:
|
|
||||||
res = div(*args)
|
|
||||||
except OSError as e:
|
|
||||||
print("{} while trying divide to {} / {}.".format(e, a, b))
|
|
||||||
if EXIT_ON_FAIL: exit(3)
|
|
||||||
return False
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
#
|
#
|
||||||
@@ -344,22 +271,19 @@ def test_div(a = 0, b = 0, expected_error = Error.Okay):
|
|||||||
expected_result = int(-(a // abs((b))))
|
expected_result = int(-(a // abs((b))))
|
||||||
else:
|
else:
|
||||||
expected_result = a // b if b != 0 else None
|
expected_result = a // b if b != 0 else None
|
||||||
return test("test_div", res, [a, b], expected_error, expected_result)
|
|
||||||
|
|
||||||
|
write_test_case(".Div", a, b, expected_result, expected_error)
|
||||||
|
|
||||||
def test_log(a = 0, base = 0, expected_error = Error.Okay):
|
def test_log(a = 0, base = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), base]
|
args = [arg_to_odin(a), base]
|
||||||
res = int_log(*args)
|
|
||||||
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = int(math.log(a, base))
|
expected_result = int(math.log(a, base))
|
||||||
return test("test_log", res, [a, base], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Log", a, base, expected_result, expected_error)
|
||||||
|
|
||||||
def test_pow(base = 0, power = 0, expected_error = Error.Okay):
|
def test_pow(base = 0, power = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(base), power]
|
args = [arg_to_odin(base), power]
|
||||||
res = int_pow(*args)
|
|
||||||
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
if power < 0:
|
if power < 0:
|
||||||
@@ -368,24 +292,19 @@ def test_pow(base = 0, power = 0, expected_error = Error.Okay):
|
|||||||
# NOTE(Jeroen): Don't use `math.pow`, it's a floating point approximation.
|
# NOTE(Jeroen): Don't use `math.pow`, it's a floating point approximation.
|
||||||
# Use built-in `pow` or `a**b` instead.
|
# Use built-in `pow` or `a**b` instead.
|
||||||
expected_result = pow(base, power)
|
expected_result = pow(base, power)
|
||||||
return test("test_pow", res, [base, power], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Pow", base, power, expected_result, expected_error)
|
||||||
|
|
||||||
def test_sqrt(number = 0, expected_error = Error.Okay):
|
def test_sqrt(number = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(number)]
|
args = [arg_to_odin(number)]
|
||||||
try:
|
|
||||||
res = int_sqrt(*args)
|
|
||||||
except OSError as e:
|
|
||||||
print("{} while trying to sqrt {}.".format(e, number))
|
|
||||||
if EXIT_ON_FAIL: exit(3)
|
|
||||||
return False
|
|
||||||
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
if number < 0:
|
if number < 0:
|
||||||
expected_result = 0
|
expected_result = 0
|
||||||
else:
|
else:
|
||||||
expected_result = big_integer_sqrt(number)
|
expected_result = big_integer_sqrt(number)
|
||||||
return test("test_sqrt", res, [number], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Sqrt", number, 0, expected_result, expected_error)
|
||||||
|
|
||||||
def root_n(number, root):
|
def root_n(number, root):
|
||||||
u, s = number, number + 1
|
u, s = number, number + 1
|
||||||
@@ -397,7 +316,6 @@ def root_n(number, root):
|
|||||||
|
|
||||||
def test_root_n(number = 0, root = 0, expected_error = Error.Okay):
|
def test_root_n(number = 0, root = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(number), root]
|
args = [arg_to_odin(number), root]
|
||||||
res = int_root_n(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
if number < 0:
|
if number < 0:
|
||||||
@@ -405,19 +323,18 @@ def test_root_n(number = 0, root = 0, expected_error = Error.Okay):
|
|||||||
else:
|
else:
|
||||||
expected_result = root_n(number, root)
|
expected_result = root_n(number, root)
|
||||||
|
|
||||||
return test("test_root_n", res, [number, root], expected_error, expected_result)
|
write_test_case(".Root", number, root, expected_result, expected_error)
|
||||||
|
|
||||||
def test_shl_leg(a = 0, digits = 0, expected_error = Error.Okay):
|
def test_shl_leg(a = 0, digits = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), digits]
|
args = [arg_to_odin(a), digits]
|
||||||
res = int_shl_leg(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = a << (digits * LEG_BITS)
|
expected_result = a << (digits * LEG_BITS)
|
||||||
return test("test_shl_leg", res, [a, digits], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Shl", a, (digits * LEG_BITS), expected_result, expected_error)
|
||||||
|
|
||||||
def test_shr_leg(a = 0, digits = 0, expected_error = Error.Okay):
|
def test_shr_leg(a = 0, digits = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), digits]
|
args = [arg_to_odin(a), digits]
|
||||||
res = int_shr_leg(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
if a < 0:
|
if a < 0:
|
||||||
@@ -426,19 +343,18 @@ def test_shr_leg(a = 0, digits = 0, expected_error = Error.Okay):
|
|||||||
else:
|
else:
|
||||||
expected_result = a >> (digits * LEG_BITS)
|
expected_result = a >> (digits * LEG_BITS)
|
||||||
|
|
||||||
return test("test_shr_leg", res, [a, digits], expected_error, expected_result)
|
write_test_case(".Shr", a, (digits * LEG_BITS), expected_result, expected_error)
|
||||||
|
|
||||||
def test_shl(a = 0, bits = 0, expected_error = Error.Okay):
|
def test_shl(a = 0, bits = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), bits]
|
args = [arg_to_odin(a), bits]
|
||||||
res = int_shl(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = a << bits
|
expected_result = a << bits
|
||||||
return test("test_shl", res, [a, bits], expected_error, expected_result)
|
|
||||||
|
write_test_case(".Shl", a, bits, expected_result, expected_error)
|
||||||
|
|
||||||
def test_shr(a = 0, bits = 0, expected_error = Error.Okay):
|
def test_shr(a = 0, bits = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), bits]
|
args = [arg_to_odin(a), bits]
|
||||||
res = int_shr(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
if a < 0:
|
if a < 0:
|
||||||
@@ -447,58 +363,47 @@ def test_shr(a = 0, bits = 0, expected_error = Error.Okay):
|
|||||||
else:
|
else:
|
||||||
expected_result = a >> bits
|
expected_result = a >> bits
|
||||||
|
|
||||||
return test("test_shr", res, [a, bits], expected_error, expected_result)
|
write_test_case(".Shr", a, bits, expected_result, expected_error)
|
||||||
|
|
||||||
def test_shr_signed(a = 0, bits = 0, expected_error = Error.Okay):
|
def test_shr_signed(a = 0, bits = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), bits]
|
args = [arg_to_odin(a), bits]
|
||||||
res = int_shr_signed(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = a >> bits
|
expected_result = a >> bits
|
||||||
|
|
||||||
return test("test_shr_signed", res, [a, bits], expected_error, expected_result)
|
write_test_case(".Shr_Signed", a, bits, expected_result, expected_error)
|
||||||
|
|
||||||
def test_factorial(number = 0, expected_error = Error.Okay):
|
def test_factorial(number = 0, expected_error = Error.Okay):
|
||||||
args = [number]
|
args = [number]
|
||||||
try:
|
|
||||||
res = int_factorial(*args)
|
|
||||||
except OSError as e:
|
|
||||||
print("{} while trying to factorial {}.".format(e, number))
|
|
||||||
if EXIT_ON_FAIL: exit(3)
|
|
||||||
return False
|
|
||||||
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = math.factorial(number)
|
expected_result = math.factorial(number)
|
||||||
|
|
||||||
return test("test_factorial", res, [number], expected_error, expected_result)
|
write_test_case(".Factorial", number, 0, expected_result, expected_error)
|
||||||
|
|
||||||
def test_gcd(a = 0, b = 0, expected_error = Error.Okay):
|
def test_gcd(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), arg_to_odin(b)]
|
args = [arg_to_odin(a), arg_to_odin(b)]
|
||||||
res = int_gcd(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = math.gcd(a, b)
|
expected_result = math.gcd(a, b)
|
||||||
|
|
||||||
return test("test_gcd", res, [a, b], expected_error, expected_result)
|
write_test_case(".Gcd", a, b, expected_result, expected_error)
|
||||||
|
|
||||||
def test_lcm(a = 0, b = 0, expected_error = Error.Okay):
|
def test_lcm(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a), arg_to_odin(b)]
|
args = [arg_to_odin(a), arg_to_odin(b)]
|
||||||
res = int_lcm(*args)
|
|
||||||
expected_result = None
|
expected_result = None
|
||||||
if expected_error == Error.Okay:
|
if expected_error == Error.Okay:
|
||||||
expected_result = big_integer_lcm(a, b)
|
expected_result = big_integer_lcm(a, b)
|
||||||
|
|
||||||
return test("test_lcm", res, [a, b], expected_error, expected_result)
|
write_test_case(".Lcm", a, b, expected_result, expected_error)
|
||||||
|
|
||||||
def test_is_square(a = 0, b = 0, expected_error = Error.Okay):
|
def test_is_square(a = 0, b = 0, expected_error = Error.Okay):
|
||||||
args = [arg_to_odin(a)]
|
args = [arg_to_odin(a)]
|
||||||
res = is_square(*args)
|
expected_result = False
|
||||||
expected_result = None
|
if expected_error == Error.Okay and a > 0:
|
||||||
if expected_error == Error.Okay:
|
expected_result = big_integer_sqrt(a) ** 2 == a
|
||||||
expected_result = str(big_integer_sqrt(a) ** 2 == a) if a > 0 else "False"
|
|
||||||
|
|
||||||
return test("test_is_square", res, [a], expected_error, expected_result)
|
write_test_case(".Is_Square", a, 0, expected_result, expected_error)
|
||||||
|
|
||||||
# TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on.
|
# TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on.
|
||||||
#
|
#
|
||||||
@@ -647,32 +552,16 @@ if __name__ == '__main__':
|
|||||||
for test_proc in TESTS:
|
for test_proc in TESTS:
|
||||||
max_name = max(max_name, len(test_proc.__name__))
|
max_name = max(max_name, len(test_proc.__name__))
|
||||||
|
|
||||||
fmt_string = "{name:>{max_name}}: {count_pass:7,} passes and {count_fail:7,} failures in {timing:9.3f} ms."
|
fmt_string = "{name:>{max_name}}, {test_count} tests"
|
||||||
fmt_string = fmt_string.replace("{max_name}", str(max_name))
|
fmt_string = fmt_string.replace("{max_name}", str(max_name))
|
||||||
|
|
||||||
for test_proc in TESTS:
|
for test_proc in TESTS:
|
||||||
count_pass = 0
|
count = 0
|
||||||
count_fail = 0
|
|
||||||
TIMINGS = {}
|
|
||||||
for t in TESTS[test_proc]:
|
for t in TESTS[test_proc]:
|
||||||
start = time.perf_counter()
|
count += 1
|
||||||
res = test_proc(*t)
|
test_proc(*t)
|
||||||
diff = time.perf_counter() - start
|
|
||||||
TOTAL_TIME += diff
|
|
||||||
|
|
||||||
if test_proc not in TIMINGS:
|
print(fmt_string.format(name=test_proc.__name__, test_count=count))
|
||||||
TIMINGS[test_proc] = diff
|
|
||||||
else:
|
|
||||||
TIMINGS[test_proc] += diff
|
|
||||||
|
|
||||||
if res:
|
|
||||||
count_pass += 1
|
|
||||||
total_passes += 1
|
|
||||||
else:
|
|
||||||
count_fail += 1
|
|
||||||
total_failures += 1
|
|
||||||
|
|
||||||
print(fmt_string.format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000))
|
|
||||||
|
|
||||||
for BITS, ITERATIONS in BITS_AND_ITERATIONS:
|
for BITS, ITERATIONS in BITS_AND_ITERATIONS:
|
||||||
print()
|
print()
|
||||||
@@ -688,9 +577,7 @@ if __name__ == '__main__':
|
|||||||
if BITS > 1_200 and test_proc in SKIP_LARGE: continue
|
if BITS > 1_200 and test_proc in SKIP_LARGE: continue
|
||||||
if BITS > 4_096 and test_proc in SKIP_LARGEST: continue
|
if BITS > 4_096 and test_proc in SKIP_LARGEST: continue
|
||||||
|
|
||||||
count_pass = 0
|
count = 0
|
||||||
count_fail = 0
|
|
||||||
TIMINGS = {}
|
|
||||||
|
|
||||||
UNTIL_ITERS = ITERATIONS
|
UNTIL_ITERS = ITERATIONS
|
||||||
if test_proc == test_root_n and BITS == 1_200:
|
if test_proc == test_root_n and BITS == 1_200:
|
||||||
@@ -747,30 +634,15 @@ if __name__ == '__main__':
|
|||||||
else:
|
else:
|
||||||
b = randint(0, 1 << BITS)
|
b = randint(0, 1 << BITS)
|
||||||
|
|
||||||
res = None
|
count += 1
|
||||||
|
test_proc(a, b)
|
||||||
|
|
||||||
start = time.perf_counter()
|
print(fmt_string.format(name=test_proc.__name__, test_count=count))
|
||||||
res = test_proc(a, b)
|
|
||||||
diff = time.perf_counter() - start
|
|
||||||
|
|
||||||
TOTAL_TIME += diff
|
|
||||||
|
|
||||||
if test_proc not in TIMINGS:
|
|
||||||
TIMINGS[test_proc] = diff
|
|
||||||
else:
|
|
||||||
TIMINGS[test_proc] += diff
|
|
||||||
|
|
||||||
if res:
|
|
||||||
count_pass += 1; total_passes += 1
|
|
||||||
else:
|
|
||||||
count_fail += 1; total_failures += 1
|
|
||||||
|
|
||||||
print(fmt_string.format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000))
|
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print("---- THE END ----")
|
print("---- THE END ----")
|
||||||
print()
|
|
||||||
print(fmt_string.format(name="total", count_pass=total_passes, count_fail=total_failures, timing=TOTAL_TIME * 1_000))
|
vectors.write("}")
|
||||||
|
|
||||||
if total_failures:
|
if total_failures:
|
||||||
exit(1)
|
exit(1)
|
||||||
@@ -1,362 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
|
||||||
Made available under Odin's BSD-3 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 exports procedures for use with the test.py test suite.
|
|
||||||
*/
|
|
||||||
package test_core_math_big
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: Write tests for `internal_*` and test reusing parameters with the public implementations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "base:runtime"
|
|
||||||
import "core:strings"
|
|
||||||
import "core:math/big"
|
|
||||||
|
|
||||||
PyRes :: struct {
|
|
||||||
res: cstring,
|
|
||||||
err: big.Error,
|
|
||||||
}
|
|
||||||
|
|
||||||
print_to_buffer :: proc(val: ^big.Int) -> cstring {
|
|
||||||
context = runtime.default_context()
|
|
||||||
r, _ := big.int_itoa_cstring(val, 16, context.allocator)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
@export test_initialize_constants :: proc "c" () -> (res: u64) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
_ = big.initialize_constants()
|
|
||||||
|
|
||||||
return u64(big._DIGIT_NAILS)
|
|
||||||
}
|
|
||||||
|
|
||||||
@export test_error_string :: proc "c" (err: big.Error) -> (res: cstring) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
es := big.Error_String
|
|
||||||
return strings.clone_to_cstring(es[err], context.allocator)
|
|
||||||
}
|
|
||||||
|
|
||||||
@export test_add :: proc "c" (a, b: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
aa, bb, sum := &big.Int{}, &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(aa, bb, sum)
|
|
||||||
|
|
||||||
if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err} }
|
|
||||||
if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err} }
|
|
||||||
if bb.used == 1 {
|
|
||||||
if err = #force_inline big.internal_add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err} }
|
|
||||||
} else {
|
|
||||||
if err = #force_inline big.internal_add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err} }
|
|
||||||
}
|
|
||||||
|
|
||||||
r := print_to_buffer(sum)
|
|
||||||
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
@export test_sub :: proc "c" (a, b: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
aa, bb, sum := &big.Int{}, &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(aa, bb, sum)
|
|
||||||
|
|
||||||
if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err} }
|
|
||||||
if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err} }
|
|
||||||
if bb.used == 1 {
|
|
||||||
if err = #force_inline big.internal_sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err} }
|
|
||||||
} else {
|
|
||||||
if err = #force_inline big.internal_sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err} }
|
|
||||||
}
|
|
||||||
|
|
||||||
r := print_to_buffer(sum)
|
|
||||||
if err != nil { return PyRes{res=":sub:itoa(sum):", err=err} }
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
@export test_mul :: proc "c" (a, b: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
aa, bb, product := &big.Int{}, &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(aa, bb, product)
|
|
||||||
|
|
||||||
if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err} }
|
|
||||||
if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err} }
|
|
||||||
if err = #force_inline big.internal_mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(product)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
@export test_sqr :: proc "c" (a: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
aa, square := &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(aa, square)
|
|
||||||
|
|
||||||
if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":sqr:atoi(a):", err=err} }
|
|
||||||
if err = #force_inline big.internal_sqr(square, aa); err != nil { return PyRes{res=":sqr:sqr(square,a):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(square)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient.
|
|
||||||
*/
|
|
||||||
@export test_div :: proc "c" (a, b: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
aa, bb, quotient := &big.Int{}, &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(aa, bb, quotient)
|
|
||||||
|
|
||||||
if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err} }
|
|
||||||
if err = big.atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err} }
|
|
||||||
if err = #force_inline big.internal_div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(quotient)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
res = log(a, base)
|
|
||||||
*/
|
|
||||||
@export test_log :: proc "c" (a: cstring, base := big.DIGIT(2)) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
l: int
|
|
||||||
|
|
||||||
aa := &big.Int{}
|
|
||||||
defer big.internal_destroy(aa)
|
|
||||||
|
|
||||||
if err = big.atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err} }
|
|
||||||
if l, err = #force_inline big.internal_log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err} }
|
|
||||||
|
|
||||||
#force_inline big.internal_zero(aa)
|
|
||||||
aa.digit[0] = big.DIGIT(l) & big._MASK
|
|
||||||
aa.digit[1] = big.DIGIT(l) >> big._DIGIT_BITS
|
|
||||||
aa.used = 2
|
|
||||||
big.clamp(aa)
|
|
||||||
|
|
||||||
r := print_to_buffer(aa)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = base^power
|
|
||||||
*/
|
|
||||||
@export test_pow :: proc "c" (base: cstring, power := int(2)) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
dest, bb := &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(dest, bb)
|
|
||||||
|
|
||||||
if err = big.atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err} }
|
|
||||||
if err = #force_inline big.internal_pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(dest)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = sqrt(src)
|
|
||||||
*/
|
|
||||||
@export test_sqrt :: proc "c" (source: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
src := &big.Int{}
|
|
||||||
defer big.internal_destroy(src)
|
|
||||||
|
|
||||||
if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err} }
|
|
||||||
if err = #force_inline big.internal_sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(src)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = root_n(src, power)
|
|
||||||
*/
|
|
||||||
@export test_root_n :: proc "c" (source: cstring, power: int) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
src := &big.Int{}
|
|
||||||
defer big.internal_destroy(src)
|
|
||||||
|
|
||||||
if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err} }
|
|
||||||
if err = #force_inline big.internal_root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(src)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = shr_digit(src, digits)
|
|
||||||
*/
|
|
||||||
@export test_shr_leg :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
src := &big.Int{}
|
|
||||||
defer big.internal_destroy(src)
|
|
||||||
|
|
||||||
if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err} }
|
|
||||||
if err = #force_inline big._private_int_shr_leg(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(src)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = shl_digit(src, digits)
|
|
||||||
*/
|
|
||||||
@export test_shl_leg :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
src := &big.Int{}
|
|
||||||
defer big.internal_destroy(src)
|
|
||||||
|
|
||||||
if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err} }
|
|
||||||
if err = #force_inline big._private_int_shl_leg(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(src)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = shr(src, bits)
|
|
||||||
*/
|
|
||||||
@export test_shr :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
src := &big.Int{}
|
|
||||||
defer big.internal_destroy(src)
|
|
||||||
|
|
||||||
if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err} }
|
|
||||||
if err = #force_inline big.internal_shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(src)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = shr_signed(src, bits)
|
|
||||||
*/
|
|
||||||
@export test_shr_signed :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
src := &big.Int{}
|
|
||||||
defer big.internal_destroy(src)
|
|
||||||
|
|
||||||
if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err} }
|
|
||||||
if err = #force_inline big.internal_shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(src)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = shl(src, bits)
|
|
||||||
*/
|
|
||||||
@export test_shl :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
src := &big.Int{}
|
|
||||||
defer big.internal_destroy(src)
|
|
||||||
|
|
||||||
if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err} }
|
|
||||||
if err = #force_inline big.internal_shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(src)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = factorial(n)
|
|
||||||
*/
|
|
||||||
@export test_factorial :: proc "c" (n: int) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
dest := &big.Int{}
|
|
||||||
defer big.internal_destroy(dest)
|
|
||||||
|
|
||||||
if err = #force_inline big.internal_int_factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(dest)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = gcd(a, b)
|
|
||||||
*/
|
|
||||||
@export test_gcd :: proc "c" (a, b: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
ai, bi, dest := &big.Int{}, &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(ai, bi, dest)
|
|
||||||
|
|
||||||
if err = big.atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err} }
|
|
||||||
if err = big.atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err} }
|
|
||||||
if err = #force_inline big.internal_int_gcd_lcm(dest, nil, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(dest)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = lcm(a, b)
|
|
||||||
*/
|
|
||||||
@export test_lcm :: proc "c" (a, b: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
|
|
||||||
ai, bi, dest := &big.Int{}, &big.Int{}, &big.Int{}
|
|
||||||
defer big.internal_destroy(ai, bi, dest)
|
|
||||||
|
|
||||||
if err = big.atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err} }
|
|
||||||
if err = big.atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err} }
|
|
||||||
if err = #force_inline big.internal_int_gcd_lcm(nil, dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err} }
|
|
||||||
|
|
||||||
r := print_to_buffer(dest)
|
|
||||||
return PyRes{res = r, err = nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dest = lcm(a, b)
|
|
||||||
*/
|
|
||||||
@export test_is_square :: proc "c" (a: cstring) -> (res: PyRes) {
|
|
||||||
context = runtime.default_context()
|
|
||||||
err: big.Error
|
|
||||||
square: bool
|
|
||||||
|
|
||||||
ai := &big.Int{}
|
|
||||||
defer big.internal_destroy(ai)
|
|
||||||
|
|
||||||
if err = big.atoi(ai, string(a), 16); err != nil { return PyRes{res=":is_square:atoi(a):", err=err} }
|
|
||||||
if square, err = #force_inline big.internal_int_is_square(ai); err != nil { return PyRes{res=":is_square:is_square(a):", err=err} }
|
|
||||||
|
|
||||||
if square {
|
|
||||||
return PyRes{"True", nil}
|
|
||||||
}
|
|
||||||
return PyRes{"False", nil}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package test_core_math_big
|
package test_core_math_big
|
||||||
|
|
||||||
import "core:math/big"
|
import "core:math/big"
|
||||||
|
import "core:strconv"
|
||||||
import "core:testing"
|
import "core:testing"
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
@@ -83,3 +84,186 @@ test_rational_to_float :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import "core:log"
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_big_math_vectors :: proc(t: ^testing.T) {
|
||||||
|
for vec in big_test_vectors {
|
||||||
|
a, b, res, expected := &big.Int{}, &big.Int{}, &big.Int{}, &big.Int{}
|
||||||
|
defer big.destroy(a, b, res, expected)
|
||||||
|
|
||||||
|
atoi(t, a, vec.a) or_continue
|
||||||
|
atoi(t, b, vec.b) or_continue
|
||||||
|
atoi(t, expected, vec.exp) or_continue
|
||||||
|
|
||||||
|
#partial switch vec.op {
|
||||||
|
case .Add:
|
||||||
|
err := big.add(res, a, b)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected add(%v, %v) to be %v, got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Sub:
|
||||||
|
err := big.sub(res, a, b)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected sub(%v, %v) to be %v, got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Mul:
|
||||||
|
err := big.mul(res, a, b)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected mul(%v, %v) to be %v, got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Div:
|
||||||
|
err := big.div(res, a, b)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected div(%v, %v) to be %v, got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Sqr:
|
||||||
|
err := big.sqr(res, a)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_a(t, "Expected sqr(%v) to be %v, got %v", a, expected, res, err)
|
||||||
|
|
||||||
|
case .Log:
|
||||||
|
base, base_ok := strconv.parse_i64_of_base(vec.b, 16)
|
||||||
|
testing.expect(t, base_ok == true)
|
||||||
|
|
||||||
|
log_res, err := big.log(a, big.DIGIT(base))
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
big.set(res, log_res)
|
||||||
|
expect_ab(t, "Expected log(%v, %v) to be %v, got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Sqrt:
|
||||||
|
err := big.sqrt(res, a)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_a(t, "Expected sqrt(%v) to be %v, got %v", a, expected, res, err)
|
||||||
|
|
||||||
|
case .Pow:
|
||||||
|
power, power_ok := strconv.parse_i64_of_base(vec.b, 16)
|
||||||
|
testing.expect(t, power_ok == true)
|
||||||
|
|
||||||
|
err := big.pow(res, a, int(power))
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected pow(%v, %v) to be '%v', got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Root:
|
||||||
|
n, n_ok := strconv.parse_i64_of_base(vec.b, 16)
|
||||||
|
testing.expect(t, n_ok == true)
|
||||||
|
|
||||||
|
err := big.root_n(res, a, int(n))
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected root_n(%v, %v) to be '%v', got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Shl:
|
||||||
|
bits, bits_ok := strconv.parse_i64_of_base(vec.b, 16)
|
||||||
|
testing.expect(t, bits_ok == true)
|
||||||
|
|
||||||
|
err := big.internal_int_shl(res, a, int(bits))
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected internal_int_shl(%v, %v) to be '%v', got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Shr:
|
||||||
|
bits, bits_ok := strconv.parse_i64_of_base(vec.b, 16)
|
||||||
|
testing.expect(t, bits_ok == true)
|
||||||
|
|
||||||
|
err := big.internal_int_shr(res, a, int(bits))
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected internal_int_shr(%v, %v) to be '%v', got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Shr_Signed:
|
||||||
|
bits, bits_ok := strconv.parse_i64_of_base(vec.b, 16)
|
||||||
|
testing.expect(t, bits_ok == true)
|
||||||
|
|
||||||
|
big.set(res, a)
|
||||||
|
err := big.internal_int_shr_signed(res, res, int(bits))
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected internal_int_shr_signed(%v, %v) to be '%v', got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Factorial:
|
||||||
|
n, n_ok := strconv.parse_i64_of_base(vec.a, 16)
|
||||||
|
testing.expect(t, n_ok == true)
|
||||||
|
|
||||||
|
err := big.factorial(res, int(n))
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_a(t, "Expected factorial(%v) to be '%v', got %v", a, expected, res, err)
|
||||||
|
|
||||||
|
case .Gcd:
|
||||||
|
err := big.internal_int_gcd_lcm(res, nil, a, b)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected gcd(%v, %v) to be '%v', got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Lcm:
|
||||||
|
err := big.internal_int_gcd_lcm(nil, res, a, b)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
expect_ab(t, "Expected lcm(%v, %v) to be '%v', got %v", a, b, expected, res, err)
|
||||||
|
|
||||||
|
case .Is_Square:
|
||||||
|
square, err := big.internal_int_is_square(a)
|
||||||
|
testing.expect(t, err == vec.err)
|
||||||
|
|
||||||
|
big.set(res, 1 if square else 0)
|
||||||
|
expect_a(t, "Expected is_square(%v) to be '%v', got %v", a, expected, res, err)
|
||||||
|
|
||||||
|
case:
|
||||||
|
log.assertf(false, "Unhandled op: %v", vec.op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_a :: proc(t: ^testing.T, format: string, a, expected, res: ^big.Int, err: big.Error, loc := #caller_location) {
|
||||||
|
if err != .Okay { return }
|
||||||
|
|
||||||
|
equal, _ := big.equals(res, expected)
|
||||||
|
if !equal {
|
||||||
|
as, _ := big.itoa(a)
|
||||||
|
rs, _ := big.itoa(res)
|
||||||
|
es, _ := big.itoa(expected)
|
||||||
|
|
||||||
|
defer delete(as)
|
||||||
|
defer delete(rs)
|
||||||
|
defer delete(es)
|
||||||
|
|
||||||
|
testing.expectf(t, equal, format, as, es, rs, loc=loc)
|
||||||
|
assert(equal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_ab :: proc(t: ^testing.T, format: string, a, b, expected, res: ^big.Int, err: big.Error, loc := #caller_location) {
|
||||||
|
if err != .Okay { return }
|
||||||
|
|
||||||
|
equal, _ := big.equals(res, expected)
|
||||||
|
if !equal {
|
||||||
|
as, _ := big.itoa(a)
|
||||||
|
bs, _ := big.itoa(b)
|
||||||
|
rs, _ := big.itoa(res)
|
||||||
|
es, _ := big.itoa(expected)
|
||||||
|
|
||||||
|
defer delete(as)
|
||||||
|
defer delete(bs)
|
||||||
|
defer delete(rs)
|
||||||
|
defer delete(es)
|
||||||
|
|
||||||
|
testing.expectf(t, equal, format, as, bs, es, rs, loc=loc)
|
||||||
|
assert(equal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
atoi :: proc(t: ^testing.T, i: ^big.Int, a: string, loc := #caller_location) -> bool {
|
||||||
|
err := big.atoi(i, a, 16)
|
||||||
|
testing.expect(t, err == .Okay, loc=loc)
|
||||||
|
return err == .Okay
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -4,3 +4,4 @@ package tests_core
|
|||||||
@(require) import "crypto"
|
@(require) import "crypto"
|
||||||
@(require) import "hash"
|
@(require) import "hash"
|
||||||
@(require) import "image"
|
@(require) import "image"
|
||||||
|
@(require) import "math/big"
|
||||||
Reference in New Issue
Block a user