import sys, os, imp

try:
  import template
except ImportError:
  # Make sure autoclutter can be found even though it isn't installed yet.
  file, path, descr = imp.find_module("template")
  template = imp.load_module("template", file, path, descr)
  
"""This program generates multiple c source code files
It handles the tedium of all the repetitive code dealing with
types and particularly combinations of types with types and
types with functions.
$Id: codegenerator.py,v 1.58 2004/08/25 17:23:32 jaytmiller Exp $
"""

#*********************************************************************#
#                                                                     #
#                            DATA SECTION                             #
#                                                                     #
#*********************************************************************#
# Of course, the warning below does not apply to this file!

_HEADER = \
'''
/*   W     W   AAA   RRRR   N   N  III  N   N   GGG   !!!
**   W     W  A   A  R   R  NN  N   I   NN  N  G   G  !!!
**   W  W  W  AAAAA  RRRR   N N N   I   N N N  G       !
**    W W W   A   A  R   R  N  NN   I   N  NN  G  GG
**     W W    A   A  R   R  N   N  III  N   N   GGG   !!!
**
** WARNING: This file is program generated by codegenerator.py.
**
** DO NOT EDIT THIS FILE! Any changes made to this file will be lost!
*/

#include <Python.h>
#include <stdio.h>
#include "libnumarray.h"
static PyObject *_Error;
'''

_TYPE_SEPARATOR = \
'''
/****************** <typename> *******************/
'''

AS_PyVALUE_TEMPLATE = \
'''
static PyObject *<typename>asPyValue(void *data) {
  return Py_BuildValue(<pybuildchar>, <pycast>*((<typename> *) data));
}

SELF_CHECKED_CFUNC_DESCR(<typename>asPyValue, CFUNC_AS_PY_VALUE);
'''

COMPLEX_AS_PyVALUE_TEMPLATE = \
'''
static PyObject *<typename>asPyValue(void *data) {
  return PyComplex_FromDoubles(((<typename> *) data)->r, 
                               ((<typename> *) data)->i);
}

SELF_CHECKED_CFUNC_DESCR(<typename>asPyValue, CFUNC_AS_PY_VALUE);
'''

# From_PyVALUE uses a (relatively) backwards convention for return status.
#   0 --> failure.  non-zero --> success.  This is actually my preferred
#   style as well, but *not* what the rest of numarray does.
#   Most ufuncs use the convention -1 for error and 0 for success.
FROM_PyVALUE_TEMPLATE = \
'''
static int <typename>fromPyValue(PyObject *value, void *dataptr) {
    <typename> *data = (<typename> *) dataptr;
    if (!PyNumber_Check(value))
        return 0;
    else {
        if (PyLong_Check(value)) {
            *data = <typecast>(PyLong_AsLongLong(value));
        } else if (PyInt_Check(value)) {
            *data = <typecast>(PyInt_AsLong(value));
        } else if (PyFloat_Check(value)) {
            *data = <typecast>(PyFloat_AsDouble(value));
        } else if (PyComplex_Check(value)) {
           *data = <typecast>(PyComplex_RealAsDouble(value));
        } else {
            return 0;
        }
        if (PyErr_Occurred())
           return 0;
        else
           return 1;
    }
}

SELF_CHECKED_CFUNC_DESCR(<typename>fromPyValue, CFUNC_FROM_PY_VALUE);
'''

COMPLEX_FROM_PyVALUE_TEMPLATE = \
'''
static int <typename>fromPyValue(PyObject *value, void *dataptr) {
    <typename> *data = (<typename> *) dataptr;
    if (!PyNumber_Check(value))
        return 0;
    else {
        if (PyLong_Check(value)) {
            data->r = PyLong_AsLong(value);
            data->i = 0;
        } else if (PyInt_Check(value)) {
            data->r = PyInt_AsLong(value);
            data->i = 0;
        } else if (PyFloat_Check(value)) {
            data->r = PyFloat_AsDouble(value);
            data->i = 0;
        } else if (PyComplex_Check(value)) {
           data->r = PyComplex_RealAsDouble(value);
           data->i = PyComplex_ImagAsDouble(value);
        } else {
            return 0;
        }
        if (PyErr_Occurred())
           return 0;
        else
           return 1;
    }
}

SELF_CHECKED_CFUNC_DESCR(<typename>fromPyValue, CFUNC_FROM_PY_VALUE);
'''

CONVERSION_TEMPLATE = \
'''
static int <typename>as<desttypename>(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) {
    long i;
    <typename>     *tin  = (<typename> *)     buffers[0];
    <desttypename> *tout = (<desttypename> *) buffers[1];
    BEGIN_THREADS
    for (i=0; i<niter; i++, tout++, tin++) {
        <numarray_to_numarray>
    }
    END_THREADS
    return 0;
}

UFUNC_DESCR2(<typename>as<desttypename>, sizeof(<typename>), sizeof(<desttypename>));
'''

_numarray_to_numarray = {
  ("Complex", "Complex"): "tout->r = tin->r; tout->i = tin->i;",
  ("Complex", "default"): "*tout = <desttypecast>(tin->r);",
  ("default", "Complex"): "tout->r = *tin; tout->i = 0;",
  ("default", "default"): "*tout = <desttypecast>(*tin);"
}

def _complex_type(t):
  if t in ["Complex32", "Complex64"]:
    return "Complex"
  else:
    return "default"

def numarray_to_numarray(t1, t2):
  return _numarray_to_numarray[_complex_type(t1), _complex_type(t2)]
    

SORT_HEADER      = _HEADER + \
'''
#define swap(i, j)   { temp=v[i]; v[i]=v[j]; v[j]=temp; }

#define wswap(i, j)   { temp=v[i];  v[i]=v[j]; v[j]=temp; \
                       wtemp=w[i];  w[i]=w[j]; w[j]=wtemp; }
'''

SORT_TEMPLATE = \
'''
#define STDC_LT(a,b) ((a) < (b))
#define STDC_LE(a,b) ((a) <= (b))
#define STDC_EQ(a,b) ((a) == (b))

void sort0<typename>(<typename> *v, long left, long right)
{
  <typename> temp;
  long i, last, lastl, lastr, diff;

  diff = right - left;
  if (diff <= 0)
     return;

  /* choose the pivot randomly. */
  i = left + (int) (((right-left)*1.0*rand())/(RAND_MAX+1.0));

  /* Split array into values < pivot, and values > pivot. */
  swap(left, i);
  last = left;
  for(i=left+1; i<=right; i++)
    if (<lessthan>(v[i], v[left]))
    {
      ++ last;
      swap(last, i);
    }
  swap(left, last);
  
  lastl = last-1;
  lastr = last+1;

  /* Exclude values equal to pivot from recursion */
  while((left < lastl) && <equals>(v[last],v[lastl]))
     --lastl;
  while((lastr < right) && <equals>(v[last],v[lastr]))
     ++lastr;

  sort0<typename>(v, left, lastl);
  sort0<typename>(v, lastr, right);
}

static int sort<typename>(long niter, long ninargs, long noutargs,
           void **buffers, long *bsizes)
{
    BEGIN_THREADS
    sort0<typename>( (<typename> *) buffers[0], 0, niter-1);
    END_THREADS
    return 0;
}

UFUNC_DESCR1(sort<typename>, sizeof(<typename>));

void asort0<typename>(<typename> *v, long *w, long left, long right)
{
  <typename> temp;
  long  wtemp, i, last, lastl, lastr;

  if (left >= right)
    return;

  /* choose the pivot randomly. */
  i = left + (int) (((right-left)*1.0*rand())/(RAND_MAX+1.0));

  wswap(left, i);
  last = left;
  for(i=left+1; i<=right; i++)
    if (<lessthan>(v[i], v[left]))
    {
      ++ last;
      wswap(last, i);
    }
  wswap(left, last);

  lastl = last-1;
  lastr = last+1;

  /* Exclude values equal to pivot from recursion */
  while((left < lastl) && <equals>(v[last],v[lastl]))
     --lastl;
  while((lastr < right) && <equals>(v[last],v[lastr]))
     ++lastr;

  asort0<typename>(v, w, left, lastl);
  asort0<typename>(v, w, lastr, right);
}

static int asort<typename>(long niter, long ninargs, long noutargs,
           void **buffers, long *bsizes)
{
    BEGIN_THREADS
    asort0<typename>( (<typename> *) buffers[0], (long *) buffers[1],
       0, niter-1);
    END_THREADS
    return 0;
}

CFUNC_DESCR(asort<typename>, CFUNC_UFUNC, CHECK_ALIGN, 0, 2, sizeof(<typename>), sizeof(long), 0, 0, 0, 0);

static long search<typename>(long na, <typename> *a, <typename> v)
{
  long i = 0;
  <typename> *b;
  while(na > 10)
    {
      long mid = na>>1;
      if (<lessequal>(v, a[mid]))
        na = mid;
      else
        {
          i += mid;
          a += mid;
          na -= mid;
        }
    }
  b = a;
  while((<lessthan>(*b, v)) && na--)
    ++ b;
  return i+(b-a);
}

static int searchsorted<typename>(int niter, int ninargs, int noutargs,
          void **buffers, long *bsizes)
{
  maybelong   nbins;   
  <typename> *bins;
  <typename> *values;
  long       *indices;
  maybelong i;
  
  if (NA_checkOneCBuffer("searchsorted<typename>", 1,
       buffers[0], bsizes[0], sizeof(maybelong)))
       return -1;
  nbins    = *(maybelong *)  buffers[0];

  if (NA_checkOneCBuffer("searchsorted<typename>", nbins,
      buffers[1], bsizes[1], sizeof(<typename>)))
      return -1;
  bins    = (<typename> *) buffers[1];

  if (NA_checkOneCBuffer("searchsorted<typename>", niter,
      buffers[2], bsizes[2], sizeof(<typename>)))
      return -1;
  values  = (<typename> *) buffers[2];

  if (NA_checkOneCBuffer("searchsorted<typename>", niter,
      buffers[3], bsizes[3], sizeof(long)))
      return -1;
  indices = (long *)   buffers[3];

  if (NA_checkIo("searchsorted<typename>", 3, 1, ninargs, noutargs))
     return -1;

  BEGIN_THREADS
  for(i=0; i<niter; i++)
     indices[i] = search<typename>(nbins, bins, values[i]);
  END_THREADS
  return 0;
}

SELF_CHECKED_CFUNC_DESCR(searchsorted<typename>, CFUNC_UFUNC);

static int fillarray<typename> (long niter, long ninargs, long noutargs, 
			     void **buffers, long *bsizes) {
    long i;
    <typename> start, delta;
    <typename> *tin;
    <typename> *tparams;
    Int8 itemsizes[2] = { sizeof(<typename>), sizeof(<typename>) };
    Int8 iters[2] = { 0, 2 };

    if (NA_checkIo("fillarray<typename>", 1, 1, ninargs, noutargs) ||
	NA_checkNCBuffers("fillarray<typename>", 2,
			 niter, buffers, bsizes, itemsizes, iters))
	    return -1;

    tin = (<typename> *) buffers[0];
    tparams = (<typename> *) buffers[1];
    start = tparams[0];
    delta = tparams[1];

    BEGIN_THREADS
    for (i=0; i<niter; i++, tin++) {
        <fillstep>
    }
    END_THREADS
    return 0;
}

SELF_CHECKED_CFUNC_DESCR(fillarray<typename>, CFUNC_UFUNC);

static int nonzeroCoords<typename>(long niter, long ninargs, long noutargs,
                               void **buffers, long *bsizes)
{
	<typename>   *source;
	maybelong  *sstride;
	long  **output;
	long i, minbsize, next = 0;
	
	if (NA_checkIo("nonzeroCoords<typename>", 2, noutargs, ninargs, noutargs))
		return -1;

	if (NA_checkOneCBuffer("nonzeroCoords<typename>", niter,
		      buffers[0], bsizes[0], sizeof(<typename>)))
		return -1;
	    
	if (NA_checkOneCBuffer("nonzeroCoords<typename>", noutargs,
		      buffers[1], bsizes[1], sizeof(maybelong)))
		return -1;

	/* Check alignment only.  Storage optimization makes numarray
	 shorter than niter likely, possibly even 0! */
	for (i=0; i<noutargs; i++)
		if (NA_checkOneCBuffer("nonzeroCoords<typename>", 0,
			      buffers[2+i], bsizes[2+i], sizeof(long)))
			return -1;

	source     = (<typename> *)  buffers[0];
	sstride    = (maybelong  *)  buffers[1];
	output     = (long **) &buffers[2];

	minbsize = bsizes[0];
	for(i=0; i<noutargs; i++)
		if (sstride[i] <= 0) {
			PyErr_Format(_Error,
			     "nonzeroCoords<typename>: bad stride[%%ld].\\n",
				     i);
			return -1;
		}
		else if (bsizes[i+2] < minbsize)
			minbsize = bsizes[i+2];
	
	next = 0;
	for(i=0; i<niter; i++)
	{
		if (<nonzero>)
		{
			long j, index = i;
			if (next >= minbsize) {
				PyErr_Format(_Error,
					     "nonzeroCoords<typename>: insufficient index space.\\n");
				return -1;
			}
			for(j=0; j<noutargs; j++)
			{
				output[j][next] = index / sstride[j];
                                index %%= sstride[j];
			}
			++ next;
		}
	}
	return 0;
}

SELF_CHECKED_CFUNC_DESCR(nonzeroCoords<typename>, CFUNC_UFUNC);

'''

