#!/usr/bin/env python

import sys
import os
from optparse import OptionParser

class ULP (object):
  def __init__ (self, function, input):
    self.function = function
    self.input = input
    self.ulp32 = 0
    self.ulp64 = 0
    self.ulp128 = 0

  def callstr (self):
    return "\"%s (%s)\"" % (self.function, self.input)

# Parse a ulp description file returning a set of (ulps) for each operation.
# Lines starting with '#' are ignored.
# Each test is decribed as:
# test <function name> <input>
# decimal<N> <ulp expected>
#
# If not described, ULP is assumed to be 0.
def parse_file (filename):
  try:
    ulp = None
    ulps = []
    for line in open(filename, 'r').readlines():
      # Ignore comments and blank lines
      if line.rstrip().startswith ("#"):
        continue
      fields = line.split()
      if len(fields) is 0:
        continue
      if fields[0] == 'test':
        ulp = ULP (fields[1], fields[2])
        ulps.append (ulp)
      elif fields[0] == 'decimal32':
        ulp.ulp32 = fields[1]
      elif fields[0] == 'decimal64':
        ulp.ulp64 = fields[1]
      elif fields[0] == 'decimal128':
        ulp.ulp128 = fields[1]

    return ulps

  except (IOError, OSError) as e:
    print ("error: open (%s) failed: %s\n" % (filename, e.strerror))
    exit (1)


def print_header (filename):
  print ("/* This file is automatically generated")
  print (" * from %s with gen-libdfp-ulps.py.  */" % filename)
  print ("")
  print ("#if defined(DECIMAL32)")
  print ("# define CHOOSE(decimal128, decimal64, decimal32) decimal32")
  print ("#elif defined(DECIMAL64)")
  print ("# define CHOOSE(decimal128, decimal64, decimal32) decimal64")
  print ("#elif defined(DECIMAL128)")
  print ("# define CHOOSE(decimal128, decimal64, decimal32) decimal128")
  print ("#endif")
  print ("")
  print ("struct ulp_data {")
  print ("  const char *name;")
  print ("  FLOAT max_ulp;")
  print ("};")
  print ("")


def print_ulps (ulps):
  print ("/* Maximal error of functions.  */")
  print ("static const struct ulp_data test_ulps[] = {")
  # The array will be used in a bsearch call, so the itens must be sorted
  for ulp in sorted(ulps, key=lambda x: x.callstr()):
    print ("  { %s, CHOOSE(%s, %s, %s) }," %
      (ulp.callstr(), ulp.ulp128, ulp.ulp64, ulp.ulp32))
  print ("};")
    

def print_output (filename):
  ulps = parse_file (filename)
  print_header (filename)
  print_ulps (ulps)


if __name__ == "__main__":
  parser = OptionParser ()
  parser.add_option ("-o", "--output", dest="filename",
                     help="white output to FILE")
  (options, args) = parser.parse_args()

  try:
    if options.filename:
      sys.stdout = open (options.filename, "w")

    if len(args) == 0:
      sys.stderr.write ("usage: gen-libdfp-ulps.py <options> <input file>\n")
      raise Exception()

    print_output (args[0])

  except:
    if options.filename:
      os.remove (options.filename)
    exit (1)
