2022-11-06 21:49:02 -05:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
2023-04-02 21:25:04 -04:00
|
|
|
from math import ceil, copysign, cos, floor, log, sin, sqrt, tan
|
2022-11-06 21:49:02 -05:00
|
|
|
from os import environ
|
|
|
|
from random import randint
|
|
|
|
from subprocess import Popen, PIPE, run
|
2023-11-05 21:06:22 -05:00
|
|
|
from sys import argv, exit
|
2022-11-06 21:49:02 -05:00
|
|
|
|
2022-11-07 14:52:29 -05:00
|
|
|
def tosigned(x):
|
|
|
|
return x if x < 32768 else x - 65536
|
|
|
|
|
2022-11-06 21:49:02 -05:00
|
|
|
u8 = {'sz': 1 << 8, 'fmt': b'%02x'}
|
2023-12-05 21:54:37 -05:00
|
|
|
x16 = {'sz': 1 << 16, 'fmt': b'%04x'}
|
2023-11-05 20:17:26 -05:00
|
|
|
z16 = {'sz': 1 << 16, 'fmt': b'%04x'} # non-zero
|
|
|
|
p16 = {'sz': 1 << 16, 'fmt': b'%04x'} # positive
|
|
|
|
t16 = {'sz': 1 << 16, 'fmt': b'%04x'} # tangent, must not be pi/2
|
2022-11-06 21:49:02 -05:00
|
|
|
|
|
|
|
def eq(got, expected):
|
|
|
|
return got == expected
|
|
|
|
def booleq(got, expected):
|
|
|
|
return bool(got) == bool(expected)
|
2022-11-07 14:52:29 -05:00
|
|
|
def releq(got0, expected0):
|
|
|
|
got, expected = tosigned(got0), tosigned(expected0)
|
2022-11-06 21:49:02 -05:00
|
|
|
if (expected - 1) <= got and got <= (expected + 1):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
error = abs(got - expected) / (abs(expected) + 0.001)
|
|
|
|
return error < 0.01
|
2023-11-05 20:17:26 -05:00
|
|
|
def sineq(got0, expected0):
|
2022-11-07 14:52:29 -05:00
|
|
|
got, expected = tosigned(got0), tosigned(expected0)
|
|
|
|
if (expected - 10) <= got and got <= (expected + 10):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
def taneq(got0, expected0):
|
|
|
|
got, expected = tosigned(got0), tosigned(expected0)
|
|
|
|
if (expected - 1) <= got and got <= (expected + 1):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
error = abs(got - expected) / (abs(expected) + 0.001)
|
|
|
|
return error < 0.1
|
2022-11-06 21:49:02 -05:00
|
|
|
|
|
|
|
def testcase(p, sym, args, out, f, eq):
|
|
|
|
vals = []
|
|
|
|
for name, g in args:
|
|
|
|
val = randint(0, g['sz'] - 1)
|
2022-11-07 14:52:29 -05:00
|
|
|
while ((val == 0 and (g is z16 or g is p16)) or
|
|
|
|
(val >= 0x8000 and g is p16) or
|
2023-12-05 21:54:37 -05:00
|
|
|
(val == 0x8000 and g is x16) or
|
2022-11-07 14:52:29 -05:00
|
|
|
(g is t16 and ((val >= 804) or ((val % 804) == 402)))):
|
2022-11-06 21:49:02 -05:00
|
|
|
val = randint(0, g['sz'] - 1)
|
|
|
|
vals.append((name, g, val))
|
|
|
|
p.stdin.write(sym)
|
|
|
|
for _, g, x in vals:
|
|
|
|
p.stdin.write(g['fmt'] % x)
|
|
|
|
p.stdin.write(b'\n')
|
|
|
|
p.stdin.flush()
|
|
|
|
got = int(p.stdout.readline().strip().decode('utf-8'), 16)
|
|
|
|
xs = [x for _, _, x in vals]
|
|
|
|
z = f(*xs)
|
|
|
|
expected = z
|
|
|
|
if eq(got, expected):
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
res = {'got': got, 'expected': expected}
|
|
|
|
for name, _, x in vals:
|
|
|
|
res[name] = x
|
|
|
|
return res
|
|
|
|
|
|
|
|
def test(p, trials, sym, args, out, f, eq=eq):
|
|
|
|
fails = 0
|
|
|
|
cases = []
|
|
|
|
for i in range(0, trials):
|
|
|
|
case1 = testcase(p, sym, args, out, f, eq)
|
|
|
|
if case1 is not None:
|
|
|
|
fails += 1
|
|
|
|
cases.append(case1)
|
|
|
|
name = sym.decode('utf-8')
|
|
|
|
if fails == 0:
|
|
|
|
print('%s passed %d trials' % (name, trials))
|
|
|
|
else:
|
|
|
|
print('%s failed %d/%d trials (%r)' % (name, fails, trials, cases))
|
|
|
|
|
2023-11-05 20:17:26 -05:00
|
|
|
def fromfix(n):
|
|
|
|
assert 0 <= n and n <= 65535
|
|
|
|
if n >= 32768:
|
|
|
|
res = (n - 65536) / 256
|
|
|
|
else:
|
|
|
|
res = n / 256
|
|
|
|
return res
|
|
|
|
|
|
|
|
bound = 32767 / 256
|
|
|
|
|
|
|
|
def tofix(x):
|
|
|
|
y = min(max(x, -bound), bound)
|
|
|
|
if y < 0:
|
|
|
|
res = int(ceil(65536 + y * 256))
|
|
|
|
else:
|
|
|
|
res = int(y * 256)
|
|
|
|
return res % 65536
|
|
|
|
|
2022-11-06 21:49:02 -05:00
|
|
|
def pipe():
|
|
|
|
return Popen(['uxncli', 'run.rom'], stdin=PIPE, stdout=PIPE)
|
|
|
|
|
|
|
|
def x16_add(x, y):
|
|
|
|
return (x + y) % 65536
|
|
|
|
def x16_sub(x, y):
|
|
|
|
return (x - y) % 65536
|
|
|
|
def x16_mul(x, y):
|
2023-11-05 20:17:26 -05:00
|
|
|
return tofix(fromfix(x) * fromfix(y))
|
2022-11-06 21:49:02 -05:00
|
|
|
def x16_div(x, y):
|
2023-11-05 20:17:26 -05:00
|
|
|
return tofix(fromfix(x) / fromfix(y))
|
2022-11-06 21:49:02 -05:00
|
|
|
def x16_quot(x, y):
|
2023-11-05 20:17:26 -05:00
|
|
|
n = x16_div(x, y)
|
|
|
|
if n < 0x7fff:
|
|
|
|
return n & 0xff00
|
|
|
|
elif n > 0x8001:
|
|
|
|
return (n + 255) & 0xff00
|
|
|
|
else:
|
|
|
|
return n
|
2022-11-06 21:49:02 -05:00
|
|
|
def x16_rem(x, y):
|
|
|
|
return x % y
|
|
|
|
def x16_is_whole(x):
|
|
|
|
return int((x & 0xff) == 0)
|
|
|
|
def x16_negate(x):
|
|
|
|
if x == 32768 or x == 0:
|
|
|
|
return x
|
|
|
|
else:
|
|
|
|
return 65536 - x
|
|
|
|
def x16_eq(x, y):
|
|
|
|
return x == y
|
|
|
|
def x16_ne(x, y):
|
|
|
|
return x != y
|
|
|
|
def x16_lt(x, y):
|
|
|
|
return int(tosigned(x) < tosigned(y))
|
|
|
|
def x16_gt(x, y):
|
|
|
|
return int(tosigned(x) > tosigned(y))
|
|
|
|
def x16_lteq(x, y):
|
|
|
|
return int(tosigned(x) <= tosigned(y))
|
|
|
|
def x16_gteq(x, y):
|
|
|
|
return int(tosigned(x) >= tosigned(y))
|
2023-04-02 21:25:04 -04:00
|
|
|
def x16_sqrt(x):
|
|
|
|
return int(sqrt(x / 256) * 256)
|
2022-11-07 14:52:29 -05:00
|
|
|
def x16_sin(x):
|
|
|
|
z = round(sin(x / 256) * 256)
|
|
|
|
return z if z >= 0 else 65536 + z
|
|
|
|
def x16_cos(x):
|
|
|
|
z = round(cos(x / 256) * 256)
|
|
|
|
return z if z >= 0 else 65536 + z
|
|
|
|
def x16_tan(x):
|
|
|
|
z0 = round(tan(x / 256) * 256)
|
|
|
|
z = min(max(z0, -0x7fff), 0x7fff)
|
|
|
|
return z if z >= 0 else 65536 + z
|
|
|
|
def x16_log(x):
|
|
|
|
z = round(log(x / 256) * 256)
|
|
|
|
return z if z >= 0 else 65536 + z
|
2022-11-09 11:48:55 -05:00
|
|
|
def x16_floor(x):
|
|
|
|
return floor(x / 256) * 256
|
|
|
|
def x16_ceil(x):
|
|
|
|
return (ceil(x / 256) * 256) % 65536
|
|
|
|
def x16_round(x):
|
|
|
|
return (round(x / 256) * 256) % 65536
|
|
|
|
def x16_trunc16(x):
|
|
|
|
r = tosigned(x) / 256
|
|
|
|
if r < 0:
|
|
|
|
return (65536 + ceil(r)) % 65536
|
|
|
|
else:
|
|
|
|
return floor(x / 256)
|
|
|
|
def x16_trunc8(x):
|
|
|
|
if tosigned(x) < 0:
|
|
|
|
return ceil(x / 256) % 256
|
|
|
|
else:
|
|
|
|
return floor(x / 256)
|
2022-11-06 21:49:02 -05:00
|
|
|
|
|
|
|
def main():
|
2023-11-05 20:17:26 -05:00
|
|
|
trials = int(argv[1]) if argv[1:] else 100
|
2023-11-05 21:06:22 -05:00
|
|
|
e = run(['uxnasm', 'test-fix16.tal', 'run.rom'])
|
|
|
|
if e.returncode != 0:
|
|
|
|
print('the command `uxnasm test-fix16.tal run.rom` failed!')
|
|
|
|
exit(e.returncode)
|
2022-11-06 21:49:02 -05:00
|
|
|
p = pipe()
|
2023-12-05 21:54:37 -05:00
|
|
|
test(p, trials, b'+', [('x', x16), ('y', x16)], x16, x16_add)
|
|
|
|
test(p, trials, b'-', [('x', x16), ('y', x16)], x16, x16_sub)
|
|
|
|
test(p, trials, b'*', [('x', x16), ('y', x16)], x16, x16_mul)
|
|
|
|
test(p, trials, b'/', [('x', x16), ('y', z16)], x16, x16_div)
|
|
|
|
test(p, trials, b'\\', [('x', x16), ('y', z16)], x16, x16_quot)
|
|
|
|
test(p, trials, b'%', [('x', x16), ('y', z16)], x16, x16_rem)
|
|
|
|
test(p, trials, b'w', [('x', x16)], u8, x16_is_whole, eq=booleq)
|
|
|
|
test(p, trials, b'N', [('x', x16)], x16, x16_negate)
|
|
|
|
test(p, trials, b'=', [('x', x16), ('y', x16)], u8, x16_eq)
|
|
|
|
test(p, trials, b'!', [('x', x16), ('y', x16)], u8, x16_ne)
|
|
|
|
test(p, trials, b'<', [('x', x16), ('y', x16)], u8, x16_lt)
|
|
|
|
test(p, trials, b'>', [('x', x16), ('y', x16)], u8, x16_gt)
|
|
|
|
test(p, trials, b'{', [('x', x16), ('y', x16)], u8, x16_lteq)
|
|
|
|
test(p, trials, b'}', [('x', x16), ('y', x16)], u8, x16_gteq)
|
|
|
|
test(p, trials, b'F', [('x', x16)], x16, x16_floor)
|
|
|
|
test(p, trials, b'C', [('x', x16)], x16, x16_ceil)
|
|
|
|
test(p, trials, b'R', [('x', x16)], x16, x16_round)
|
|
|
|
test(p, trials, b'8', [('x', x16)], x16, x16_trunc8)
|
|
|
|
test(p, trials, b'T', [('x', x16)], x16, x16_trunc16)
|
2023-11-05 20:17:26 -05:00
|
|
|
# the next five are known to be somewhat inaccurate and use
|
|
|
|
# a "relaxed" equality predicate for testing purposes.
|
2023-12-05 21:54:37 -05:00
|
|
|
test(p, trials, b'r', [('x', p16)], x16, x16_sqrt, eq=releq)
|
|
|
|
test(p, trials, b's', [('x', p16)], x16, x16_sin, eq=sineq)
|
|
|
|
test(p, trials, b'c', [('x', p16)], x16, x16_cos, eq=sineq)
|
|
|
|
test(p, trials, b't', [('x', t16)], x16, x16_tan, eq=taneq)
|
|
|
|
test(p, trials, b'l', [('x', p16)], x16, x16_log, eq=releq)
|
2022-11-06 21:49:02 -05:00
|
|
|
p.stdin.write(b'\n\n')
|
|
|
|
p.stdin.flush()
|
|
|
|
p.stdin.close()
|
|
|
|
p.stdout.close()
|
|
|
|
p.kill()
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|