sort_operator_td = {
  "lessthan"          : "STDC_LT",
  "lessequal"         : "STDC_LE",
  "equals"            : "STDC_EQ",
  "clear"             : "s = 0;",
  "dotstep"           : "s += *ap * *bp;",
  "fillstep"          : "*tin = start;  start += delta;",
  "nonzero"           : "source[i] != 0"
  }

sort_complex_td = {
  "lessthan"          : "NUM_CLT",
  "lessequal"         : "NUM_CLE",
  "equals"            : "NUM_CEQ",
  "clear"             : "s.r = 0; s.i=0;",
  "dotstep"           : "Complex64 t; NUM_CMUL(*ap, *bp, t); NUM_CADD(s, t, s);",
  "fillstep"          : "NUM_CASS(*tin, start); NUM_CADD(start, delta, start);",
  "nonzero"           : "((source[i].r !=0) || (source[i].i != 0))"
  }


BYTES_HEADER       = _HEADER + \
'''
#include <assert.h>

#define NA_ACOPYN(i, o) memcpy(o, i, N)

/* The following is used to copy nbytes of data for each element.   **
** As such it can be used to align any sort of data provided the    **
** output pointers used are aligned                                 */

static int copyNbytes(long dim, long nbytes, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {
    long i, j;
    char *tin  = (char *) input  + inboffset;
    char *tout = (char *) output + outboffset;
    if (dim == 0) {
        for (i=0; i<niters[dim]; i++) {
            for (j=0; j<nbytes; j++) {
                *tout++ = *tin++;
            }
            tin = tin + inbstrides[dim] - nbytes;
            tout = tout + outbstrides[dim]- nbytes;
        }
    }
    else {
        for (i=0; i<niters[dim]; i++) {
            copyNbytes(dim-1, nbytes, niters,
                input,  inboffset  + i*inbstrides[dim],  inbstrides,
                output, outboffset + i*outbstrides[dim], outbstrides);
        }
    }
    return 0;
}

STRIDING_DESCR2(copyNbytes, !CHECK_ALIGN, -1, -1);

/* Copy a data buffer to a new string
**
** Arguments:
**
**   Tuple of iteration values for each dimension of input array.
**   Input buffer object.
**   Input byte offset.
**   Tuple of input byte strides.
**   Size of input data item in bytes.
**
** Returns Python string.
*/

static PyObject *copyToString(PyObject *self, PyObject *args) {
    PyObject *inbuffObj;
    PyObject *nitersObj, *inbstridesObj;
    PyObject *otemp, *outstring;
    long ltemp;

    int nniters, ninbstrides, nargs;
    long nbytes;
    maybelong niters[MAXDIM], inbstrides[MAXDIM], outbstrides[MAXDIM];
    void *inbuffer, *outbuffer;
    long i, inbsize, outbsize, nelements=1, inboffset;
    
    nargs = PyObject_Length(args);
    if (!PyArg_ParseTuple(args, "OOlOl",
            &nitersObj, &inbuffObj,  &inboffset, &inbstridesObj, &nbytes))
        return NULL;
        
    if (!PySequence_Check(nitersObj))
        return PyErr_Format(_Error, "copyToString: invalid shape object");
    if (!PySequence_Check(inbstridesObj))
        return PyErr_Format(_Error, "copyToString: invalid strides object");
        
    nniters = PyObject_Length(nitersObj);
    ninbstrides = PyObject_Length(inbstridesObj);
    if (nniters != ninbstrides)
        return PyErr_Format(PyExc_ValueError,
        "copyToString: shape & strides don't match");
        
    for (i=nniters-1; i>=0; i--) {
        otemp = PySequence_GetItem(nitersObj, i);
        if (PyInt_Check(otemp))
           ltemp = PyInt_AsLong(otemp);
        else if (PyLong_Check(otemp))
           ltemp = PyLong_AsLong(otemp);
        else
           return PyErr_Format(_Error,
                 "copyToString: non-integer shape element");
        nelements *= ltemp;
        niters[nniters-i-1] = ltemp;
        Py_DECREF(otemp);
        otemp = PySequence_GetItem(inbstridesObj, i);
        if (PyInt_Check(otemp))
            inbstrides[nniters-i-1] = PyInt_AsLong(otemp);
        else if (PyLong_Check(otemp))
            inbstrides[nniters-i-1] = PyLong_AsLong(otemp);
        else
           return PyErr_Format(_Error,
                 "copyToString: non-integer stride element");
        Py_DECREF(otemp);
    }
    if (!nelements)
       return PyString_FromStringAndSize("", 0);
    outbstrides[0] = nbytes;
    for (i=1; i<nniters; i++) {
        outbstrides[i] = outbstrides[i-1]*niters[i-1];
    }
    outbsize = outbstrides[nniters-1]*niters[nniters-1];
    outstring = PyString_FromStringAndSize(NULL, outbsize);
    if (!outstring)
        return NULL;
    outbuffer = (void *) PyString_AsString(outstring);
    
    if ((inbsize = NA_getBufferPtrAndSize(inbuffObj, 1, &inbuffer)) < 0)
       return PyErr_Format(_Error,
           "copyToString: Problem with array buffer");

    if (NA_checkOneStriding("copyToString", nniters, niters,
			  inboffset, inbstrides, inbsize, nbytes, 0) ||
	NA_checkOneStriding("copyToString", nniters, niters,
			  0, outbstrides, outbsize, nbytes, 0))
                          return NULL;

    BEGIN_THREADS
    copyNbytes(nniters-1, nbytes, niters,
          inbuffer, inboffset, inbstrides, outbuffer, 0, outbstrides);
    END_THREADS
    return outstring;
}

/* chooseXbytes functions are called as uFuncs... */

enum CLIP_MODE {
  CLIPPED,
  WRAPPED,
  RAISE
};

#define wrap(i, max) \
     while(i < 0)    \
         i += max;   \
     while(i >= max)  \
        i -= max;

static int takeNbytes(long niter, long ninargs, long noutargs,
                         void **buffers, long *bsizes)
{
  maybelong  i, cMode, N;
  maybelong *scatteredstrides, *scatteredshape, **indices;
  char  *gathered, *scattered;
  maybelong nindices = ninargs-4, outi = ninargs+noutargs-1;

  if (NA_checkIo("takeNbytes", 4, 1, MIN(ninargs, 4), noutargs))
     return -1;

  if (nindices == 0)
     return 0;
  
  if (NA_checkOneCBuffer("takeNbytes", 2, buffers[0], bsizes[0], sizeof(maybelong)))
     return -1;
  else {
     cMode            =  ((maybelong *) buffers[0])[0];
     N                =  ((maybelong *) buffers[0])[1];
  }

  if (NA_checkOneCBuffer("takeNbytes", nindices, buffers[2], bsizes[2], sizeof(maybelong)))
     return -1;
  else {
     scatteredstrides =  (maybelong *)  buffers[2];
  }
  
  if (NA_checkOneCBuffer("takeNbytes", nindices, buffers[3], bsizes[3], sizeof(maybelong)))
     return -1;
  else {
     scatteredshape   =  (maybelong *)  buffers[3];
  }
  
  if (NA_checkOneStriding("takeNBytes", nindices, scatteredshape, 0, scatteredstrides, bsizes[1], N, 0))
     return -1;
  else
     scattered        =  (char *)   buffers[1];

  for(i=4; i<nindices; i++)
     if (NA_checkOneCBuffer("takeNbytes", niter, buffers[i], bsizes[i], sizeof(maybelong)))
        return -1;
  indices          =  (maybelong **) &buffers[4];

  if (NA_checkOneCBuffer("takeNbytes", niter*N, buffers[outi], bsizes[outi], 1))
     return -1;
  else 
     gathered         =  (char *)  buffers[ninargs+noutargs-1];

  switch( cMode )
    {
    case WRAPPED:
      for(i=0; i<niter; i++)
        {
          maybelong j, index;
          for(j=index=0; j<nindices; j++)
            {
              maybelong k = indices[j][i];
	      wrap(k, scatteredshape[j]);
              index += scatteredstrides[j]*k;
            }
          memcpy( &gathered[i*N], scattered+index, N);
        }
      break;
    case CLIPPED:
    default:
      for(i=0; i<niter; i++)
        {
          maybelong j, index;
          for(j=index=0; j<nindices; j++)
            {
              maybelong k = indices[j][i];
              if (k < 0)
                k = 0;
              else if (k >= scatteredshape[j])
                k = scatteredshape[j]-1;
              index += scatteredstrides[j]*k;
            }
          memcpy( &gathered[i*N], scattered+index, N);
        }
      break;
      case RAISE:
      for(i=0; i<niter; i++)
        {
          maybelong j, index;
          for(j=index=0; j<nindices; j++)
            {
              maybelong k = indices[j][i];
              if (k < 0)
                k += scatteredshape[j];
              if (k >= scatteredshape[j]) {
                 PyErr_Format(PyExc_IndexError,
                   "Index[" ML_DEC "," ML_DEC "]=" ML_DEC
                   " out of range[" ML_DEC "]",
                   i, j, k, scatteredshape[j]);
                   return -1;
              }
              index += scatteredstrides[j]*k;
            }
          memcpy( &gathered[i*N], scattered+index, N);
        }
      break;
    }
  return 0;
}

SELF_CHECKED_CFUNC_DESCR(takeNbytes, CFUNC_UFUNC);

static int putNbytes(long niter, long ninargs, long noutargs,
                         void **buffers, long *bsizes)
{
  maybelong  i, cMode, N;
  maybelong  *scatteredstrides, *scatteredshape, **indices;
  char   *gathered, *scattered;
  long nindices = ninargs-4, outi = ninargs+noutargs-1;

  if (nindices == 0)
     return 0;
  
  if (NA_checkIo("putNbytes", 4, 1, MIN(ninargs, 4), noutargs))
     return -1;

  if (NA_checkOneCBuffer("putNbytes", 2, buffers[0], bsizes[0], sizeof(maybelong)))
     return -1;
  else {
     cMode            =  ((maybelong *) buffers[0])[0];
     N                =  ((maybelong *) buffers[0])[1];
  }

  if (NA_checkOneCBuffer("putNbytes", niter*N, buffers[1], bsizes[1], 1))
     return -1;
  else 
     gathered  =  (char *)  buffers[1];

  if (NA_checkOneCBuffer("putNbytes", nindices, buffers[2], bsizes[2], sizeof(maybelong)))
     return -1;
  else {
     scatteredstrides =  (maybelong *)  buffers[2];
  }
  
  if (NA_checkOneCBuffer("putNbytes", nindices, buffers[3], bsizes[3], sizeof(maybelong)))
     return -1;
  else {
     scatteredshape   =  (maybelong *)  buffers[3];
  }

  for(i=4; i<nindices; i++)
     if (NA_checkOneCBuffer("putNbytes", niter, buffers[i], bsizes[i], sizeof(maybelong)))
        return -1;
  indices          =  (maybelong **) &buffers[4];

  if (NA_checkOneStriding("putNBytes", nindices, scatteredshape, 0, scatteredstrides, bsizes[outi], N, 0))
    return -1;
  else
    scattered        =  (char *)   buffers[outi];

  switch( cMode )
    {
    case WRAPPED:
      for(i=0; i<niter; i++)
        {
          maybelong j, index;
          for(j=index=0; j<nindices; j++)
            {
              maybelong k = indices[j][i];
	      wrap(k, scatteredshape[j]);
              index += scatteredstrides[j]*k;
            }
          memcpy( scattered+index, &gathered[i*N], N);
        }
      break;
    case CLIPPED:
    default:
      for(i=0; i<niter; i++)
        {
          maybelong j, index;
          for(j=index=0; j<nindices; j++)
            {
              maybelong k = indices[j][i];
              if (k < 0)
                k = 0;
              else if (k >= scatteredshape[j])
                k = scatteredshape[j]-1;
              index += scatteredstrides[j]*k;
            }
          memcpy( scattered+index, &gathered[i*N], N);
        }
      break;
    case RAISE:
      for(i=0; i<niter; i++)
        {
          maybelong j, index;
          for(j=index=0; j<nindices; j++)
            {
              maybelong k = indices[j][i];
              if (k < 0)
                k += scatteredshape[j];
              if (k >= scatteredshape[j]) {
                 PyErr_Format(PyExc_IndexError,
                   "Index[" ML_DEC "," ML_DEC "]=" ML_DEC
                   " out of range[" ML_DEC "]",
                   i, j, k, scatteredshape[j]);
                   return -1;              
              }
              index += scatteredstrides[j]*k;
            }
          memcpy( scattered+index, &gathered[i*N], N);
        }
      break;
    }
  return 0;
}

SELF_CHECKED_CFUNC_DESCR(putNbytes, CFUNC_UFUNC);
'''

