560 lines
17 KiB
Python
560 lines
17 KiB
Python
#!/usr/bin/env python
|
|
|
|
#
|
|
# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
#
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
#
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
# SUCH DAMAGE.
|
|
#
|
|
|
|
|
|
# Generate test cases for deccheck.py.
|
|
|
|
|
|
#
|
|
# Grammar from http://speleotrove.com/decimal/daconvs.html
|
|
#
|
|
# sign ::= '+' | '-'
|
|
# digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
|
|
# '8' | '9'
|
|
# indicator ::= 'e' | 'E'
|
|
# digits ::= digit [digit]...
|
|
# decimal-part ::= digits '.' [digits] | ['.'] digits
|
|
# exponent-part ::= indicator [sign] digits
|
|
# infinity ::= 'Infinity' | 'Inf'
|
|
# nan ::= 'NaN' [digits] | 'sNaN' [digits]
|
|
# numeric-value ::= decimal-part [exponent-part] | infinity
|
|
# numeric-string ::= [sign] numeric-value | [sign] nan
|
|
#
|
|
|
|
|
|
from random import randrange, sample
|
|
from fractions import Fraction
|
|
from randfloat import un_randfloat, bin_randfloat, tern_randfloat
|
|
|
|
|
|
def sign():
|
|
if randrange(2):
|
|
if randrange(2): return '+'
|
|
return ''
|
|
return '-'
|
|
|
|
def indicator():
|
|
return "eE"[randrange(2)]
|
|
|
|
def digits(maxprec):
|
|
if maxprec == 0: return ''
|
|
return str(randrange(10**maxprec))
|
|
|
|
def dot():
|
|
if randrange(2): return '.'
|
|
return ''
|
|
|
|
def decimal_part(maxprec):
|
|
if randrange(100) > 60: # integers
|
|
return digits(maxprec)
|
|
if randrange(2):
|
|
intlen = randrange(1, maxprec+1)
|
|
fraclen = maxprec-intlen
|
|
intpart = digits(intlen)
|
|
fracpart = digits(fraclen)
|
|
return ''.join((intpart, '.', fracpart))
|
|
else:
|
|
return ''.join((dot(), digits(maxprec)))
|
|
|
|
def expdigits(maxexp):
|
|
return str(randrange(maxexp))
|
|
|
|
def exponent_part(maxexp):
|
|
return ''.join((indicator(), sign(), expdigits(maxexp)))
|
|
|
|
def infinity():
|
|
if randrange(2): return 'Infinity'
|
|
return 'Inf'
|
|
|
|
def nan():
|
|
d = ''
|
|
if randrange(2):
|
|
d = digits(randrange(99))
|
|
if randrange(2):
|
|
return ''.join(('NaN', d))
|
|
else:
|
|
return ''.join(('sNaN', d))
|
|
|
|
def numeric_value(maxprec, maxexp):
|
|
if randrange(100) > 90:
|
|
return infinity()
|
|
exp_part = ''
|
|
if randrange(100) > 60:
|
|
exp_part = exponent_part(maxexp)
|
|
return ''.join((decimal_part(maxprec), exp_part))
|
|
|
|
def numeric_string(maxprec, maxexp):
|
|
if randrange(100) > 95:
|
|
return ''.join((sign(), nan()))
|
|
else:
|
|
return ''.join((sign(), numeric_value(maxprec, maxexp)))
|
|
|
|
def randdec(maxprec, maxexp):
|
|
return numeric_string(maxprec, maxexp)
|
|
|
|
def rand_adjexp(maxprec, maxadjexp):
|
|
d = digits(maxprec)
|
|
maxexp = maxadjexp-len(d)+1
|
|
if maxexp == 0: maxexp = 1
|
|
exp = str(randrange(maxexp-2*(abs(maxexp)), maxexp))
|
|
return ''.join((sign(), d, 'E', exp))
|
|
|
|
|
|
def ndigits(n):
|
|
if n < 1: return 0
|
|
return randrange(10**(n-1), 10**n)
|
|
|
|
def randtuple(maxprec, maxexp):
|
|
n = randrange(100)
|
|
sign = randrange(2)
|
|
coeff = ndigits(maxprec)
|
|
if n >= 95:
|
|
coeff = ()
|
|
exp = 'F'
|
|
elif n >= 85:
|
|
coeff = tuple(map(int, str(ndigits(maxprec))))
|
|
exp = "nN"[randrange(2)]
|
|
else:
|
|
coeff = tuple(map(int, str(ndigits(maxprec))))
|
|
exp = randrange(-maxexp, maxexp)
|
|
return (sign, coeff, exp)
|
|
|
|
def from_triple(sign, coeff, exp):
|
|
return ''.join((str(sign*coeff), indicator(), str(exp)))
|
|
|
|
|
|
# Close to 10**n
|
|
def un_close_to_pow10(prec, maxexp, itr=None):
|
|
if itr is None:
|
|
lst = range(prec+30)
|
|
else:
|
|
lst = sample(range(prec+30), itr)
|
|
nines = [10**n - 1 for n in lst]
|
|
pow10 = [10**n for n in lst]
|
|
for coeff in nines:
|
|
yield coeff
|
|
yield -coeff
|
|
yield from_triple(1, coeff, randrange(2*maxexp))
|
|
yield from_triple(-1, coeff, randrange(2*maxexp))
|
|
for coeff in pow10:
|
|
yield coeff
|
|
yield -coeff
|
|
|
|
# Close to 10**n
|
|
def bin_close_to_pow10(prec, maxexp, itr=None):
|
|
if itr is None:
|
|
lst = range(prec+30)
|
|
else:
|
|
lst = sample(range(prec+30), itr)
|
|
nines = [10**n - 1 for n in lst]
|
|
pow10 = [10**n for n in lst]
|
|
for coeff in nines:
|
|
yield coeff, 1
|
|
yield -coeff, -1
|
|
yield 1, coeff
|
|
yield -1, -coeff
|
|
yield from_triple(1, coeff, randrange(2*maxexp)), 1
|
|
yield from_triple(-1, coeff, randrange(2*maxexp)), -1
|
|
yield 1, from_triple(1, coeff, -randrange(2*maxexp))
|
|
yield -1, from_triple(-1, coeff, -randrange(2*maxexp))
|
|
for coeff in pow10:
|
|
yield coeff, -1
|
|
yield -coeff, 1
|
|
yield 1, -coeff
|
|
yield -coeff, 1
|
|
|
|
# Close to 1:
|
|
def close_to_one_greater(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("1.", '0'*randrange(prec),
|
|
str(randrange(rprec))))
|
|
|
|
def close_to_one_less(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("0.9", '9'*randrange(prec),
|
|
str(randrange(rprec))))
|
|
|
|
# Close to 0:
|
|
def close_to_zero_greater(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("0.", '0'*randrange(prec),
|
|
str(randrange(rprec))))
|
|
|
|
def close_to_zero_less(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("-0.", '0'*randrange(prec),
|
|
str(randrange(rprec))))
|
|
|
|
# Close to emax:
|
|
def close_to_emax_less(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("9.", '9'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(emax)))
|
|
|
|
def close_to_emax_greater(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("1.", '0'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(emax+1)))
|
|
|
|
# Close to emin:
|
|
def close_to_emin_greater(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("1.", '0'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(emin)))
|
|
|
|
def close_to_emin_less(prec, emax, emin):
|
|
rprec = 10**prec
|
|
return ''.join(("9.", '9'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(emin-1)))
|
|
|
|
# Close to etiny:
|
|
def close_to_etiny_greater(prec, emax, emin):
|
|
rprec = 10**prec
|
|
etiny = emin - (prec - 1)
|
|
return ''.join(("1.", '0'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(etiny)))
|
|
|
|
def close_to_etiny_less(prec, emax, emin):
|
|
rprec = 10**prec
|
|
etiny = emin - (prec - 1)
|
|
return ''.join(("9.", '9'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(etiny-1)))
|
|
|
|
|
|
def close_to_min_etiny_greater(prec, max_prec, min_emin):
|
|
rprec = 10**prec
|
|
etiny = min_emin - (max_prec - 1)
|
|
return ''.join(("1.", '0'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(etiny)))
|
|
|
|
def close_to_min_etiny_less(prec, max_prec, min_emin):
|
|
rprec = 10**prec
|
|
etiny = min_emin - (max_prec - 1)
|
|
return ''.join(("9.", '9'*randrange(prec),
|
|
str(randrange(rprec)), "E", str(etiny-1)))
|
|
|
|
|
|
close_funcs = [
|
|
close_to_one_greater, close_to_one_less, close_to_zero_greater,
|
|
close_to_zero_less, close_to_emax_less, close_to_emax_greater,
|
|
close_to_emin_greater, close_to_emin_less, close_to_etiny_greater,
|
|
close_to_etiny_less, close_to_min_etiny_greater, close_to_min_etiny_less
|
|
]
|
|
|
|
|
|
def un_close_numbers(prec, emax, emin, itr=None):
|
|
if itr is None:
|
|
itr = 1000
|
|
for _ in range(itr):
|
|
for func in close_funcs:
|
|
yield func(prec, emax, emin)
|
|
|
|
def bin_close_numbers(prec, emax, emin, itr=None):
|
|
if itr is None:
|
|
itr = 1000
|
|
for _ in range(itr):
|
|
for func1 in close_funcs:
|
|
for func2 in close_funcs:
|
|
yield func1(prec, emax, emin), func2(prec, emax, emin)
|
|
for func in close_funcs:
|
|
yield randdec(prec, emax), func(prec, emax, emin)
|
|
yield func(prec, emax, emin), randdec(prec, emax)
|
|
|
|
def tern_close_numbers(prec, emax, emin, itr):
|
|
if itr is None:
|
|
itr = 1000
|
|
for _ in range(itr):
|
|
for func1 in close_funcs:
|
|
for func2 in close_funcs:
|
|
for func3 in close_funcs:
|
|
yield (func1(prec, emax, emin), func2(prec, emax, emin),
|
|
func3(prec, emax, emin))
|
|
for func in close_funcs:
|
|
yield (randdec(prec, emax), func(prec, emax, emin),
|
|
func(prec, emax, emin))
|
|
yield (func(prec, emax, emin), randdec(prec, emax),
|
|
func(prec, emax, emin))
|
|
yield (func(prec, emax, emin), func(prec, emax, emin),
|
|
randdec(prec, emax))
|
|
for func in close_funcs:
|
|
yield (randdec(prec, emax), randdec(prec, emax),
|
|
func(prec, emax, emin))
|
|
yield (randdec(prec, emax), func(prec, emax, emin),
|
|
randdec(prec, emax))
|
|
yield (func(prec, emax, emin), randdec(prec, emax),
|
|
randdec(prec, emax))
|
|
|
|
|
|
# If itr == None, test all digit lengths up to prec + 30
|
|
def un_incr_digits(prec, maxexp, itr):
|
|
if itr is None:
|
|
lst = range(prec+30)
|
|
else:
|
|
lst = sample(range(prec+30), itr)
|
|
for m in lst:
|
|
yield from_triple(1, ndigits(m), 0)
|
|
yield from_triple(-1, ndigits(m), 0)
|
|
yield from_triple(1, ndigits(m), randrange(maxexp))
|
|
yield from_triple(-1, ndigits(m), randrange(maxexp))
|
|
|
|
# If itr == None, test all digit lengths up to prec + 30
|
|
# Also output decimals im tuple form.
|
|
def un_incr_digits_tuple(prec, maxexp, itr):
|
|
if itr is None:
|
|
lst = range(prec+30)
|
|
else:
|
|
lst = sample(range(prec+30), itr)
|
|
for m in lst:
|
|
yield from_triple(1, ndigits(m), 0)
|
|
yield from_triple(-1, ndigits(m), 0)
|
|
yield from_triple(1, ndigits(m), randrange(maxexp))
|
|
yield from_triple(-1, ndigits(m), randrange(maxexp))
|
|
# test from tuple
|
|
yield (0, tuple(map(int, str(ndigits(m)))), 0)
|
|
yield (1, tuple(map(int, str(ndigits(m)))), 0)
|
|
yield (0, tuple(map(int, str(ndigits(m)))), randrange(maxexp))
|
|
yield (1, tuple(map(int, str(ndigits(m)))), randrange(maxexp))
|
|
|
|
# If itr == None, test all combinations of digit lengths up to prec + 30
|
|
def bin_incr_digits(prec, maxexp, itr):
|
|
if itr is None:
|
|
lst1 = range(prec+30)
|
|
lst2 = range(prec+30)
|
|
else:
|
|
lst1 = sample(range(prec+30), itr)
|
|
lst2 = sample(range(prec+30), itr)
|
|
for m in lst1:
|
|
x = from_triple(1, ndigits(m), 0)
|
|
yield x, x
|
|
x = from_triple(-1, ndigits(m), 0)
|
|
yield x, x
|
|
x = from_triple(1, ndigits(m), randrange(maxexp))
|
|
yield x, x
|
|
x = from_triple(-1, ndigits(m), randrange(maxexp))
|
|
yield x, x
|
|
for m in lst1:
|
|
for n in lst2:
|
|
x = from_triple(1, ndigits(m), 0)
|
|
y = from_triple(1, ndigits(n), 0)
|
|
yield x, y
|
|
x = from_triple(-1, ndigits(m), 0)
|
|
y = from_triple(1, ndigits(n), 0)
|
|
yield x, y
|
|
x = from_triple(1, ndigits(m), 0)
|
|
y = from_triple(-1, ndigits(n), 0)
|
|
yield x, y
|
|
x = from_triple(-1, ndigits(m), 0)
|
|
y = from_triple(-1, ndigits(n), 0)
|
|
yield x, y
|
|
x = from_triple(1, ndigits(m), randrange(maxexp))
|
|
y = from_triple(1, ndigits(n), randrange(maxexp))
|
|
yield x, y
|
|
x = from_triple(-1, ndigits(m), randrange(maxexp))
|
|
y = from_triple(1, ndigits(n), randrange(maxexp))
|
|
yield x, y
|
|
x = from_triple(1, ndigits(m), randrange(maxexp))
|
|
y = from_triple(-1, ndigits(n), randrange(maxexp))
|
|
yield x, y
|
|
x = from_triple(-1, ndigits(m), randrange(maxexp))
|
|
y = from_triple(-1, ndigits(n), randrange(maxexp))
|
|
yield x, y
|
|
|
|
|
|
def randsign():
|
|
return (1, -1)[randrange(2)]
|
|
|
|
# If itr == None, test all combinations of digit lengths up to prec + 30
|
|
def tern_incr_digits(prec, maxexp, itr):
|
|
if itr is None:
|
|
lst1 = range(prec+30)
|
|
lst2 = range(prec+30)
|
|
lst3 = range(prec+30)
|
|
else:
|
|
lst1 = sample(range(prec+30), itr)
|
|
lst2 = sample(range(prec+30), itr)
|
|
lst3 = sample(range(prec+30), itr)
|
|
for m in lst1:
|
|
for n in lst2:
|
|
for p in lst3:
|
|
x = from_triple(randsign(), ndigits(m), 0)
|
|
y = from_triple(randsign(), ndigits(n), 0)
|
|
z = from_triple(randsign(), ndigits(p), 0)
|
|
yield x, y, z
|
|
|
|
|
|
# Tests for the 'logical' functions
|
|
def bindigits(prec):
|
|
z = 0
|
|
for i in range(prec):
|
|
z += randrange(2) * 10**i
|
|
return z
|
|
|
|
def logical_un_incr_digits(prec, itr):
|
|
if itr is None:
|
|
lst = range(prec+30)
|
|
else:
|
|
lst = sample(range(prec+30), itr)
|
|
for m in lst:
|
|
yield from_triple(1, bindigits(m), 0)
|
|
|
|
def logical_bin_incr_digits(prec, itr):
|
|
if itr is None:
|
|
lst1 = range(prec+30)
|
|
lst2 = range(prec+30)
|
|
else:
|
|
lst1 = sample(range(prec+30), itr)
|
|
lst2 = sample(range(prec+30), itr)
|
|
for m in lst1:
|
|
x = from_triple(1, bindigits(m), 0)
|
|
yield x, x
|
|
for m in lst1:
|
|
for n in lst2:
|
|
x = from_triple(1, bindigits(m), 0)
|
|
y = from_triple(1, bindigits(n), 0)
|
|
yield x, y
|
|
|
|
|
|
def randint():
|
|
p = randrange(1, 100)
|
|
return ndigits(p) * (1,-1)[randrange(2)]
|
|
|
|
def randfloat():
|
|
p = randrange(1, 100)
|
|
s = numeric_value(p, 383)
|
|
try:
|
|
f = float(numeric_value(p, 383))
|
|
except ValueError:
|
|
f = 0.0
|
|
return f
|
|
|
|
def randcomplex():
|
|
real = randfloat()
|
|
if randrange(100) > 30:
|
|
imag = 0.0
|
|
else:
|
|
imag = randfloat()
|
|
return complex(real, imag)
|
|
|
|
def randfraction():
|
|
num = randint()
|
|
denom = randint()
|
|
if denom == 0:
|
|
denom = 1
|
|
return Fraction(num, denom)
|
|
|
|
number_funcs = [randint, randfloat, randcomplex, randfraction]
|
|
|
|
def un_random_mixed_op(itr=None):
|
|
if itr is None:
|
|
itr = 1000
|
|
for _ in range(itr):
|
|
for func in number_funcs:
|
|
yield func()
|
|
# Test garbage input
|
|
for x in (['x'], ('y',), {'z'}, {1:'z'}):
|
|
yield x
|
|
|
|
def bin_random_mixed_op(prec, emax, emin, itr=None):
|
|
if itr is None:
|
|
itr = 1000
|
|
for _ in range(itr):
|
|
for func in number_funcs:
|
|
yield randdec(prec, emax), func()
|
|
yield func(), randdec(prec, emax)
|
|
for number in number_funcs:
|
|
for dec in close_funcs:
|
|
yield dec(prec, emax, emin), number()
|
|
# Test garbage input
|
|
for x in (['x'], ('y',), {'z'}, {1:'z'}):
|
|
for y in (['x'], ('y',), {'z'}, {1:'z'}):
|
|
yield x, y
|
|
|
|
def tern_random_mixed_op(prec, emax, emin, itr):
|
|
if itr is None:
|
|
itr = 1000
|
|
for _ in range(itr):
|
|
for func in number_funcs:
|
|
yield randdec(prec, emax), randdec(prec, emax), func()
|
|
yield randdec(prec, emax), func(), func()
|
|
yield func(), func(), func()
|
|
# Test garbage input
|
|
for x in (['x'], ('y',), {'z'}, {1:'z'}):
|
|
for y in (['x'], ('y',), {'z'}, {1:'z'}):
|
|
for z in (['x'], ('y',), {'z'}, {1:'z'}):
|
|
yield x, y, z
|
|
|
|
def all_unary(prec, exp_range, itr):
|
|
for a in un_close_to_pow10(prec, exp_range, itr):
|
|
yield (a,)
|
|
for a in un_close_numbers(prec, exp_range, -exp_range, itr):
|
|
yield (a,)
|
|
for a in un_incr_digits_tuple(prec, exp_range, itr):
|
|
yield (a,)
|
|
for a in un_randfloat():
|
|
yield (a,)
|
|
for a in un_random_mixed_op(itr):
|
|
yield (a,)
|
|
for a in logical_un_incr_digits(prec, itr):
|
|
yield (a,)
|
|
for _ in range(100):
|
|
yield (randdec(prec, exp_range),)
|
|
for _ in range(100):
|
|
yield (randtuple(prec, exp_range),)
|
|
|
|
def all_binary(prec, exp_range, itr):
|
|
for a, b in bin_close_to_pow10(prec, exp_range, itr):
|
|
yield a, b
|
|
for a, b in bin_close_numbers(prec, exp_range, -exp_range, itr):
|
|
yield a, b
|
|
for a, b in bin_incr_digits(prec, exp_range, itr):
|
|
yield a, b
|
|
for a, b in bin_randfloat():
|
|
yield a, b
|
|
for a, b in bin_random_mixed_op(prec, exp_range, -exp_range, itr):
|
|
yield a, b
|
|
for a, b in logical_bin_incr_digits(prec, itr):
|
|
yield a, b
|
|
for _ in range(100):
|
|
yield randdec(prec, exp_range), randdec(prec, exp_range)
|
|
|
|
def all_ternary(prec, exp_range, itr):
|
|
for a, b, c in tern_close_numbers(prec, exp_range, -exp_range, itr):
|
|
yield a, b, c
|
|
for a, b, c in tern_incr_digits(prec, exp_range, itr):
|
|
yield a, b, c
|
|
for a, b, c in tern_randfloat():
|
|
yield a, b, c
|
|
for a, b, c in tern_random_mixed_op(prec, exp_range, -exp_range, itr):
|
|
yield a, b, c
|
|
for _ in range(100):
|
|
a = randdec(prec, 2*exp_range)
|
|
b = randdec(prec, 2*exp_range)
|
|
c = randdec(prec, 2*exp_range)
|
|
yield a, b, c
|