text: """
  Generate the src/_ufuncs.c file to turn the scalar C functions
  into numpy ufuncs.  Also writes ufuncs.list as a record of the
  ufunc names.
  
  """
  from pathlib import Path
  import sys
  import shutil
  
  from c_header_parser import (get_simple_sig_dict,
                               get_complex_scalar_dict_by_nargs_nreturns)
  
  blacklist = ['add_barrier']
  
  basedir = Path('..').resolve()
  
  modfile_head_top = """
  /*
  This file is auto-generated--do not edit it.
  
  This is python 3-only (for simplicity) to begin with.
  */
  
  #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
  #include "Python.h"
  #include "math.h"
  #include "numpy/ndarraytypes.h"
  #include "numpy/ufuncobject.h"
  #include "numpy/npy_3kcompat.h"
  #include "gswteos-10.h"
  
  /* possible hack for MSVC: */
  #ifndef NAN
      static double NAN = 0.0/0.0;
  #endif
  
  #ifndef isnan
  #   define isnan(x) ((x) != (x))
  #endif
  
  #define CONVERT_INVALID(x) ((x == GSW_INVALID_VALUE)? NAN: x)
  
  """
  
  # Loops will be generated by calls to modfile_loop_entry.
  
  
  modfile_middle = """
  
  #include "method_bodies.c"
  
  static PyMethodDef GswMethods[] = {
  # include "method_def_entries.c"
          {NULL, NULL, 0, NULL}
  };
  
  static struct PyModuleDef moduledef = {
      PyModuleDef_HEAD_INIT,
      "npufunc",
      NULL,
      -1,
      GswMethods,
      NULL,
      NULL,
      NULL,
      NULL
  };
  
  
  PyMODINIT_FUNC PyInit__gsw_ufuncs(void)
  {
      PyObject *m, *d;
  
      PyObject *ufunc_ptr;
  
      m = PyModule_Create(&moduledef);
      if (!m) {
          return NULL;
      }
  
      d = PyModule_GetDict(m);
  
      import_array();
      import_umath();
  """
  
  modfile_tail = """
  
      return m;
  }
  """
  
  
  def modfile_loop_entry(nin, nout):
      ndin = 'd'*nin
      ndout = 'd'*nout
      loop_id = '%s_%s' % (ndin, ndout)
  
      linelist = ['/* %d in, %d out */' % (nin, nout)]
      linelist.extend([
      'static void loop1d_%s(char **args, npy_intp *dimensions,' % loop_id,
      '                          npy_intp* steps, void* data)',
      '{',
      '    npy_intp i;',
      '    npy_intp n = dimensions[0];'])
      for i in range(nin):
          linelist.append('    char *in%d = args[%d];' % (i, i))
          linelist.append('    npy_intp in_step%d = steps[%d];' % (i, i))
      for i in range(nout):
          linelist.append('    char *out%d = args[%d];' % (i, i+nin))
          linelist.append('    npy_intp out_step%d = steps[%d];' % (i, i+nin))
      intypes = ', '.join(['double'] * nin)
      if nout == 1:
          linelist.append('    double (*func)(%s);' % (intypes,))
      else:
          outtypes = ', '.join(['double *'] * nout)
          linelist.append('    void (*func)(%s, %s);' % (intypes, outtypes))
  
      douts = []
      for i in range(nout):
          douts.append('outd%d' % (i,))
      linelist.append('    double %s;' % ', '.join(douts))
      linelist.extend([
      '    func = data;',
      '',
      '    for (i = 0; i < n; i++) {'])
      tests = []
      args = []
      for i in range(nin):
          tests.append('isnan(*(double *)in%d)' % i)
          args.append('*(double *)in%d' % i)
      linelist.append('        if (%s) {' % '||'.join(tests))
      outs = []
      for i in range(nout):
          outs.append('*((double *)out%d) = NAN;' % i)
      linelist.append('            %s' % ''.join(outs))
      linelist.append('        } else {')
      if nout > 1:
          for i in range(nout):
              args.append('&outd%d' % i)
          linelist.append('            func(%s);' % ', '.join(args))
      else:
          linelist.append('            outd0 = func(%s);' % ', '.join(args))
      for i in range(nout):
          linelist.append('            *((double *)out%d)' % (i,)
                          + ' = CONVERT_INVALID(outd%d);' % (i,))
      linelist.append('        }')
      for i in range(nin):
          linelist.append('        in%d += in_step%d;' % (i, i))
      for i in range(nout):
          linelist.append('        out%d += out_step%d;' % (i, i))
  
      linelist.extend(['    }', '}', ''])
      linelist.append('static PyUFuncGenericFunction'
                      ' funcs_%s[] = {&loop1d_%s};' % (loop_id, loop_id))
      linelist.append('')
      linelist.append('static char types_%s[] = {' % (loop_id,))
  
      linelist.append('        ' + 'NPY_DOUBLE, ' * nin)
      linelist.append('        ' + 'NPY_DOUBLE, ' * nout)
      linelist.extend(['};', ''])
  
      return '\n'.join(linelist)
  
  
  
  def modfile_array_entry(funcname):
      return "static void *data_%s[] = {&gsw_%s};\n" % (funcname, funcname)
  
  
  _init_entry = """
      ufunc_ptr = PyUFunc_FromFuncAndData(funcs_%(ndin)s_%(ndout)s,
                                      data_%(funcname)s,
                                      types_%(ndin)s_%(ndout)s,
                                      1, %(nin)d, %(nout)d,  // ndatatypes, nin, nout
                                      PyUFunc_None,
                                      "%(funcname)s",
                                      "%(funcname)s_docstring",
                                      0);
  
      PyDict_SetItemString(d, "%(funcname)s", ufunc_ptr);
      Py_DECREF(ufunc_ptr);
  """
  
  
  def modfile_init_entry(funcname, nin, nout):
      return _init_entry % dict(funcname=funcname, nin=nin, nout=nout,
                                ndin='d'*nin, ndout='d'*nout)
  
  
  def write_modfile(modfile_name, srcdir):
      argcategories1 = get_simple_sig_dict(srcdir=srcdir)
      argcategories2 = get_complex_scalar_dict_by_nargs_nreturns(srcdir=srcdir)
  
      funcnamelist1 = []
      funcnamelist2 = []
  
      nins = range(1, 6)
      artups = [(2, 2), (3, 2), (3, 3), (6, 2), (2, 3), (4, 3), (5, 3), (3, 5)]
  
      modfile_head_parts = [modfile_head_top]
      for nin in nins:
          modfile_head_parts.append(modfile_loop_entry(nin, 1))
      for artup in artups:
          modfile_head_parts.append(modfile_loop_entry(*artup))
      modfile_head = '\n'.join(modfile_head_parts)
  
      chunks = [modfile_head]
  
      for nin in nins:
          for funcname in sorted(argcategories1[nin]):
              if funcname in blacklist:
                  continue
              chunks.append(modfile_array_entry(funcname))
              funcnamelist1.append(funcname)
  
      for artup in artups:
          for funcname in sorted(argcategories2[artup]):
              if funcname in blacklist:
                  continue
              chunks.append(modfile_array_entry(funcname))
              funcnamelist2.append(funcname)
  
      chunks.append(modfile_middle)
  
      for nin in nins:
          for funcname in sorted(argcategories1[nin]):
              if funcname in blacklist:
                  continue
              chunks.append(modfile_init_entry(funcname, nin, 1))
  
      for artup in artups:
          for funcname in sorted(argcategories2[artup]):
              if funcname in blacklist:
                  continue
              chunks.append(modfile_init_entry(funcname, *artup))
  
  
      chunks.append(modfile_tail)
  
      with modfile_name.open('w') as f:
          f.write(''.join(chunks))
  
      funcnamelist1.sort()
      with open(srcdir + '_ufuncs1.list', 'w') as f:
          f.write('\n'.join(funcnamelist1))
  
      funcnamelist2.sort()
      with open(srcdir + '_ufuncs2.list', 'w') as f:
          f.write('\n'.join(funcnamelist2))
  
      funcnamelist = funcnamelist1 + funcnamelist2
      funcnamelist.sort()
      with open(srcdir + '_ufuncs.list', 'w') as f:
          f.write('\n'.join(funcnamelist))
  
  if __name__ == '__main__':
      srcdir = 'src'
      modfile_name = basedir.joinpath(srcdir, '_ufuncs.c')
      write_modfile(modfile_name, srcdir=srcdir)
  