COPY_TEMPLATE = \
'''
/*******************************************
*                                          *
* These copy data to a contiguous buffer.  *
* They do not handle non-aligned data.     *
* Offsets and Strides are in byte units    *
*                                          *
*******************************************/

static int copy<size>bytes(long dim, long dummy, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {
    long i;
    char *tin     = (char *) input  + inboffset;
    char *tout    = (char *) output + outboffset;
    if (dim == 0) {
        for (i=0; i<niters[dim]; i++) {
            NA_ACOPY<size>(tin, tout);
            tin  += inbstrides[dim];
            tout += outbstrides[dim];
        }
    }
    else {
        for (i=0; i<niters[dim]; i++) {
            copy<size>bytes(dim-1, dummy, niters,
               input,  inboffset  + i*inbstrides[dim],  inbstrides,
               output, outboffset + i*outbstrides[dim], outbstrides);
        }
    }
    return 0;
}

STRIDING_DESCR2(copy<size>bytes, CHECK_ALIGN, <size>, <size>);
'''

ALIGN_TEMPLATE  = \
'''
static int align<size>bytes(long dim, long dummy, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {
	return copyNbytes(dim, <size>, niters, input, inboffset, inbstrides, 
			  output, outboffset, outbstrides);
}

STRIDING_DESCR2(align<size>bytes, !CHECK_ALIGN, <size>, <size>);

'''

BYTESWAP_TEMPLATE = \
'''
/******* byteswap *****/

static int byteswap<sizename>(long dim, long dummy, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {

    long i;
    char *tin  = (char *) input  + inboffset;
    char *tout = (char *) output + outboffset;
    if (dim == 0) {
        for (i=0; i<niters[dim]; i++) {
            char t[<size>];
            NA_COPY<size>(tin, t);
            <swapkind><size>(t, tout);
            tin  += inbstrides[dim];
            tout += outbstrides[dim];
        }
    }
    else {
        for (i=0; i<niters[dim]; i++) {
            byteswap<sizename>(dim-1, dummy, niters,
               input,  inboffset  + i*inbstrides[dim],  inbstrides,
               output, outboffset + i*outbstrides[dim], outbstrides);
        }
    }
    return 0;
}

STRIDING_DESCR2(byteswap<sizename>, !CHECK_ALIGN, <size>, <size>);

'''

CHOOSE_TEMPLATE = \
'''
static int choose<size>bytes(long niter, long ninargs, long noutargs,
                         void **buffers, long *bsizes)
{
  maybelong i, cMode, maxP, N, *selector;
  char **population, *output;
  int outi = ninargs + noutargs - 1;

  if (NA_checkIo("choose<size>bytes", 2, 1, MIN(ninargs,2), noutargs))
     return -1;

  if (NA_checkOneCBuffer("choose<size>bytes", 2, buffers[0], bsizes[0], sizeof(maybelong)))
     return -1;
  else {
     cMode        =  ((maybelong *) buffers[0])[0];
     N            =  ((maybelong *) buffers[0])[1];
  }

  if (NA_checkOneCBuffer("choose<size>bytes", niter, buffers[1], bsizes[1],
     sizeof(maybelong)))
        return -1;
  else
     selector     =  (maybelong *)  buffers[1];

  if (ninargs-2 == 0)
    return 0;
  else
    maxP = ninargs-2;
  for(i=2; i<ninargs; i++)
     if (NA_checkOneCBuffer("choose<size>bytes", niter,
        buffers[i], bsizes[i], <size>))
           return -1;
  population      =  (char **) &buffers[2];

  if (NA_checkOneCBuffer("choose<size>bytes", niter,
     buffers[outi], bsizes[outi], <size>))
        return -1;
  else
     output       =  (char *)   buffers[outi];

  if (maxP == 0) 
	  return 0;

  switch(cMode)
    {
    case WRAPPED:
      for(i=0; i<niter; i++)
        {
          maybelong j = selector[i];
	  wrap(j, maxP);
          NA_ACOPY<size>(&population[j][i*<size>], &output[i*<size>]);
        }
      break;
    default:
    case CLIPPED:
      for(i=0; i<niter; i++)
        {
          maybelong j = selector[i];
          if (j < 0)
            j = 0;
          else if (j >= maxP)
            j = maxP-1;
          NA_ACOPY<size>(&population[j][i*<size>], &output[i*<size>]);
        }
      break;
    case RAISE:
      for(i=0; i<niter; i++)
        {
          maybelong j = selector[i];
          if ((j < 0) || (j >= maxP)) {
		  PyErr_Format(
			  PyExc_IndexError,
			  "selector[" ML_DEC "] = " ML_DEC
                          " out of range: 0.." ML_DEC, i, j, maxP-1);
		  return -1;                           
          }
          NA_ACOPY<size>(&population[j][i*<size>], &output[i*<size>]);
        }
      break;
    }
  return 0;
}

SELF_CHECKED_CFUNC_DESCR(choose<size>bytes, CFUNC_UFUNC);
'''

