1  #
       2  # Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
       3  #
       4  # Redistribution and use in source and binary forms, with or without
       5  # modification, are permitted provided that the following conditions
       6  # are met:
       7  #
       8  # 1. Redistributions of source code must retain the above copyright
       9  #    notice, this list of conditions and the following disclaimer.
      10  #
      11  # 2. Redistributions in binary form must reproduce the above copyright
      12  #    notice, this list of conditions and the following disclaimer in the
      13  #    documentation and/or other materials provided with the distribution.
      14  #
      15  # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
      16  # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      17  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      18  # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
      19  # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      20  # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      21  # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      22  # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      23  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      24  # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      25  # SUCH DAMAGE.
      26  #
      27  
      28  
      29  # Generate test cases for deccheck.py.
      30  
      31  
      32  #
      33  # Grammar from http://speleotrove.com/decimal/daconvs.html
      34  #
      35  # sign           ::=  '+' | '-'
      36  # digit          ::=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
      37  #                     '8' | '9'
      38  # indicator      ::=  'e' | 'E'
      39  # digits         ::=  digit [digit]...
      40  # decimal-part   ::=  digits '.' [digits] | ['.'] digits
      41  # exponent-part  ::=  indicator [sign] digits
      42  # infinity       ::=  'Infinity' | 'Inf'
      43  # nan            ::=  'NaN' [digits] | 'sNaN' [digits]
      44  # numeric-value  ::=  decimal-part [exponent-part] | infinity
      45  # numeric-string ::=  [sign] numeric-value | [sign] nan
      46  #
      47  
      48  
      49  from random import randrange, sample
      50  from fractions import Fraction
      51  from randfloat import un_randfloat, bin_randfloat, tern_randfloat
      52  
      53  
      54  def sign():
      55      if randrange(2):
      56          if randrange(2): return '+'
      57          return ''
      58      return '-'
      59  
      60  def indicator():
      61      return "eE"[randrange(2)]
      62  
      63  def digits(maxprec):
      64      if maxprec == 0: return ''
      65      return str(randrange(10**maxprec))
      66  
      67  def dot():
      68      if randrange(2): return '.'
      69      return ''
      70  
      71  def decimal_part(maxprec):
      72      if randrange(100) > 60: # integers
      73          return digits(maxprec)
      74      if randrange(2):
      75          intlen = randrange(1, maxprec+1)
      76          fraclen = maxprec-intlen
      77          intpart = digits(intlen)
      78          fracpart = digits(fraclen)
      79          return ''.join((intpart, '.', fracpart))
      80      else:
      81          return ''.join((dot(), digits(maxprec)))
      82  
      83  def expdigits(maxexp):
      84      return str(randrange(maxexp))
      85  
      86  def exponent_part(maxexp):
      87      return ''.join((indicator(), sign(), expdigits(maxexp)))
      88  
      89  def infinity():
      90      if randrange(2): return 'Infinity'
      91      return 'Inf'
      92  
      93  def nan():
      94      d = ''
      95      if randrange(2):
      96          d = digits(randrange(99))
      97      if randrange(2):
      98          return ''.join(('NaN', d))
      99      else:
     100          return ''.join(('sNaN', d))
     101  
     102  def numeric_value(maxprec, maxexp):
     103      if randrange(100) > 90:
     104          return infinity()
     105      exp_part = ''
     106      if randrange(100) > 60:
     107          exp_part = exponent_part(maxexp)
     108      return ''.join((decimal_part(maxprec), exp_part))
     109  
     110  def numeric_string(maxprec, maxexp):
     111      if randrange(100) > 95:
     112          return ''.join((sign(), nan()))
     113      else:
     114          return ''.join((sign(), numeric_value(maxprec, maxexp)))
     115  
     116  def randdec(maxprec, maxexp):
     117      return numeric_string(maxprec, maxexp)
     118  
     119  def rand_adjexp(maxprec, maxadjexp):
     120      d = digits(maxprec)
     121      maxexp = maxadjexp-len(d)+1
     122      if maxexp == 0: maxexp = 1
     123      exp = str(randrange(maxexp-2*(abs(maxexp)), maxexp))
     124      return ''.join((sign(), d, 'E', exp))
     125  
     126  
     127  def ndigits(n):
     128      if n < 1: return 0
     129      return randrange(10**(n-1), 10**n)
     130  
     131  def randtuple(maxprec, maxexp):
     132      n = randrange(100)
     133      sign = randrange(2)
     134      coeff = ndigits(maxprec)
     135      if n >= 95:
     136          coeff = ()
     137          exp = 'F'
     138      elif n >= 85:
     139          coeff = tuple(map(int, str(ndigits(maxprec))))
     140          exp = "nN"[randrange(2)]
     141      else:
     142          coeff = tuple(map(int, str(ndigits(maxprec))))
     143          exp = randrange(-maxexp, maxexp)
     144      return (sign, coeff, exp)
     145  
     146  def from_triple(sign, coeff, exp):
     147      return ''.join((str(sign*coeff), indicator(), str(exp)))
     148  
     149  
     150  # Close to 10**n
     151  def un_close_to_pow10(prec, maxexp, itr=None):
     152      if itr is None:
     153          lst = range(prec+30)
     154      else:
     155          lst = sample(range(prec+30), itr)
     156      nines = [10**n - 1 for n in lst]
     157      pow10 = [10**n for n in lst]
     158      for coeff in nines:
     159          yield coeff
     160          yield -coeff
     161          yield from_triple(1, coeff, randrange(2*maxexp))
     162          yield from_triple(-1, coeff, randrange(2*maxexp))
     163      for coeff in pow10:
     164          yield coeff
     165          yield -coeff
     166  
     167  # Close to 10**n
     168  def bin_close_to_pow10(prec, maxexp, itr=None):
     169      if itr is None:
     170          lst = range(prec+30)
     171      else:
     172          lst = sample(range(prec+30), itr)
     173      nines = [10**n - 1 for n in lst]
     174      pow10 = [10**n for n in lst]
     175      for coeff in nines:
     176          yield coeff, 1
     177          yield -coeff, -1
     178          yield 1, coeff
     179          yield -1, -coeff
     180          yield from_triple(1, coeff, randrange(2*maxexp)), 1
     181          yield from_triple(-1, coeff, randrange(2*maxexp)), -1
     182          yield 1, from_triple(1, coeff, -randrange(2*maxexp))
     183          yield -1, from_triple(-1, coeff, -randrange(2*maxexp))
     184      for coeff in pow10:
     185          yield coeff, -1
     186          yield -coeff, 1
     187          yield 1, -coeff
     188          yield -coeff, 1
     189  
     190  # Close to 1:
     191  def close_to_one_greater(prec, emax, emin):
     192      rprec = 10**prec
     193      return ''.join(("1.", '0'*randrange(prec),
     194                     str(randrange(rprec))))
     195  
     196  def close_to_one_less(prec, emax, emin):
     197      rprec = 10**prec
     198      return ''.join(("0.9", '9'*randrange(prec),
     199                     str(randrange(rprec))))
     200  
     201  # Close to 0:
     202  def close_to_zero_greater(prec, emax, emin):
     203      rprec = 10**prec
     204      return ''.join(("0.", '0'*randrange(prec),
     205                     str(randrange(rprec))))
     206  
     207  def close_to_zero_less(prec, emax, emin):
     208      rprec = 10**prec
     209      return ''.join(("-0.", '0'*randrange(prec),
     210                     str(randrange(rprec))))
     211  
     212  # Close to emax:
     213  def close_to_emax_less(prec, emax, emin):
     214      rprec = 10**prec
     215      return ''.join(("9.", '9'*randrange(prec),
     216                     str(randrange(rprec)), "E", str(emax)))
     217  
     218  def close_to_emax_greater(prec, emax, emin):
     219      rprec = 10**prec
     220      return ''.join(("1.", '0'*randrange(prec),
     221                     str(randrange(rprec)), "E", str(emax+1)))
     222  
     223  # Close to emin:
     224  def close_to_emin_greater(prec, emax, emin):
     225      rprec = 10**prec
     226      return ''.join(("1.", '0'*randrange(prec),
     227                     str(randrange(rprec)), "E", str(emin)))
     228  
     229  def close_to_emin_less(prec, emax, emin):
     230      rprec = 10**prec
     231      return ''.join(("9.", '9'*randrange(prec),
     232                     str(randrange(rprec)), "E", str(emin-1)))
     233  
     234  # Close to etiny:
     235  def close_to_etiny_greater(prec, emax, emin):
     236      rprec = 10**prec
     237      etiny = emin - (prec - 1)
     238      return ''.join(("1.", '0'*randrange(prec),
     239                     str(randrange(rprec)), "E", str(etiny)))
     240  
     241  def close_to_etiny_less(prec, emax, emin):
     242      rprec = 10**prec
     243      etiny = emin - (prec - 1)
     244      return ''.join(("9.", '9'*randrange(prec),
     245                     str(randrange(rprec)), "E", str(etiny-1)))
     246  
     247  
     248  def close_to_min_etiny_greater(prec, max_prec, min_emin):
     249      rprec = 10**prec
     250      etiny = min_emin - (max_prec - 1)
     251      return ''.join(("1.", '0'*randrange(prec),
     252                     str(randrange(rprec)), "E", str(etiny)))
     253  
     254  def close_to_min_etiny_less(prec, max_prec, min_emin):
     255      rprec = 10**prec
     256      etiny = min_emin - (max_prec - 1)
     257      return ''.join(("9.", '9'*randrange(prec),
     258                     str(randrange(rprec)), "E", str(etiny-1)))
     259  
     260  
     261  close_funcs = [
     262    close_to_one_greater, close_to_one_less, close_to_zero_greater,
     263    close_to_zero_less, close_to_emax_less, close_to_emax_greater,
     264    close_to_emin_greater, close_to_emin_less, close_to_etiny_greater,
     265    close_to_etiny_less, close_to_min_etiny_greater, close_to_min_etiny_less
     266  ]
     267  
     268  
     269  def un_close_numbers(prec, emax, emin, itr=None):
     270      if itr is None:
     271          itr = 1000
     272      for _ in range(itr):
     273          for func in close_funcs:
     274              yield func(prec, emax, emin)
     275  
     276  def bin_close_numbers(prec, emax, emin, itr=None):
     277      if itr is None:
     278          itr = 1000
     279      for _ in range(itr):
     280          for func1 in close_funcs:
     281              for func2 in close_funcs:
     282                  yield func1(prec, emax, emin), func2(prec, emax, emin)
     283          for func in close_funcs:
     284              yield randdec(prec, emax), func(prec, emax, emin)
     285              yield func(prec, emax, emin), randdec(prec, emax)
     286  
     287  def tern_close_numbers(prec, emax, emin, itr):
     288      if itr is None:
     289          itr = 1000
     290      for _ in range(itr):
     291          for func1 in close_funcs:
     292              for func2 in close_funcs:
     293                  for func3 in close_funcs:
     294                      yield (func1(prec, emax, emin), func2(prec, emax, emin),
     295                             func3(prec, emax, emin))
     296          for func in close_funcs:
     297              yield (randdec(prec, emax), func(prec, emax, emin),
     298                     func(prec, emax, emin))
     299              yield (func(prec, emax, emin), randdec(prec, emax),
     300                     func(prec, emax, emin))
     301              yield (func(prec, emax, emin), func(prec, emax, emin),
     302                     randdec(prec, emax))
     303          for func in close_funcs:
     304              yield (randdec(prec, emax), randdec(prec, emax),
     305                     func(prec, emax, emin))
     306              yield (randdec(prec, emax), func(prec, emax, emin),
     307                     randdec(prec, emax))
     308              yield (func(prec, emax, emin), randdec(prec, emax),
     309                     randdec(prec, emax))
     310  
     311  
     312  # If itr == None, test all digit lengths up to prec + 30
     313  def un_incr_digits(prec, maxexp, itr):
     314      if itr is None:
     315          lst = range(prec+30)
     316      else:
     317          lst = sample(range(prec+30), itr)
     318      for m in lst:
     319          yield from_triple(1, ndigits(m), 0)
     320          yield from_triple(-1, ndigits(m), 0)
     321          yield from_triple(1, ndigits(m), randrange(maxexp))
     322          yield from_triple(-1, ndigits(m), randrange(maxexp))
     323  
     324  # If itr == None, test all digit lengths up to prec + 30
     325  # Also output decimals im tuple form.
     326  def un_incr_digits_tuple(prec, maxexp, itr):
     327      if itr is None:
     328          lst = range(prec+30)
     329      else:
     330          lst = sample(range(prec+30), itr)
     331      for m in lst:
     332          yield from_triple(1, ndigits(m), 0)
     333          yield from_triple(-1, ndigits(m), 0)
     334          yield from_triple(1, ndigits(m), randrange(maxexp))
     335          yield from_triple(-1, ndigits(m), randrange(maxexp))
     336          # test from tuple
     337          yield (0, tuple(map(int, str(ndigits(m)))), 0)
     338          yield (1, tuple(map(int, str(ndigits(m)))), 0)
     339          yield (0, tuple(map(int, str(ndigits(m)))), randrange(maxexp))
     340          yield (1, tuple(map(int, str(ndigits(m)))), randrange(maxexp))
     341  
     342  # If itr == None, test all combinations of digit lengths up to prec + 30
     343  def bin_incr_digits(prec, maxexp, itr):
     344      if itr is None:
     345          lst1 = range(prec+30)
     346          lst2 = range(prec+30)
     347      else:
     348          lst1 = sample(range(prec+30), itr)
     349          lst2 = sample(range(prec+30), itr)
     350      for m in lst1:
     351          x = from_triple(1, ndigits(m), 0)
     352          yield x, x
     353          x = from_triple(-1, ndigits(m), 0)
     354          yield x, x
     355          x = from_triple(1, ndigits(m), randrange(maxexp))
     356          yield x, x
     357          x = from_triple(-1, ndigits(m), randrange(maxexp))
     358          yield x, x
     359      for m in lst1:
     360          for n in lst2:
     361              x = from_triple(1, ndigits(m), 0)
     362              y = from_triple(1, ndigits(n), 0)
     363              yield x, y
     364              x = from_triple(-1, ndigits(m), 0)
     365              y = from_triple(1, ndigits(n), 0)
     366              yield x, y
     367              x = from_triple(1, ndigits(m), 0)
     368              y = from_triple(-1, ndigits(n), 0)
     369              yield x, y
     370              x = from_triple(-1, ndigits(m), 0)
     371              y = from_triple(-1, ndigits(n), 0)
     372              yield x, y
     373              x = from_triple(1, ndigits(m), randrange(maxexp))
     374              y = from_triple(1, ndigits(n), randrange(maxexp))
     375              yield x, y
     376              x = from_triple(-1, ndigits(m), randrange(maxexp))
     377              y = from_triple(1, ndigits(n), randrange(maxexp))
     378              yield x, y
     379              x = from_triple(1, ndigits(m), randrange(maxexp))
     380              y = from_triple(-1, ndigits(n), randrange(maxexp))
     381              yield x, y
     382              x = from_triple(-1, ndigits(m), randrange(maxexp))
     383              y = from_triple(-1, ndigits(n), randrange(maxexp))
     384              yield x, y
     385  
     386  
     387  def randsign():
     388      return (1, -1)[randrange(2)]
     389  
     390  # If itr == None, test all combinations of digit lengths up to prec + 30
     391  def tern_incr_digits(prec, maxexp, itr):
     392      if itr is None:
     393          lst1 = range(prec+30)
     394          lst2 = range(prec+30)
     395          lst3 = range(prec+30)
     396      else:
     397          lst1 = sample(range(prec+30), itr)
     398          lst2 = sample(range(prec+30), itr)
     399          lst3 = sample(range(prec+30), itr)
     400      for m in lst1:
     401          for n in lst2:
     402              for p in lst3:
     403                  x = from_triple(randsign(), ndigits(m), 0)
     404                  y = from_triple(randsign(), ndigits(n), 0)
     405                  z = from_triple(randsign(), ndigits(p), 0)
     406                  yield x, y, z
     407  
     408  
     409  # Tests for the 'logical' functions
     410  def bindigits(prec):
     411      z = 0
     412      for i in range(prec):
     413          z += randrange(2) * 10**i
     414      return z
     415  
     416  def logical_un_incr_digits(prec, itr):
     417      if itr is None:
     418          lst = range(prec+30)
     419      else:
     420          lst = sample(range(prec+30), itr)
     421      for m in lst:
     422          yield from_triple(1, bindigits(m), 0)
     423  
     424  def logical_bin_incr_digits(prec, itr):
     425      if itr is None:
     426          lst1 = range(prec+30)
     427          lst2 = range(prec+30)
     428      else:
     429          lst1 = sample(range(prec+30), itr)
     430          lst2 = sample(range(prec+30), itr)
     431      for m in lst1:
     432          x = from_triple(1, bindigits(m), 0)
     433          yield x, x
     434      for m in lst1:
     435          for n in lst2:
     436              x = from_triple(1, bindigits(m), 0)
     437              y = from_triple(1, bindigits(n), 0)
     438              yield x, y
     439  
     440  
     441  def randint():
     442      p = randrange(1, 100)
     443      return ndigits(p) * (1,-1)[randrange(2)]
     444  
     445  def randfloat():
     446      p = randrange(1, 100)
     447      s = numeric_value(p, 383)
     448      try:
     449          f = float(numeric_value(p, 383))
     450      except ValueError:
     451          f = 0.0
     452      return f
     453  
     454  def randcomplex():
     455      real = randfloat()
     456      if randrange(100) > 30:
     457          imag = 0.0
     458      else:
     459          imag = randfloat()
     460      return complex(real, imag)
     461  
     462  def randfraction():
     463      num = randint()
     464      denom = randint()
     465      if denom == 0:
     466          denom = 1
     467      return Fraction(num, denom)
     468  
     469  number_funcs = [randint, randfloat, randcomplex, randfraction]
     470  
     471  def un_random_mixed_op(itr=None):
     472      if itr is None:
     473          itr = 1000
     474      for _ in range(itr):
     475          for func in number_funcs:
     476              yield func()
     477      # Test garbage input
     478      for x in (['x'], ('y',), {'z'}, {1:'z'}):
     479          yield x
     480  
     481  def bin_random_mixed_op(prec, emax, emin, itr=None):
     482      if itr is None:
     483          itr = 1000
     484      for _ in range(itr):
     485          for func in number_funcs:
     486              yield randdec(prec, emax), func()
     487              yield func(), randdec(prec, emax)
     488          for number in number_funcs:
     489              for dec in close_funcs:
     490                  yield dec(prec, emax, emin), number()
     491      # Test garbage input
     492      for x in (['x'], ('y',), {'z'}, {1:'z'}):
     493          for y in (['x'], ('y',), {'z'}, {1:'z'}):
     494              yield x, y
     495  
     496  def tern_random_mixed_op(prec, emax, emin, itr):
     497      if itr is None:
     498          itr = 1000
     499      for _ in range(itr):
     500          for func in number_funcs:
     501              yield randdec(prec, emax), randdec(prec, emax), func()
     502              yield randdec(prec, emax), func(), func()
     503              yield func(), func(), func()
     504      # Test garbage input
     505      for x in (['x'], ('y',), {'z'}, {1:'z'}):
     506          for y in (['x'], ('y',), {'z'}, {1:'z'}):
     507              for z in (['x'], ('y',), {'z'}, {1:'z'}):
     508                  yield x, y, z
     509  
     510  def all_unary(prec, exp_range, itr):
     511      for a in un_close_to_pow10(prec, exp_range, itr):
     512          yield (a,)
     513      for a in un_close_numbers(prec, exp_range, -exp_range, itr):
     514          yield (a,)
     515      for a in un_incr_digits_tuple(prec, exp_range, itr):
     516          yield (a,)
     517      for a in un_randfloat():
     518          yield (a,)
     519      for a in un_random_mixed_op(itr):
     520          yield (a,)
     521      for a in logical_un_incr_digits(prec, itr):
     522          yield (a,)
     523      for _ in range(100):
     524          yield (randdec(prec, exp_range),)
     525      for _ in range(100):
     526          yield (randtuple(prec, exp_range),)
     527  
     528  def unary_optarg(prec, exp_range, itr):
     529      for _ in range(100):
     530          yield randdec(prec, exp_range), None
     531          yield randdec(prec, exp_range), None, None
     532  
     533  def all_binary(prec, exp_range, itr):
     534      for a, b in bin_close_to_pow10(prec, exp_range, itr):
     535          yield a, b
     536      for a, b in bin_close_numbers(prec, exp_range, -exp_range, itr):
     537          yield a, b
     538      for a, b in bin_incr_digits(prec, exp_range, itr):
     539          yield a, b
     540      for a, b in bin_randfloat():
     541          yield a, b
     542      for a, b in bin_random_mixed_op(prec, exp_range, -exp_range, itr):
     543          yield a, b
     544      for a, b in logical_bin_incr_digits(prec, itr):
     545          yield a, b
     546      for _ in range(100):
     547          yield randdec(prec, exp_range), randdec(prec, exp_range)
     548  
     549  def binary_optarg(prec, exp_range, itr):
     550      for _ in range(100):
     551          yield randdec(prec, exp_range), randdec(prec, exp_range), None
     552          yield randdec(prec, exp_range), randdec(prec, exp_range), None, None
     553  
     554  def all_ternary(prec, exp_range, itr):
     555      for a, b, c in tern_close_numbers(prec, exp_range, -exp_range, itr):
     556          yield a, b, c
     557      for a, b, c in tern_incr_digits(prec, exp_range, itr):
     558          yield a, b, c
     559      for a, b, c in tern_randfloat():
     560          yield a, b, c
     561      for a, b, c in tern_random_mixed_op(prec, exp_range, -exp_range, itr):
     562          yield a, b, c
     563      for _ in range(100):
     564          a = randdec(prec, 2*exp_range)
     565          b = randdec(prec, 2*exp_range)
     566          c = randdec(prec, 2*exp_range)
     567          yield a, b, c
     568  
     569  def ternary_optarg(prec, exp_range, itr):
     570      for _ in range(100):
     571          a = randdec(prec, 2*exp_range)
     572          b = randdec(prec, 2*exp_range)
     573          c = randdec(prec, 2*exp_range)
     574          yield a, b, c, None
     575          yield a, b, c, None, None