BYTES_TEMPLATE = ( COPY_TEMPLATE +
                   ALIGN_TEMPLATE +
                   BYTESWAP_TEMPLATE +
                   CHOOSE_TEMPLATE )

_METHODS_DEF = \
'''
static PyMethodDef <module>Methods[] = {
<module_methods>
\t{NULL,      NULL}        /* Sentinel */
};
'''

ADDMETHOD_TEMPLATE = \
'''\t{ "%s", %s, METH_VARARGS},
'''

_TAIL = \
'''
/* platform independent*/
#ifdef MS_WIN32
__declspec(dllexport)
#endif
void init<module>(void) {
    PyObject *m, *d;
    m = Py_InitModule("<module>", <module>Methods);
    d = PyModule_GetDict(m);
    _Error = PyErr_NewException("<module>.error", NULL, NULL);
    PyDict_SetItemString(d, "error", _Error);
    import_libnumarray();
    PyDict_SetItemString(d, "functionDict", init_funcDict());
}
'''

#*********************************************************************#
#                       data for _ufuncmodule.c                       #
#*********************************************************************#
UFUNC_HEADER = \
'''
#include <Python.h>
#include <stdio.h>
#include <math.h>
#include "libnumarray.h"

#ifdef MS_WIN32
#pragma warning(once : 4244)
#endif

#define logical_and(arg1, arg2) (arg1 != 0) & (arg2 != 0)
#define logical_or(arg1, arg2)  (arg1 != 0) | (arg2 != 0)
#define logical_xor(arg1, arg2) ((arg1 != 0) ^ (arg2 != 0)) & 1
#define ufmaximum(arg1, arg2) (((temp1=arg1) > (temp2=arg2)) ? temp1 : temp2)
#define ufminimum(arg1, arg2) (((temp1=arg1) < (temp2=arg2)) ? temp1 : temp2)

static PyObject *_Error;
'''

UFUNC_UNARY_TEMPLATE = \
'''
static int <pyname>_<intype0>_vector(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) {
    long i;
    <intype0> *tin = (<intype0> *) buffers[0];
    <outtype0> *tout = (<outtype0> *) buffers[1];
    <optional_declarations>
    BEGIN_THREADS
    for (i=0; i<niter; i++, tin++, tout++) {
        <operator1>
    }
    END_THREADS
    return 0;
}

UFUNC_DESCR2(<pyname>_<intype0>_vector, sizeof(<intype0>), sizeof(<outtype0>));
'''

UFUNC_VECTOR_VECTOR = \
'''
static int <pyname>_<intype0>_vector_vector(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) {
    long i;
    <intype0>  *tin1  = (<intype0> *) buffers[0];
    <intype1>  *tin2  = (<intype1> *) buffers[1];
    <outtype0> *tout  = (<outtype0> *) buffers[2];
    <optional_declarations>
    BEGIN_THREADS
    for (i=0; i<niter; i++, tin1++, tin2++, tout++) {
        <operator_vv>
    }
    END_THREADS
    return 0;
}

UFUNC_DESCR3(<pyname>_<intype0>_vector_vector, sizeof(<intype0>), sizeof(<intype1>), sizeof(<outtype0>));

'''

UFUNC_VECTOR_SCALAR = \
'''
static int <pyname>_<intype0>_vector_scalar(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) {
    long i;
    <intype0> *tin1     = (<intype0> *) buffers[0];
    <intype1> tscalar   = *(<intype1> *) buffers[1];
    <outtype0> *tout    = (<outtype0> *) buffers[2];
    <optional_declarations>
    BEGIN_THREADS
    for (i=0; i<niter; i++, tin1++, tout++) {
        <operator_vs>
    }
    END_THREADS
    return 0;
}

UFUNC_DESCR3vs(<pyname>_<intype0>_vector_scalar, sizeof(<intype0>), sizeof(<intype1>), sizeof(<outtype0>));

'''

UFUNC_SCALAR_VECTOR = \
'''
static int <pyname>_<intype0>_scalar_vector(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) {
    long i;
    <intype0> tscalar = *(<intype0> *) buffers[0];
    <intype1> *tin2   =  (<intype1> *) buffers[1];
    <outtype0> *tout  = (<outtype0> *) buffers[2];
    <optional_declarations>
    BEGIN_THREADS
    for (i=0; i<niter; i++, tin2++, tout++) {
        <operator_sv>
    }
    END_THREADS
    return 0;
}

UFUNC_DESCR3sv(<pyname>_<intype0>_scalar_vector, sizeof(<intype0>), sizeof(<intype1>), sizeof(<outtype0>));

'''

UFUNC_REDUCE = \
'''
static void _<pyname>_<intype0>_reduce(long dim, long dummy, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {
    long i;
    <intype0>  *tin   = (<intype0> *) ((char *) input  + inboffset);
    <outtype0> *tout  = (<outtype0> *) ((char *) output + outboffset);
    <outtype0> net;
    <optional_declarations>
    if (dim == 0) {
        net = *tout;
        for (i=1; i<niters[dim]; i++) {
            tin = (<intype0> *) ((char *) tin + inbstrides[dim]);
            <operator_reduce>
        }
        *tout = net;
    }
    else {
        for (i=0; i<niters[dim]; i++) {
            _<pyname>_<intype0>_reduce(dim-1, dummy, niters,
               input,  inboffset  + i*inbstrides[dim],  inbstrides,
               output, outboffset + i*outbstrides[dim], outbstrides);
        }
    }
}

static int <pyname>_<intype0>_reduce(long dim, long dummy, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {
            BEGIN_THREADS
            _<pyname>_<intype0>_reduce(dim, dummy, niters,
                             input, inboffset, inbstrides, output, outboffset, outbstrides );
            END_THREADS
            return 0;
}

STRIDING_DESCR2(<pyname>_<intype0>_reduce, CHECK_ALIGN, sizeof(<intype0>), sizeof(<outtype0>));
'''

UFUNC_ACCUMULATE = \
'''
static void _<pyname>_<intype0>_accumulate(
            long dim, long dummy, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {
    long i;
    <intype0> *tin   = (<intype0> *) ((char *) input  + inboffset);
    <outtype0> *tout = (<outtype0> *) ((char *) output + outboffset);
    <outtype0> lastval;
    <optional_declarations>
    if (dim == 0) {
        for (i=1; i<niters[dim]; i++) {
            lastval = *tout;
            tin = (<intype0> *) ((char *) tin + inbstrides[dim]);
            tout = (<outtype0> *) ((char *) tout + outbstrides[dim]);
            <operator_accumulate>
        }
    }
    else {
        for (i=0; i<niters[dim]; i++) {
            _<pyname>_<intype0>_accumulate(dim-1, dummy, niters,
               input,  inboffset  + i*inbstrides[dim],  inbstrides,
               output, outboffset + i*outbstrides[dim], outbstrides);
        }
    }
}

static int <pyname>_<intype0>_accumulate(
            long dim, long dummy, maybelong *niters,
            void *input,  long inboffset,  maybelong *inbstrides,
            void *output, long outboffset, maybelong *outbstrides) {
            BEGIN_THREADS
            _<pyname>_<intype0>_accumulate(dim, dummy, niters,
                  input, inboffset, inbstrides, output, outboffset, outbstrides);
            END_THREADS
            return 0;
}

STRIDING_DESCR2(<pyname>_<intype0>_accumulate, CHECK_ALIGN, sizeof(<intype0>), sizeof(<outtype0>));
'''

UFUNC_PVECTOR_PVECTOR = \
'''
static int <pyname>_<intype0>_vector_vector(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) {
    long i;
    <intype0>  *tin1  = (<intype0> *) buffers[0];
    <intype1>  *tin2  = (<intype1> *) buffers[1];
    <outtype0> *tout  = (<outtype0> *) buffers[2];
    <optional_declarations>
    BEGIN_THREADS
    for (i=0; i<niter; i++, tin1++, tin2++, tout++) {
        <operator_pp>
    }
    END_THREADS
    return 0;
}

UFUNC_DESCR3(<pyname>_<intype0>_vector_vector, sizeof(<intype0>), sizeof(<intype1>), sizeof(<outtype0>));

'''

UFUNC_COMPARE_TEMPLATE = ( UFUNC_VECTOR_VECTOR +
                           UFUNC_VECTOR_SCALAR + 
                           UFUNC_SCALAR_VECTOR )

UFUNC_BINARY_TEMPLATE = ( UFUNC_COMPARE_TEMPLATE +
                          UFUNC_ACCUMULATE + 
                          UFUNC_REDUCE )

#*********************************************************************#
#                   data for filling function table                   #
#*********************************************************************#
_CFUNCDICT_HEADER = \
'''
static PyObject *init_funcDict(void) {
    PyObject *dict;
    dict = PyDict_New();
'''

_ADDCFUNC_TEMPLATE = \
'''    NA_add_cfunc(dict, "%s", (void *) &%s_descr);
'''

_CFUNCDICT_TAIL = \
'''    return dict;
}
'''

# The following templates are the most complex. There are a number of
# different forms of templates, for unary and binary functions, and then
# different forms for the computation line (operator, function plus
# special versions for integer multiply and divide for overflow and
# divide by zero checking. More may be necesary in the future. These
# are defined here since most are used multiple times in the ufunc
# definition table.
# td stands for 'template dict'

macro_td = {
    "operator1"          :"<operator>(*tin, *tout);",
    "operator_vv"        :"<operator>(*tin1, *tin2, *tout);",
    "operator_vs"        :"<operator>(*tin1, tscalar, *tout);",
    "operator_sv"        :"<operator>(tscalar, *tin2, *tout);",
    "operator_reduce"    :"<operator>(net, *tin, net);",
    "operator_accumulate":"<operator>(lastval ,*tin, *tout);"
    }

function_td = {
    "operator1"          :"*tout = <operator>(*tin);",
    "operator_vv"        :"*tout = <operator>(*tin1, *tin2);",
    "operator_vs"        :"*tout = <operator>(*tin1, tscalar);",
    "operator_sv"        :"*tout = <operator>(tscalar, *tin2);",
    "operator_reduce"    :"net = <operator>(net, *tin);",
    "operator_accumulate":"*tout = <operator>(lastval ,*tin);"
    }

mask_td = {
    "operator_pp"        :"*tout = <operator>(tin1, tin2);",
}

operator_td = {
    "operator1"          :"*tout = <operator>*tin;",
    "operator_vv"        :"*tout = *tin1 <operator> *tin2;",
    "operator_vs"        :"*tout = *tin1 <operator> tscalar;",
    "operator_sv"        :"*tout = tscalar <operator> *tin2;",
    "operator_reduce"    :"net = net <operator> *tin;",
    "operator_accumulate":"*tout = lastval <operator> *tin;"
    }

int_divide_td = {
    "operator_vv":"*tout = ((*tin2==0) ? int_dividebyzero_error(*tin2, *tin1) : *tin1 <operator> *tin2);",
    "operator_vs":"*tout = ((tscalar==0) ? int_dividebyzero_error(tscalar, *tin1) : *tin1 <operator> tscalar);",
    "operator_sv":"*tout = ((*tin2==0) ? int_dividebyzero_error(*tin2, 0) : tscalar <operator> *tin2);",
    "operator_reduce":"net = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : net <operator> *tin);",
    "operator_accumulate":"*tout = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : lastval <operator> *tin);"
    }

int_truedivide_td = {
    "operator_vv":"*tout = ((*tin2==0) ? int_dividebyzero_error(*tin2, *tin1) : *tin1 <operator> (double) *tin2);",
    "operator_vs":"*tout = ((tscalar==0) ? int_dividebyzero_error(tscalar, *tin1) : *tin1 <operator> (double) tscalar);",
    "operator_sv":"*tout = ((*tin2==0) ? int_dividebyzero_error(*tin2, 0) : tscalar <operator> (double) *tin2);",
    "operator_reduce":"net = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : net <operator> (double) *tin);",
    "operator_accumulate":"*tout = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : lastval <operator> (double) *tin);"
    }

int_floordivide_td = {
    "operator_vv":"*tout = ((*tin2==0) ? int_dividebyzero_error(*tin2, *tin1) : floor(*tin1 <operator> (double) *tin2));",
    "operator_vs":"*tout = ((tscalar==0) ? int_dividebyzero_error(tscalar, *tin1) : floor(*tin1 <operator> (double) tscalar));",
    "operator_sv":"*tout = ((*tin2==0) ? int_dividebyzero_error(*tin2, 0) : floor(tscalar <operator> (double) *tin2));",
    "operator_reduce":"net = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : floor(net <operator> (double) *tin));",
    "operator_accumulate":"*tout = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : floor(lastval <operator> (double) *tin));"
    }

int8_check = \
"""if (temp > 127) temp = int_overflow_error(127.);
if (temp < -128) temp = int_overflow_error(-128.);"""

uint8_check = \
"""if (temp > 255) temp = int_overflow_error(255.);"""

int16_check = \
"""if (temp > 32767) temp = int_overflow_error(32767.);
if (temp < -32768) temp = int_overflow_error(-32768);"""

uint16_check = \
"""if (temp > 65535) temp = int_overflow_error(65535.);"""

int32_check = \
"""if (temp > 2147483647) temp = (Float64) int_overflow_error(2147483647.);
if (temp < (-2147483648.)) temp = (Float64) int_overflow_error(-2147483648.);"""

uint32_check = \
"""if (temp > 4294967295U) temp = (Float64) int_overflow_error(4294967295.);"""

int64_check = \
"""if (smult64_overflow(<p1>, <p2>)) temp = (Float64) int_overflow_error(4611686018427387903.);"""

uint64_check = \
"""if (umult64_overflow(<p1>, <p2>)) temp = (Float64) int_overflow_error(9223372036854775807.);"""

# A template used below to generate multiple templates! (since they
# are all so similar. These are used by make_int_template_dict just below.
intX_td = {
    "operator_vv": ("*tin1",   "*tin2",   "*tout"),
    "operator_vs": ("*tin1",   "tscalar", "*tout"),
    "operator_sv": ("tscalar", "*tin2",   "*tout"),
    "operator_reduce": ("net", "*tin",    "net"),
    "operator_accumulate": ("lastval", "*tin", "*tout"),
    }

intX_mult_template = """temp = ((<uptype>) <p1>) %<operator> ((<uptype>) <p2>);
    <checkstr>
    <p3> = (<type>) temp;"""

def make_int_template_dict(uptype, sizestr, checkstr):
    td = {}
    for key in intX_td.keys():
        td[key] = intX_mult_template % \
                {"type": sizestr,
                 "checkstr": (checkstr % { "p1":intX_td[key][0],
                                           "p2":intX_td[key][1] }),
                 "uptype":uptype,
                 "p1":intX_td[key][0],
                 "p2":intX_td[key][1],
                 "p3":intX_td[key][2],
                 }
    return td

#*********************************************************************#
#                   data lists for type conversion                    #
#*********************************************************************#

# These are used by the code to generate dictionaries with the keys
# defined in tdictfields and the corresponding values in typeconfig
# list items. There will be one dictionary for each element in
# typeconfig. It is done this way to make the table more readable
# and easier to edit.

def hasUInt64():
  return "--hasUInt64" in sys.argv

def hasFloat128():
  return "--hasFloat128" in sys.argv

typeconfig = [
    ["Bool",   "(Bool) isNonZERO",   "PY_BOOL_CHAR",   ""],
    ["Int8",   "(Int8)",             "PY_INT8_CHAR",   ""],
    ["Int16",  "(Int16)",            "PY_INT16_CHAR",  ""],
    ["Int32",  "(Int32)",            "PY_INT32_CHAR",  ""],
    ["UInt32",  "(UInt32)",          "PY_UINT32_CHAR",  ""],
    ["UInt8",  "(UInt8)",            "PY_UINT8_CHAR",  "(Int16)"],
    ["UInt16", "(UInt16)",           "PY_UINT16_CHAR", "(Int32)"],
    ["Float32","(Float32)",          "PY_FLOAT32_CHAR",""],
    ["Float64","(Float64)",          "PY_FLOAT64_CHAR",""],
    ["Complex32", "NUM_TO_COMPLEX",  "PY_COMPLEX32_CHAR",""],
    ["Complex64", "NUM_TO_COMPLEX",  "PY_COMPLEX64_CHAR",""],
    ]

typeconfig.append(["Int64", "(Int64)", "PY_LONG_CHAR", ""])
if hasUInt64():
  typeconfig.append(["UInt64", "(UInt64)", "PY_LONG_CHAR", ""])

if hasFloat128():
  typeconfig.append(["Float128", "(Float128)", "PY_FLOAT128_CHAR", ""])
  typeconfig.append(["Complex128", "NUM_TO_COMPLEX", "PY_COMPLEX128_CHAR", ""])

class ConvParams:
    def __init__(self, typename, typecast, pybuildchar, pycast):
        self.typename = typename
        self.typecast = typecast
        self.pybuildchar = pybuildchar
        self.pycast = pycast

bytesconfig = [
    ["1", "Int8"],
    ["2", "Int16"],
    ["4", "Int32"],
    ["8", "Float64"],
    ["16", "Complex64"],  
    ];

class BytesParams:
  def __init__(self, size, type):
    self.size = size
    self.sizename = str(size) + "bytes"
    self.typename = type
    self.swapkind = "NA_SWAP"

NBytesParams = BytesParams("N","AnyType")

class ComplexBytesParams:
  def __init__(self, size, type):
    self.size = size
    self.sizename = type
    self.typename = type
    self.swapkind = "NA_COMPLEX_SWAP"

Complex32BytesCfg = ComplexBytesParams(8, "Complex32")
Complex64BytesCfg = ComplexBytesParams(16, "Complex64")

    
#*********************************************************************#
#                   data lists for ufunc generation                   #
#*********************************************************************#
# Some implicit rules:
# Bool never returned by math functions
# Bool always returned by comparison functions
# Math operators (+-*/**) generally return all types, double math functions
#  only return float types, special purpose functions (floor, ceil, etc)
#  only return one kind (int or float)
# Define various forms for signatures. New ufuncs may require new
# signatures lists. These are used in the ufunc definition table
# repeatedly.

generic_operator_sigs = [
    ('Bool',    'Int8'),
    ('Int8',    'Int8'),
    ('Int16',   'Int16'),
    ('Int32',   'Int32'),
    ('UInt8',   'UInt8'),
    ('UInt16',  'UInt16'),
    ('UInt32',  'UInt32'),
    ('Float32', 'Float32'),
    ('Float64', 'Float64')
    ]

generic_mathfunction_sigs = [
    ('Bool',    'Float64'),
    ('Int8',    'Float64'),
    ('Int16',   'Float64'),
    ('Int32',   'Float64'),
    ('UInt8',   'Float64'),
    ('UInt16',  'Float64'),
    ('UInt32',  'Float64'),
    ('Float32', 'Float32'),
    ('Float64', 'Float64')
    ]

generic_comparisonfunction_sigs = [
    ('Bool',    'Bool'),
    ('Int8',    'Bool'),
    ('Int16',   'Bool'),
    ('Int32',   'Bool'),
    ('UInt8',   'Bool'),
    ('UInt16',  'Bool'),
    ('UInt32',   'Bool'),
    ('Float32', 'Bool'),
    ('Float64', 'Bool')
    ]

generic_int_sigs = [
    ('Bool',    'Bool'),
    ('Int8',    'Int8'),
    ('Int16',   'Int16'),
    ('Int32',   'Int32'),
    ('UInt8',   'UInt8'),
    ('UInt16',  'UInt16'),
    ('UInt32',  'UInt32')
    ]

truedivide_int_sigs = [
  ('Bool',    'Float32'),
  ('Int8',    'Float32'),
  ('Int16',   'Float32'),
  ('Int32',   'Float32'),
  ('UInt8',   'Float32'),
  ('UInt16',  'Float32'),
  ('UInt32',  'Float32')
  ]

generic_operator_sigs.append(('Int64', 'Int64'))
generic_mathfunction_sigs.append(('Int64', 'Float64'))
generic_comparisonfunction_sigs.append(('Int64', 'Bool'))
generic_int_sigs.append(('Int64', 'Int64'))
truedivide_int_sigs.append(('Int64', 'Float64'))


if hasUInt64():
  generic_operator_sigs.append(('UInt64', 'UInt64'))
  generic_mathfunction_sigs.append(('UInt64', 'Float64'))
  generic_comparisonfunction_sigs.append(('UInt64', 'Bool'))
  generic_int_sigs.append(('UInt64', 'UInt64'))
  truedivide_int_sigs.append(('UInt64', 'Float64'))

generic_float_sigs = [
    ('Float32', 'Float32'),
    ('Float64', 'Float64')
    ]

complex_complex_sigs = [
    ('Complex32', 'Complex32'),
    ('Complex64', 'Complex64')
    ]

complex_real_sigs = [
    ('Complex32', 'Float32'),
    ('Complex64', 'Float64')
    ]

complex_bool_sigs = [
    ('Complex32', 'Bool'),
    ('Complex64', 'Bool')
    ]
    
opt_mult_decl = "Int32 temp;"
opt_mult32_decl = "Float64 temp;"
opt_mult64_decl = "Int64 temp;"
opt_minmax_decl = "<intype0> temp1, temp2;"


# ============================================================================
#          IMPORTANT:  no <>-sugared strings below this point

# translate <var> --> %(var)s in templates seen *so far*
template.sugar_dict(globals())  

# ============================================================================
    
# Use function to define all variants of integer multiply.
multiply_int8_td   = make_int_template_dict("Int32",   "Int8",   int8_check)
multiply_uint8_td  = make_int_template_dict("Int32",   "UInt8",  uint8_check)
multiply_int16_td  = make_int_template_dict("Int32",   "Int16",  int16_check)
multiply_uint16_td = make_int_template_dict("Int32",   "UInt16", uint16_check)
multiply_uint32_td = make_int_template_dict("Float64", "UInt32", uint32_check)
multiply_int32_td  = make_int_template_dict("Float64", "Int32",  int32_check)
multiply_int64_td  = make_int_template_dict("Int64",   "Int64",  int64_check)
multiply_uint64_td = make_int_template_dict("UInt64",  "UInt64", uint64_check)

# These are used by the code to generate dictionaries with the keys
# defined in ufdictfields and the corresponding values in ufuncconfigs
# list items. There will be one dictionary for each element in
# ufuncconfigs. It is done this way to make the table more readable
# and easier to edit.

# Multiple entries for function names are permitted as long as the
# signatures have no overlaps.

# pyname    ins outs  form               sigs                  optional_decls

ufuncconfigs = [
["minus",    1, 1, (operator_td, "-"),   generic_operator_sigs, ""],
["add",      2, 1, (operator_td, "+"),   generic_operator_sigs,""],
["subtract", 2, 1, (operator_td, "-"),     generic_operator_sigs, ""],
["multiply", 2, 1, (multiply_int8_td,  "*"), [('Int8', 'Int8')], opt_mult_decl],
["multiply", 2, 1, (multiply_uint8_td,  "*"), [('UInt8', 'UInt8')], opt_mult_decl],
["multiply", 2, 1, (multiply_int16_td,  "*"), [('Int16', 'Int16')], opt_mult_decl],
["multiply", 2, 1, (multiply_uint16_td,  "*"), [('UInt16', 'UInt16')], opt_mult_decl],
["multiply", 2, 1, (multiply_int32_td,  "*"), [('Int32', 'Int32')], opt_mult32_decl],
["multiply", 2, 1, (multiply_uint32_td,  "*"), [('UInt32', 'UInt32')], opt_mult32_decl],
["multiply", 2, 1, (multiply_int64_td,  "*"), [('Int64', 'Int64')], opt_mult64_decl],
["multiply", 2, 1, (multiply_uint64_td,  "*"), [('UInt64', 'UInt64')], opt_mult64_decl],
["multiply", 2, 1, (operator_td, "*"),   generic_float_sigs, ""],
["divide",   2, 1, (int_divide_td,"/"),  generic_int_sigs, ""],
["divide",   2, 1, (operator_td, "/"),   generic_float_sigs, ""],
["floor_divide",   2, 1, (int_floordivide_td,"/"),  generic_int_sigs, ""],
["floor_divide",   2, 1, (macro_td, "NUM_FLOORDIVIDE"), generic_float_sigs, ""],
["true_divide",   2, 1, (int_truedivide_td,"/"),  truedivide_int_sigs, ""],
["true_divide",   2, 1, (operator_td, "/"),   generic_float_sigs, ""],
["remainder",2, 1, (int_divide_td, " % "), generic_int_sigs, ""],
["remainder",2, 1, (function_td, "fmod"),   generic_float_sigs, ""],
["power",  2, 1, (function_td, "num_pow"),   generic_operator_sigs, ""],
["abs",    1, 1, (function_td, "fabs"),  generic_operator_sigs, ""],
["sin",    1, 1, (function_td, "sin"),  generic_mathfunction_sigs, ""],
["cos",    1, 1, (function_td, "cos"),  generic_mathfunction_sigs, ""],
["tan",    1, 1, (function_td, "tan"),  generic_mathfunction_sigs, ""],
["arcsin", 1, 1, (function_td, "asin"), generic_mathfunction_sigs, ""],
["arccos", 1, 1, (function_td, "acos"), generic_mathfunction_sigs, ""],
["arctan", 1, 1, (function_td, "atan"), generic_mathfunction_sigs, ""],
["arctan2",2, 1, (function_td, "atan2"), generic_mathfunction_sigs, ""],
["log",    1, 1, (function_td, "num_log"),  generic_mathfunction_sigs, ""],
["log10",  1, 1, (function_td, "num_log10"),generic_mathfunction_sigs, ""],
["exp",    1, 1, (function_td, "exp"),  generic_mathfunction_sigs, ""],
["sinh",   1, 1, (function_td, "sinh"), generic_mathfunction_sigs, ""],
["cosh",   1, 1, (function_td, "cosh"), generic_mathfunction_sigs, ""],
["tanh",   1, 1, (function_td, "tanh"), generic_mathfunction_sigs, ""],
["arcsinh",   1, 1, (function_td, "num_asinh"), generic_mathfunction_sigs, ""],
["arccosh",   1, 1, (function_td, "num_acosh"), generic_mathfunction_sigs, ""],
["arctanh",   1, 1, (function_td, "num_atanh"), generic_mathfunction_sigs, ""],

["ieeemask",  2, 1, (mask_td, "NA_IeeeSpecial32"), [('Float32', 'Int32', 'Bool')], "",
 UFUNC_PVECTOR_PVECTOR, ['vector_vector'] ],
["ieeemask",  2, 1, (mask_td, "NA_IeeeSpecial64"), [('Float64', 'Int32', 'Bool')], "",
 UFUNC_PVECTOR_PVECTOR, ['vector_vector'] ],

["sqrt",   1, 1, (function_td, "sqrt"), generic_mathfunction_sigs, ""],
["equal",        2, 1, (operator_td, "=="), generic_comparisonfunction_sigs, ""],
["not_equal",    2, 1, (operator_td, "!="), generic_comparisonfunction_sigs, ""],
["greater",      2, 1, (operator_td, ">"),  generic_comparisonfunction_sigs, ""],
["greater_equal", 2, 1, (operator_td, ">="),  generic_comparisonfunction_sigs, ""],
["less",         2, 1, (operator_td, "<"),  generic_comparisonfunction_sigs, ""],
["less_equal",   2, 1, (operator_td, "<="), generic_comparisonfunction_sigs, ""],

["logical_and",  2, 1, (function_td, "logical_and"), generic_comparisonfunction_sigs, ""],
["logical_or",   2, 1, (function_td, "logical_or"), generic_comparisonfunction_sigs, ""],
["logical_xor",  2, 1, (function_td, "logical_xor"), generic_comparisonfunction_sigs, ""],
["logical_not",  1, 1, (operator_td, "!"),  generic_comparisonfunction_sigs, ""],
["bitwise_and",  2, 1, (operator_td, "&"),  generic_int_sigs, ""],
["bitwise_or",   2, 1, (operator_td, "|"),  generic_int_sigs, ""],
["bitwise_xor",  2, 1, (operator_td, "^"),  generic_int_sigs, ""],

["bitwise_not",  1, 1, (operator_td, "~"),  generic_int_sigs[1:], ""],
["bitwise_not",  1, 1, (function_td, "BOOLEAN_BITWISE_NOT"), generic_int_sigs[0:1], ""],

["rshift",  2, 1, (operator_td, ">>"),  generic_int_sigs, ""],
["lshift",  2, 1, (operator_td, "<<"),  generic_int_sigs, ""],

["floor", 1, 1, (function_td, ""), generic_int_sigs, ""],
["floor", 1, 1, (function_td, "floor"), generic_float_sigs, ""],
["ceil", 1, 1, (function_td, ""), generic_int_sigs, ""],
["ceil", 1, 1, (function_td, "ceil"), generic_float_sigs, ""],
["maximum", 2, 1, (function_td, "ufmaximum"), generic_operator_sigs, opt_minmax_decl],
["minimum", 2, 1, (function_td, "ufminimum"), generic_operator_sigs, opt_minmax_decl],

["fabs",    1, 1, (function_td, "fabs"),  generic_mathfunction_sigs, ""],
["_round",   1, 1, (function_td, "num_round"), generic_mathfunction_sigs, ""],
["hypot",   2, 1, (function_td, "hypot"), generic_mathfunction_sigs, ""],

# Complex ufuncs

["minus",    1, 1, (macro_td, "NUM_CMINUS"), complex_complex_sigs, ""],
["add",      2, 1, (macro_td, "NUM_CADD"), complex_complex_sigs, ""],
["subtract", 2, 1, (macro_td, "NUM_CSUB"), complex_complex_sigs, ""],
["multiply", 2, 1, (macro_td, "NUM_CMUL"),   complex_complex_sigs, ""],
["divide",   2, 1, (macro_td, "NUM_CDIV"),   complex_complex_sigs, ""],
["true_divide",   2, 1, (macro_td, "NUM_CDIV"),   complex_complex_sigs, ""],
["remainder", 2, 1, (macro_td, "NUM_CREM"), complex_complex_sigs, ""],
["power",  2, 1, (macro_td, "NUM_CPOW"),   complex_complex_sigs, ""],
["abs",    1, 1, (function_td, "NUM_CABS"), complex_real_sigs, ""],
["sin",    1, 1, (macro_td, "NUM_CSIN"),  complex_complex_sigs, ""],
["cos",    1, 1, (macro_td,    "NUM_CCOS"),  complex_complex_sigs, ""],
["tan",    1, 1, (macro_td, "NUM_CTAN"),  complex_complex_sigs, ""],

["arcsin", 1, 1, (macro_td, "NUM_CASIN"), complex_complex_sigs, ""],
["arccos", 1, 1, (macro_td, "NUM_CACOS"), complex_complex_sigs, ""],
["arctan", 1, 1, (macro_td, "NUM_CATAN"), complex_complex_sigs, ""],
["arcsinh", 1, 1, (macro_td, "NUM_CASINH"), complex_complex_sigs, ""],
["arccosh", 1, 1, (macro_td, "NUM_CACOSH"), complex_complex_sigs, ""],
["arctanh", 1, 1, (macro_td, "NUM_CATANH"), complex_complex_sigs, ""],

# ["arctan2",2, 1, (function_td, "atan2"), generic_mathfunction_sigs, ""],

["log",    1, 1, (macro_td, "NUM_CLOG"),  complex_complex_sigs, ""],
["log10",  1, 1, (macro_td, "NUM_CLOG10"), complex_complex_sigs, ""],
["exp",    1, 1, (macro_td, "NUM_CEXP"),  complex_complex_sigs, ""],
["sinh",   1, 1, (macro_td, "NUM_CSINH"), complex_complex_sigs, ""],
["cosh",   1, 1, (macro_td, "NUM_CCOSH"), complex_complex_sigs, ""],
["tanh",   1, 1, (macro_td, "NUM_CTANH"), complex_complex_sigs, ""],
["sqrt",   1, 1, (macro_td, "NUM_CSQRT"),  complex_complex_sigs, ""],

# Strictly speaking, these are implemented as macros as well, but they
# return real valued (Bool) expressions rather than fill in a 3rd
# Complex parameter.
["equal",        2, 1, (function_td, "NUM_CEQ"), complex_bool_sigs, ""],
["not_equal",    2, 1, (function_td, "NUM_CNE"), complex_bool_sigs, ""],

["logical_and",  2, 1, (function_td, "NUM_CLAND"), complex_bool_sigs, ""],
["logical_or",   2, 1, (function_td, "NUM_CLOR"), complex_bool_sigs, ""],
["logical_xor",  2, 1, (function_td, "NUM_CLXOR"), complex_bool_sigs, ""],
["logical_not",  1, 1, (function_td, "NUM_CLNOT"),  complex_bool_sigs, ""],
# ["bitwise_and",  2, 1, (operator_td, "&"),  generic_int_sigs, ""],
# ["bitwise_or",   2, 1, (operator_td, "|"),  generic_int_sigs, ""],
# ["bitwise_xor",  2, 1, (operator_td, "^"),  generic_int_sigs, ""],
# ["bitwise_not",  1, 1, (operator_td, "~"),  generic_int_sigs, ""],
# ["rshift",  2, 1, (operator_td, ">>"),  generic_int_sigs, ""],
# ["lshift",  2, 1, (operator_td, "<<"),  generic_int_sigs, ""],
["floor", 1, 1, (macro_td, "NUM_CFLOOR"), complex_complex_sigs, ""],
["ceil", 1, 1, (macro_td, "NUM_CCEIL"),   complex_complex_sigs, ""],
["maximum", 2, 1, (function_td, "NUM_CMAX"), complex_complex_sigs, ""],
["minimum", 2, 1, (function_td, "NUM_CMIN"), complex_complex_sigs, ""],
["fabs",      1, 1, (macro_td, "NUM_CFABS"),  complex_complex_sigs, ""],
["_round",    1, 1, (macro_td, "NUM_CROUND"), complex_complex_sigs, ""],
["hypot",     2, 1, (macro_td, "NUM_CHYPOT"), complex_complex_sigs, ""],
]

class UFuncParams:
    def __init__(self, pyname, ninputs, noutputs, form, sigs,
                 opt_declarations, template = None, vslist=None):
        self.pyname = pyname
        self.ninputs = ninputs
        self.noutputs = noutputs
        self.form = form
        self.sigs = sigs
        self.opt_decls = opt_declarations

        # Select appropriate base template form.
        self.template = template
        self.vslist = vslist
            
        # select appropriate computation form (operator vs function)
        # opdict = ufo.form[0].copy()
        self.operator = self.form[1]
        
        # for operator1, operator_sv, operator_vs, operator_vv...:
        #    Substitute actual operator/function for <operator>
        for key in self.form[0].keys():
          self.__dict__[key] = self.form[0][key] % self.__dict__ 
        
                
#*********************************************************************#
#                                                                     #
#                           PROGRAM SECTION                           #
#                                                                     #
#*********************************************************************#

"""
The classes below assemble the various elements defined in
the previous part of the module into the final C modules.

Each subclass of CodeGenerator works basically the same way:

It is constructed by supplying optional module unique header, tail,
and function separator.

When called, a CodeGenerator builds up two lists of strings by calling
its "bodygenerator()" method.  Looping over a parameter table,
the bodygenerator builds:

1. A code list containing the instantiated header and emitted function bodies.
This is the bulk of the code.

2. A function list containing the C struct initializers which are used to
register the function bodies with python.  These are necessarily in a
seperate textual segment so that they may be joined in an array and looped
over at initialization time.

In general, templates are instantiated by formatting them either using a
CodeGenerator instance dict, or a Params instance dict.

After generating its two lists, a CodeGenerator combines them into
the complete C code for a python module and writes the code out to
the specified file.

CodeGenerator subclasses customize behavior by overriding the "bodygenerator"
and "addcfunc" methods.  Each "bodygenerator" tends to be tailored to the
parameter table used to specify the cfuncs for the module.

Several of the codegenerators (cconv, ufunc, bytes) are supported by a
"Params" class, instances of which act as table records.  As the body
generator loops over the parameter table, it creates a Params instance
from each record.  The Params instance dictionary then provides a
source of keyword values to instantiate the code template.  See
ConvParams, ByteParams, and UfuncParams.  Each is essentially a
struct.

In general, template instantiation is performed by string substitution
using the python %(identifier)s syntax.  The %(indentifer)s syntax
permits a string to be formatted using a dictionary to supply identifier
values.
"""

class CodeGenerator:
    """Generates source code using supplied functions and code templates"""
    def __init__(self, header=_HEADER, tail=_TAIL, config=None,
                 separator=_TYPE_SEPARATOR):
        self.header = header
        self.tail = tail
        self.separator = separator
        self.config = config
        self.module_methods = ""

    def gen_header(self):
        self.codelist = [ self.header % self.__dict__]
        self.funclist = [ _CFUNCDICT_HEADER ]

    def gen_trailer(self):
        self.funclist.append(_CFUNCDICT_TAIL)
        self.codelist.append(_METHODS_DEF % self.__dict__)
        self.codelist.extend(self.funclist)
        self.codelist.append(self.tail % self.__dict__)

    def emit_code(self, file):
        sourcecode = "".join(self.codelist)
        if file:
            f = open(file,"w")
            f.write(sourcecode)
            f.close()
        else:
            return sourcecode
      
    def __call__(self, file=None, Type=None):
        self.gen_header()
        if Type is None:
            self.gen_body()
        else:
          self.gen_body(Type)
        self.gen_trailer()
        return self.emit_code(file)
            
    def addcfunc(self, name, key=None):
        if key is None:
            key = name
        self.funclist.append(_ADDCFUNC_TEMPLATE % (key, name))

    def addmethod(self, name, key=None):
        if key is None:
            key = name
        self.module_methods += (ADDMETHOD_TEMPLATE % (key, name))

class ConvCodeGenerator(CodeGenerator):
    def __init__(self, *components):
        CodeGenerator.__init__(self, *components)
        self.module = "_conv"
        
    def addcfunc(self, type, name):
        CodeGenerator.addcfunc(self, type+name, key=repr((type, name)))

    def gen_body(self):
        """Generates the repetitive sections of code for conversions"""

        # Iterate over the "from" datatype.
        for cfg1 in typeconfig:
            t1 = apply(ConvParams, cfg1)
            if t1.typename in ["Complex32","Complex64"]:
              frompy = COMPLEX_FROM_PyVALUE_TEMPLATE
              aspy = COMPLEX_AS_PyVALUE_TEMPLATE
            else:
              frompy = FROM_PyVALUE_TEMPLATE
              aspy = AS_PyVALUE_TEMPLATE
            self.codelist.append((self.separator + aspy + frompy) % \
                                 t1.__dict__)
            self.addcfunc(t1.typename, "asPyValue")
            self.addcfunc(t1.typename, "fromPyValue")
            
            # Iterate over the "to" datatype
            for cfg2 in typeconfig:
                t2 = apply(ConvParams, cfg2)
                if cfg1 == cfg2:
                    continue
                t1.desttypename = t2.typename
                t1.desttypecast = t2.typecast
                t1.numarray_to_numarray = numarray_to_numarray(t1.typename, t2.typename) % t1.__dict__
                self.codelist.append(CONVERSION_TEMPLATE % t1.__dict__)
                
                typetup = repr((t1.typename, t2.typename))
                name = t1.typename + "as" + t2.typename
                self.funclist.append(_ADDCFUNC_TEMPLATE % (typetup, name))

class SortCodeGenerator(CodeGenerator):
    def __init__(self, *components):
        CodeGenerator.__init__(self, *components)
        self.module = "_sort"

    def addcfunc(self, type, name):
        CodeGenerator.addcfunc(self, name+type, key=repr((type, name)))

    def gen_body(self):
        for cfg in typeconfig:
            t = apply(ConvParams, cfg)
            if t.typename in ["Complex32","Complex64"]:
              t.__dict__.update(sort_complex_td)
            else:
              t.__dict__.update(sort_operator_td)
            self.codelist.append((self.separator + SORT_TEMPLATE) % t.__dict__)
            self.addcfunc(t.typename, "sort")
            self.addcfunc(t.typename, "asort")
            self.addcfunc(t.typename, "searchsorted")
            self.addcfunc(t.typename, "fillarray")
            self.addcfunc(t.typename, "nonzeroCoords")

class BytesCodeGenerator(CodeGenerator):
    def __init__(self, *components):
        CodeGenerator.__init__(self, *components)
        self.module = "_bytes"

    def gen_body(self):
        for cfg in bytesconfig:
            t = apply(BytesParams, cfg)
            self.codelist.append((self.separator + BYTES_TEMPLATE) %
                                 t.__dict__)
            self.addcfunc("copy"+ t.sizename)
            self.addcfunc("byteswap"+t.sizename)
            self.addcfunc("align"+t.sizename)
            self.addcfunc("choose"+t.sizename)
            
        self.codelist.append((self.separator + CHOOSE_TEMPLATE) %
                             NBytesParams.__dict__)
        self.addcfunc("chooseNbytes")
              
        self.addcfunc("copyNbytes")
        self.addcfunc("putNbytes")
        self.addcfunc("takeNbytes")

        # Hack in the type based (not size based) methods for complex
        self.codelist.append((self.separator + BYTESWAP_TEMPLATE) %
                             Complex32BytesCfg.__dict__)
        self.addcfunc("byteswapComplex32" )
        self.codelist.append((self.separator + BYTESWAP_TEMPLATE) %
                             Complex64BytesCfg.__dict__)
        self.addcfunc("byteswapComplex64" )
        
        self.addmethod("copyToString")
            
class UFuncCodeGenerator(CodeGenerator):
    def __init__(self, *components):
        CodeGenerator.__init__(self, *components)

    def gen_body(self, Type):
        """Generates the repetitive sections of code."""
        self.module = "_ufunc" + Type

        # Loop over ufunc table
        for cfg in self.config:
            ufo = apply(UFuncParams, cfg)
            
            if self.separator is not None:
              self.codelist.append(self.separator % ufo.pyname)

            # Loop over signatures for given ufunc
            for sig in ufo.sigs:
              if ufo.ninputs + ufo.noutputs != len(sig):  # populate default signatures
                sig = (sig[0],)*ufo.ninputs + (sig[1],)*ufo.noutputs
              for i in range(ufo.ninputs):
                setattr(ufo, "intype%d" % i, sig[i]);
              for i in range(ufo.noutputs):
                setattr(ufo, "outtype%d" % i, sig[i+ufo.ninputs]);

              if ufo.intype0 != Type:
                continue

              if ufo.template is None:
                if ufo.ninputs == 1:
                  ufo.template = UFUNC_UNARY_TEMPLATE
                  ufo.vslist = ['vector']
                elif ufo.ninputs == 2:
                  if sig[0] == sig[-1]:
                    ufo.template = UFUNC_BINARY_TEMPLATE
                    ufo.vslist = ['vector_vector','vector_scalar','scalar_vector',
                                   'reduce', 'accumulate']
                  else:
                    ufo.template = UFUNC_COMPARE_TEMPLATE
                    ufo.vslist = ['vector_vector','vector_scalar','scalar_vector']
                else:
                  raise ValueError("Can't handle ufuncs with > 2 inputs")

              # Expand opt_decls in terms of ufo vars
              ufo.optional_declarations = ufo.opt_decls % ufo.__dict__

              # Expand template in terms of ufo vars
              self.codelist.append(ufo.template % ufo.__dict__)

              # for each form, add 
              for form in ufo.vslist:
                if form in ["accumulate","reduce"]:
                  sig2 = ((sig[0],),(sig[1],))
                else:
                  sig2 = (sig[:ufo.ninputs],sig[-ufo.noutputs:],)
                self.addcfunc(name=ufo.pyname+"_"+ufo.intype0+"_"+form,
                              key=repr((ufo.pyname, form, sig2)))

#*********************************************************************#

generate_conv_code  = ConvCodeGenerator()
generate_sort_code  = SortCodeGenerator(SORT_HEADER)
generate_bytes_code = BytesCodeGenerator(BYTES_HEADER)

generate_ufunc_code = UFuncCodeGenerator(
  UFUNC_HEADER, _TAIL, ufuncconfigs,
  "/*********************  %s  *********************/\n")

def _generate(Name, Source, Type=None):
    if Type is None:
        s = "generate%s_code('%s')" % (Name, Source)
    else:
        s = "generate%s_code('%s', '%s')" % (Name, Source, Type)
    eval(s)

#
# PseudoMake compares the modification date of Lib/codegenerator.py
# to the source code module implied by Name.  If the codegenerator is
# newer, the specified module is regenerated.
#
#
# This code is written to be executed from the parent directory of Lib,
# where setup.py resides.
#
def PseudoMake(Name, Type=None):
    if Type is None:
        Source = "Src/%smodule.c" % Name
    else:
        Source = "Src/%smodule.c" % (Name+Type)
    timemodgen = os.path.getmtime("Lib/codegenerator.py")
    # If codegenerator module more recent, regenerate Source
    if os.path.exists(Source):
        timemod = os.path.getmtime(Source)
    else:
        timemod = -1
    if timemodgen > timemod or "--check-dates" not in sys.argv:
        print "generating new version of", Source
        _generate(Name, Source, Type)

all_types = ["Bool", "Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32",
         "Float32", "Float64", "Complex32","Complex64"]
  
all_types.append("Int64")
if hasUInt64():
  all_types.append("UInt64")

def GenAll():
  PseudoMake("_conv")
  PseudoMake("_sort")
  PseudoMake("_bytes")
    
  for T in all_types:
    PseudoMake("_ufunc",T)

# =================================================================

class UfuncModule:

  """UfuncModule objects support the generation of numarray ufunc extension
  modules with methods and by maintaining the following state:

  _ufuncs   a list of ufunc configuration tuples.
  _code     a list of code snippets to insert into the module.
  
  """
  def __init__(self, module):
    self.module = module
    self._ufuncs = []  # Ufunc configurations
    self._code = []     # inline code

  def add_code(self, code):
    """inserts the specified c-code into the module."""
    self._code.append(code)

  def add_ufunc(self, ufunc_name, c_name, unary_binary=1,
                signatures="math", style="function"):
    """generates c-functions which handle a set of type signatures
for a single ufunc:

   ufunc_name    the name of the ufunc being defined.

   c_name        the name of the C-function or macro to be applied elementwise.

   unary_binary  1 -- unary ufunc  2 -- binary ufunc

   signatures    Specifies a set of input,output type pairs for which to generate code.
                 When a ufunc is called, type matching selects the "closest matching"
                 generated code to operate on the input types; mis-matches result in
                 blockwise type conversion to the nearest type.

                 There are pre-defined signature sets:

                      set                     ins        out

                    "float"    --         ('Float32', 'Float32')
                                          ('Float64', 'Float64')

                    "math"     --         ('Bool',    'Float64')
                                          ('Int8',    'Float64')
                                          ('Int16',   'Float64')
                                          ('Int32',   'Float64')
                                          ('UInt8',   'Float64')
                                          ('UInt16',  'Float64')
                                          ('UInt32',  'Float64')
                                          ('Float32', 'Float32')
                                          ('Float64', 'Float64')

                    "int"              -- ('Bool',    'Bool')
                                          ('Int8',    'Int8')
                                          ('Int16',   'Int16')
                                          ('Int32',   'Int32')
                                          ('UInt8',   'UInt8')
                                          ('UInt16',  'UInt16')
                                          ('UInt32',  'UInt32')
 
                    "complex_complex"  -- ('Complex32', 'Complex32')
                                       -- ('Complex64', 'Complex64')

                    "complex_real"     -- ('Complex32', 'Float32')
                                       -- ('Complex64', 'Float64')
                                        
                    "complex_bool"     -- ('Complex32', 'bool')
                                       -- ('Complex64', 'bool')
                                        
                 Alternately, a list of tuples of strings may be used:

                    [('Int32','Int32'), 'Float64','Float64')]

   style         "function" or "macro"

                   "function" -- c_func is a function of 1 or 2 parameters
                                 returning a single value.

                   "macro"    -- c_func is a macro of 2 or 3 parameters,
                                 returning nothing, where the last parameter
                                 accepts the output value.
"""
    template_dict_mapping = {
      "function":function_td,
      "macro":macro_td,
      }
    if style not in template_dict_mapping.keys():
      raise ValueError("Supported calling styles are:" +
                       str(template_dict_mapping.keys()))
    else:
      template_dict = template_dict_mapping[ style ]
    sigs_mapping = {
      "math"    : generic_mathfunction_sigs,
      "comparison" : generic_comparisonfunction_sigs,
      "int"     : generic_int_sigs,
      "float"   : generic_float_sigs,
      "complex_complex" : complex_complex_sigs,
      "complex_real" : complex_real_sigs,
      "complex_bool" : complex_bool_sigs,
      }
    if signatures not in sigs_mapping.keys():
      if (isinstance(signatures, list) and
          isinstance(signatures[0], tuple) and
          isinstance(signatures[0][0], type(""))):
        sigs = signatures
      else:
        raise ValueError("Supported signatures are:" + str(sigs_mapping.keys()))
    else:
      sigs = sigs_mapping[ signatures ]
    cfg = ( ufunc_name, unary_binary, 1, (template_dict, c_name), sigs, "" )
    self._ufuncs.append(cfg)

  def generate(self, filename):
    """emits the code for the entire module to the specified file."""
    gen = UFuncCodeGenerator(
      UFUNC_HEADER, _TAIL, self._ufuncs, None)
    # "/*********************  %s  *********************/\n")
    gen.gen_header()
    gen.codelist.extend(self._code)
    for t in all_types:
      gen.gen_body(t)
    gen.module = self.module
    gen.gen_trailer()
    gen.emit_code(filename)


def make_stub(module, c_module):
    """Generates Python code to define Ufuncs in 'module' corresponding to
    the Cfuncs in 'c_module'."""
    f = open(module+".py", "w")
    f.write("""import %s
import numarray.ufunc as _uf

globals().update(_uf.make_ufuncs(%s))

""" % (c_module, c_module))


# =================================================================

if __name__ == "__main__":
  GenAll()
