#include <stdio.h>
#include "wrapper.h"

extern void NGCALLF(wrf_vintrp,WRF_VINTRP)(double *, double *, double *, 
                                           double *, double *, double *, 
                                           double *, double *, double *, 
                                           double *, double *, int *, 
                                           int *, int *, int *, int *, 
                                           int *, int *, int *, double *);

extern void NGCALLF(wrf_monotonic,WRF_MONOTONIC)(double *, double *, double *, 
                                                 double *, int *, double *, 
                                                 int *, int *, int *, int *);


NhlErrorTypes wrf_vintrp_W( void )
{

/*
 * Input variables
 */
/*
 * Argument # 0
 */
  void *field;
  double *tmp_field;
  int       ndims_field;
  ng_size_t dsizes_field[NCL_MAX_DIMENSIONS];
  int has_missing_field;
  NclScalar missing_field, missing_flt_field, missing_dbl_field;
  NclBasicDataTypes type_field;

/*
 * Argument # 1
 */
  void *pres;
  double *tmp_pres;
  int       ndims_pres;
  ng_size_t dsizes_pres[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_pres;

/*
 * Argument # 2
 */
  void *tk;
  double *tmp_tk;
  int       ndims_tk;
  ng_size_t dsizes_tk[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_tk;

/*
 * Argument # 3
 */
  void *qvp;
  double *tmp_qvp;
  int       ndims_qvp;
  ng_size_t dsizes_qvp[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_qvp;

/*
 * Argument # 4
 */
  void *ght;
  double *tmp_ght;
  int       ndims_ght;
  ng_size_t dsizes_ght[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_ght;


/*
* Argument # 5
*/
void *ter;
double *tmp_ter;
int       ndims_ter;
ng_size_t dsizes_ter[NCL_MAX_DIMENSIONS];
NclBasicDataTypes type_ter;

/*
 * Argument # 6
 */
  void *sfp;
  double *tmp_sfp;
  int       ndims_sfp;
  ng_size_t dsizes_sfp[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_sfp;

/*
 * Argument # 7
 */
  void *smsfp;
  double *tmp_smsfp;
  int       ndims_smsfp;
  ng_size_t dsizes_smsfp[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_smsfp;

/*
 * Argument # 8
 */
  void *vcarray;
  double *tmp_vcarray;
  int       ndims_vcarray;
  ng_size_t dsizes_vcarray[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_vcarray;

/*
 * Argument # 9
 */
  void *intrp_levels;
  double *tmp_intrp_levels;
  ng_size_t dsizes_intrp_levels[1];
  NclBasicDataTypes type_intrp_levels;

/*
 * Arguments # 10-13
 */
  int *icase, *extrap, *vcor, *logp;

/*
 * Return variable
 */
  void *field_out;
  double *tmp_field_out;
  ng_size_t *dsizes_field_out;
  NclScalar missing_field_out;
  NclBasicDataTypes type_field_out;
  
/*
 * Various
 */
  ng_size_t ninlev, nlat, nlon, ninlevlatlon, nlatlon;
  ng_size_t noutlev, noutlevlatlon;
  ng_size_t index_field, index_sfp, index_field_out;
  ng_size_t i, size_leftmost, size_output;
  int ininlev, inlat, inlon, inoutlev, ret;

/*
 * Retrieve parameters.
 *
 * Note any of the pointer parameters can be set to NULL, which
 * implies you don't care about its value.
 */
/*
 * Get argument # 0
 */
  field = (void*)NclGetArgValue(
           0,
           14,
           &ndims_field,
           dsizes_field,
           &missing_field,
           &has_missing_field,
           &type_field,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_field < 3 || ndims_field > 4) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The field array must be 3D or 4D");
    return(NhlFATAL);
  }

/*
 * Coerce missing value to double if necessary.
 */
  coerce_missing(type_field,has_missing_field,&missing_field,
                 &missing_dbl_field,&missing_flt_field);

  ninlev = dsizes_field[ndims_field-3];
  nlat   = dsizes_field[ndims_field-2];
  nlon   = dsizes_field[ndims_field-1];

/*
 * Test dimension sizes.
 */
  if(ninlev > INT_MAX || nlat > INT_MAX || nlon > INT_MAX) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: one of bottom_top, south_north, or west_east is greater than INT_MAX");
    return(NhlFATAL);
  }
  ininlev = (int) ninlev;
  inlat   = (int) nlat;
  inlon   = (int) nlon;

/*
 * Get argument # 1
 */
  pres = (void*)NclGetArgValue(
           1,
           14,
           &ndims_pres,
           dsizes_pres,
           NULL,
           NULL,
           &type_pres,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_pres != ndims_field) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The pres and field arrays must have the same dimensionality");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_field; i++) {
      if(dsizes_pres[i] != dsizes_field[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The pres and field arrays must have the same dimensionality");
        return(NhlFATAL);
      }
    }
  }

/*
 * Get argument # 2
 */
  tk = (void*)NclGetArgValue(
           2,
           14,
           &ndims_tk,
           dsizes_tk,
           NULL,
           NULL,
           &type_tk,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_tk != ndims_field) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The tk and field arrays must have the same dimensionality");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_field; i++) {
      if(dsizes_tk[i] != dsizes_field[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The tk and field arrays must have the same dimensionality");
        return(NhlFATAL);
      }
    }
  }


/*
 * Get argument # 3
 */
  qvp = (void*)NclGetArgValue(
           3,
           14,
           &ndims_qvp,
           dsizes_qvp,
           NULL,
           NULL,
           &type_qvp,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_qvp != ndims_field) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The qvp and field arrays must have the same dimensionality");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_field; i++) {
      if(dsizes_qvp[i] != dsizes_field[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The qvp and field arrays must have the same dimensionality");
        return(NhlFATAL);
      }
    }
  }

/*
 * Get argument # 4
 */
  ght = (void*)NclGetArgValue(
           4,
           14,
           &ndims_ght,
           dsizes_ght,
           NULL,
           NULL,
           &type_ght,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_ght != ndims_field) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The ght and field arrays must have the same dimensionality");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_field; i++) {
      if(dsizes_ght[i] != dsizes_field[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The ght and field arrays must have the same dimensionality");
        return(NhlFATAL);
      }
    }
  }

/*
 * Get argument # 5
 */
  ter = (void*)NclGetArgValue(
           5,
           14,
		   &ndims_ter,
           dsizes_ter,
           NULL,
           NULL,
           &type_ter,
           DONT_CARE);
/*
 * Check dimension sizes for ter.  It can either be 2D, or one fewer
 * dimensions than field.
 */
  if(dsizes_ter[ndims_ter-2] != nlat || dsizes_ter[ndims_ter-1] != nlon) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The dimensions of ter must be south_north x west_east");
    return(NhlFATAL);
  }

/*
 * Get argument # 6
 */
  sfp = (void*)NclGetArgValue(
           6,
           14,
           &ndims_sfp,
           dsizes_sfp,
           NULL,
           NULL,
           &type_sfp,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_sfp != (ndims_field-1)) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The sfp array must have the same dimensionality as the field array, minus the level dimension");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_field-3; i++) {
      if(dsizes_sfp[i] != dsizes_field[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The sfp array must have the same dimensionality as the field array, minus the level dimension");
        return(NhlFATAL);
      }
    }
    if(dsizes_sfp[ndims_sfp-2] != nlat || dsizes_sfp[ndims_sfp-1] != nlon) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The sfp array must have the same dimensionality as the field array, minus the level dimension");
      return(NhlFATAL);
    }
  }


/*
 * Get argument # 7
 */
  smsfp = (void*)NclGetArgValue(
           7,
           14,
           &ndims_smsfp,
           dsizes_smsfp,
           NULL,
           NULL,
           &type_smsfp,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_smsfp != (ndims_field-1)) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The smsfp array must have the same dimensionality as the field array, minus the level dimension");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_field-3; i++) {
      if(dsizes_smsfp[i] != dsizes_field[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The smsfp array must have the same dimensionality as the field array, minus the level dimension");
        return(NhlFATAL);
      }
    }
    if(dsizes_smsfp[ndims_smsfp-2] != nlat || 
       dsizes_smsfp[ndims_smsfp-1] != nlon) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The smsfp array must have the same dimensionality as the field array, minus the level dimension");
      return(NhlFATAL);
    }
  }


/*
 * Get argument # 8
 */
  vcarray = (void*)NclGetArgValue(
           8,
           14,
           &ndims_vcarray,
           dsizes_vcarray,
           NULL,
           NULL,
           &type_vcarray,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_vcarray != ndims_field) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The vcarray and field arrays must have the same dimensionality");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_field; i++) {
      if(dsizes_vcarray[i] != dsizes_field[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The vcarray and field arrays must have the same dimensionality");
        return(NhlFATAL);
      }
    }
  }

/*
 * Get argument # 9
 */
  intrp_levels = (void*)NclGetArgValue(
           9,
           14,
           NULL,
           dsizes_intrp_levels,
           NULL,
           NULL,
           &type_intrp_levels,
           DONT_CARE);

  noutlev = dsizes_intrp_levels[0];

/*
 * Test dimension sizes.
 */
  if(noutlev > INT_MAX) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: the # of output levels is greater than INT_MAX");
    return(NhlFATAL);
  }
  inoutlev = (int) noutlev;


/*
 * Get argument # 10
 */
  icase = (int*)NclGetArgValue(
           10,
           14,
           NULL,
           NULL,
           NULL,
           NULL,
           NULL,
           DONT_CARE);
/*
 * Get argument # 11
 */
  extrap = (int*)NclGetArgValue(
           11,
           14,
           NULL,
           NULL,
           NULL,
           NULL,
           NULL,
           DONT_CARE);
/*
 * Get argument # 12
 */
  vcor = (int*)NclGetArgValue(
           12,
           14,
           NULL,
           NULL,
           NULL,
           NULL,
           NULL,
           DONT_CARE);
/*
 * Get argument # 13
 */
  logp = (int*)NclGetArgValue(
           13,
           14,
           NULL,
           NULL,
           NULL,
           NULL,
           NULL,
           DONT_CARE);

/*
 * Calculate size of leftmost dimensions.
 */
  size_leftmost  = 1;
  for(i = 0; i < ndims_field-3; i++) size_leftmost *= dsizes_field[i];

/* 
 * Allocate space for coercing input arrays.  If any of the input
 * is already double, then we don't need to allocate space for
 * temporary arrays, because we'll just change the pointer into
 * the void array appropriately.
 */
/*
 * Allocate space for tmp_field.
 */
  nlatlon       = nlat * nlon;
  ninlevlatlon  = ninlev * nlatlon;
  noutlevlatlon = noutlev * nlatlon;

  if(type_field != NCL_double) {
    tmp_field = (double *)calloc(ninlevlatlon,sizeof(double));
    if(tmp_field == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing field array to double");
      return(NhlFATAL);
    }
  }

/*
 * Allocate space for tmp_pres.
 */
  if(type_pres != NCL_double) {
    tmp_pres = (double *)calloc(ninlevlatlon,sizeof(double));
    if(tmp_pres == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing pressure array to double");
      return(NhlFATAL);
    }
  }

/*
 * Allocate space for tmp_tk.
 */
  if(type_tk != NCL_double) {
    tmp_tk = (double *)calloc(ninlevlatlon,sizeof(double));
    if(tmp_tk == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing tk array to double");
      return(NhlFATAL);
    }
  }

/*
 * Allocate space for tmp_qvp.
 */
  if(type_qvp != NCL_double) {
    tmp_qvp = (double *)calloc(ninlevlatlon,sizeof(double));
    if(tmp_qvp == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing qvp array to double");
      return(NhlFATAL);
    }
  }

/*
 * Allocate space for tmp_ght.
 */
  if(type_ght != NCL_double) {
    tmp_ght = (double *)calloc(ninlevlatlon,sizeof(double));
    if(tmp_ght == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing ght array to double");
      return(NhlFATAL);
    }
  }

  /*
   * Allocate space for tmp_ter.
   */
    if(type_ter != NCL_double) {
      tmp_ter = (double *)calloc(nlatlon,sizeof(double));
      if(tmp_ter == NULL) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing ter array to double");
        return(NhlFATAL);
      }
    }

/*
 * Allocate space for tmp_sfp.
 */
  if(type_sfp != NCL_double) {
    tmp_sfp = (double *)calloc(nlatlon,sizeof(double));
    if(tmp_sfp == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing sfp array to double");
      return(NhlFATAL);
    }
  }

/*
 * Allocate space for tmp_smsfp.
 */
  if(type_smsfp != NCL_double) {
    tmp_smsfp = (double *)calloc(nlatlon,sizeof(double));
    if(tmp_smsfp == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing smsfp array to double");
      return(NhlFATAL);
    }
  }

/*
 * Allocate space for tmp_vcarray.
 */
  if(type_vcarray != NCL_double) {
    tmp_vcarray = (double *)calloc(ninlevlatlon,sizeof(double));
    if(tmp_vcarray == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing vcarray to double");
      return(NhlFATAL);
    }
  }
/*
 * Coerce intrp_levels to double, if necessary.
 */
  tmp_intrp_levels = coerce_input_double(intrp_levels,type_intrp_levels,
                                         noutlev,0,NULL,NULL);
  if(tmp_intrp_levels == NULL) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for coercing interp_levels array to double");
    return(NhlFATAL);
  }

/*
 * The output type defaults to float, unless one or more input 
 * arrays are double.
 */
  if(type_field   == NCL_double || type_pres  == NCL_double || 
     type_tk      == NCL_double || type_qvp   == NCL_double || 
     type_ght     == NCL_double || type_ter   == NCL_double || 
     type_sfp     == NCL_double || type_smsfp == NCL_double || 
     type_vcarray == NCL_double) {
    type_field_out = NCL_double;
  }
  else {
    type_field_out = NCL_float;
  }

/* 
 * Allocate space for output array and set a missing value.
 * Note: a missing value is returned even if the input doesn't
 * have a missing value, because the output may contain missing 
 * values after interpolation is done.
 */
  size_output = size_leftmost * noutlevlatlon;
  if(type_field_out != NCL_double) {
    field_out = (void *)calloc(size_output, sizeof(float));
    tmp_field_out = (double *)calloc(noutlevlatlon,sizeof(double));
    if(field_out == NULL || tmp_field_out == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for temporary output array");
      return(NhlFATAL);
    }
    missing_field_out = missing_flt_field;
  }
  else {
    field_out = (void *)calloc(size_output, sizeof(double));
    if(field_out == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for output array");
      return(NhlFATAL);
    }
    missing_field_out = missing_dbl_field;
  }

/* 
 * Allocate space for output dimension sizes and set them.
 */
  dsizes_field_out = (ng_size_t*)calloc(ndims_field,sizeof(ng_size_t));  
  if( dsizes_field_out == NULL ) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: Unable to allocate memory for holding dimension sizes");
    return(NhlFATAL);
  }
  for(i = 0; i < ndims_field-3; i++) dsizes_field_out[i] = dsizes_field[i];
  dsizes_field_out[ndims_field-3] = noutlev;
  dsizes_field_out[ndims_field-2] = nlat;
  dsizes_field_out[ndims_field-1] = nlon;

/*
 * Loop across leftmost dimensions and call the Fortran routine for each
 * subsection of the input arrays.
 */
  index_field = index_sfp = index_field_out = 0;

  for(i = 0; i < size_leftmost; i++) {
/*
 * Coerce subsection of field (tmp_field) to double if necessary.
 */
    if(type_field != NCL_double) {
      coerce_subset_input_double(field,tmp_field,index_field,type_field,
                                 ninlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_field = &((double*)field)[index_field];
    }

/*
 * Coerce subsection of pres (tmp_pres) to double if necessary.
 */
    if(type_pres != NCL_double) {
      coerce_subset_input_double(pres,tmp_pres,index_field,type_pres,ninlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_pres = &((double*)pres)[index_field];
    }

/*
 * Coerce subsection of tk (tmp_tk) to double if necessary.
 */
    if(type_tk != NCL_double) {
      coerce_subset_input_double(tk,tmp_tk,index_field,type_tk,
                                 ninlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_tk = &((double*)tk)[index_field];
    }

/*
 * Coerce subsection of qvp (tmp_qvp) to double if necessary.
 */
    if(type_qvp != NCL_double) {
      coerce_subset_input_double(qvp,tmp_qvp,index_field,type_qvp,
                                 ninlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_qvp = &((double*)qvp)[index_field];
    }

/*
 * Coerce subsection of ght (tmp_ght) to double if necessary.
 */
    if(type_ght != NCL_double) {
      coerce_subset_input_double(ght,tmp_ght,index_field,type_ght,
                                 ninlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_ght = &((double*)ght)[index_field];
    }

/*
 * Coerce subsection of ter (tmp_ter) to double if necessary.
 */
	if(type_ter != NCL_double) {
	  coerce_subset_input_double(ter,tmp_ter,index_sfp,type_ter,
								 nlatlon,0,NULL,NULL);
	}
	else {
	  tmp_ter = &((double*)ter)[index_sfp];
	}

/*
 * Coerce subsection of sfp (tmp_sfp) to double if necessary.
 */
    if(type_sfp != NCL_double) {
      coerce_subset_input_double(sfp,tmp_sfp,index_sfp,type_sfp,
                                 nlatlon,0,NULL,NULL);
    }
    else {
      tmp_sfp = &((double*)sfp)[index_sfp];
    }

/*
 * Coerce subsection of smsfp (tmp_smsfp) to double if necessary.
 */
    if(type_smsfp != NCL_double) {
      coerce_subset_input_double(smsfp,tmp_smsfp,index_sfp,type_smsfp,
                                 nlatlon,0,NULL,NULL);
    }
    else {
      tmp_smsfp = &((double*)smsfp)[index_sfp];
    }

/*
 * Coerce subsection of vcarray (tmp_vcarray) to double if necessary.
 */
    if(type_vcarray != NCL_double) {
      coerce_subset_input_double(vcarray,tmp_vcarray,index_field,type_vcarray,
                                 ninlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_vcarray = &((double*)vcarray)[index_field];
    }


/*
 * Point temporary output array to void output array if appropriate.
 */
    if(type_field_out == NCL_double) {
      tmp_field_out = &((double*)field_out)[index_field_out];
    }
    
/*
 * Call the Fortran routine.
 */
    NGCALLF(wrf_vintrp,WRF_VINTRP)(tmp_field, tmp_field_out, tmp_pres,
                                   tmp_tk, tmp_qvp, tmp_ght, tmp_ter, 
                                   tmp_sfp, tmp_smsfp, tmp_vcarray, 
                                   tmp_intrp_levels, &inoutlev, icase, 
                                   &inlon, &inlat, &ininlev, extrap, vcor, 
                                   logp, &missing_dbl_field.doubleval);

/*
 * Coerce output back to float if necessary.
 */
    if(type_field_out == NCL_float) {
      coerce_output_float_only(field_out,tmp_field_out,noutlevlatlon,
                               index_field_out);
    }
    index_field     += ninlevlatlon;
    index_field_out += noutlevlatlon;
    index_sfp       += nlatlon;
  }

/*
 * Free unneeded memory.
 */

  if(type_field        != NCL_double) NclFree(tmp_field);
  if(type_pres         != NCL_double) NclFree(tmp_pres);
  if(type_tk           != NCL_double) NclFree(tmp_tk);
  if(type_qvp          != NCL_double) NclFree(tmp_qvp);
  if(type_ght          != NCL_double) NclFree(tmp_ght);
  if(type_ter          != NCL_double) NclFree(tmp_ter);
  if(type_sfp          != NCL_double) NclFree(tmp_sfp);
  if(type_smsfp        != NCL_double) NclFree(tmp_smsfp);
  if(type_vcarray      != NCL_double) NclFree(tmp_vcarray);
  if(type_intrp_levels != NCL_double) NclFree(tmp_intrp_levels);
  if(type_field_out    != NCL_double) NclFree(tmp_field_out);

/*
 * Return value back to NCL script.
 */
  ret = NclReturnValue(field_out,ndims_field,dsizes_field_out,
                       &missing_field_out,type_field_out,0);

  NclFree(dsizes_field_out);
  return(ret);
}

NhlErrorTypes wrf_monotonic_W( void )
{

/*
 * Input variables
 */
/*
 * Argument # 0
 */
  void *x;
  double *tmp_x;
  int       ndims_x;
  ng_size_t dsizes_x[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_x;

/*
 * Argument # 1
 */
  void *pres;
  double *tmp_pres;
  int       ndims_pres;
  ng_size_t dsizes_pres[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_pres;

/*
 * Argument # 2
 */
  void *cor;
  double *tmp_cor;
  /*ng_size_t dsizes_cor[2];*/
  int       ndims_cor;
  ng_size_t dsizes_cor[NCL_MAX_DIMENSIONS];
  NclBasicDataTypes type_cor;

/*
 * Argument # 3
 */
  int *idir;
/*
 * Argument # 4
 */
  void *delta;
  double *tmp_delta;
  NclBasicDataTypes type_delta;

/*
 * Argument # 5
 */
  int *icorsw;
/*
 * Return variable
 */
  void *xout;
  double *tmp_xout;
  NclBasicDataTypes type_xout;


/*
 * Various
 */
  ng_size_t nlev, nlat, nlon, nlevlatlon, nlatlon;
  ng_size_t i, index_x, index_cor, size_leftmost, size_output;
  int inlev, inlat, inlon, ret;
/*
 * Retrieve parameters.
 *
 * Note any of the pointer parameters can be set to NULL, which
 * implies you don't care about its value.
 */
/*
 * Get argument # 0
 */
  x = (void*)NclGetArgValue(
           0,
           6,
           &ndims_x,
           dsizes_x,
           NULL,
           NULL,
           &type_x,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_x < 3) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: The x array must have at least three dimensions");
    return(NhlFATAL);
  }
  nlev = dsizes_x[ndims_x-3];
  nlat = dsizes_x[ndims_x-2];
  nlon = dsizes_x[ndims_x-1];

/*
 * Test dimension sizes.
 */
  if(nlev > INT_MAX || nlat > INT_MAX || nlon > INT_MAX) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: one of bottom_top, south_north, or west_east is greater than INT_MAX");
    return(NhlFATAL);
  }
  inlev = (int) nlev;
  inlat = (int) nlat;
  inlon = (int) nlon;


/*
 * Get argument # 1
 */
  pres = (void*)NclGetArgValue(
           1,
           6,
           &ndims_pres,
           dsizes_pres,
           NULL,
           NULL,
           &type_pres,
           DONT_CARE);

/*
 * Check dimension sizes.
 */
  if(ndims_pres != ndims_x) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: The pres and x arrays must have the same dimensionality");
    return(NhlFATAL);
  }
  else {
    for(i = 0; i < ndims_x; i++) {
      if(dsizes_pres[i] != dsizes_x[i]) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: The pres and x arrays must have the same dimensionality");
        return(NhlFATAL);
      }
    }
  }

/*
 * Get argument # 2
 */
  cor = (void*)NclGetArgValue(
           2,
           6,
		   &ndims_cor,
           dsizes_cor,
           NULL,
           NULL,
           &type_cor,
           DONT_CARE);

  if(dsizes_cor[ndims_cor-2] != nlat || dsizes_cor[ndims_cor-1] != nlon) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_vintrp: The dimensions of cor must be south_north x west_east");
    return(NhlFATAL);
  }

/*
 * Get argument # 3
 */
  idir = (int*)NclGetArgValue(
           3,
           6,
           NULL,
           NULL,
           NULL,
           NULL,
           NULL,
           DONT_CARE);
/*
 * Get argument # 4
 */
  delta = (void*)NclGetArgValue(
           4,
           6,
           NULL,
           NULL,
           NULL,
           NULL,
           &type_delta,
           DONT_CARE);
/*
 * Get argument # 5
 */
  icorsw = (int*)NclGetArgValue(
           5,
           6,
           NULL,
           NULL,
           NULL,
           NULL,
           NULL,
           DONT_CARE);

/*
 * Calculate size of leftmost dimensions.
 */
  size_leftmost  = 1;
  for(i = 0; i < ndims_x-3; i++) size_leftmost *= dsizes_x[i];

/* 
 * Allocate space for coercing input arrays.  If any of the input
 * is already double, then we don't need to allocate space for
 * temporary arrays, because we'll just change the pointer into
 * the void array appropriately.
 */

/*
 * Allocate space for tmp_x.
 */
  nlatlon     = nlat * nlon;
  nlevlatlon = nlev * nlatlon;

  if(type_x != NCL_double) {
    type_xout = NCL_float;
    tmp_x = (double *)calloc(nlevlatlon,sizeof(double));
    if(tmp_x == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: Unable to allocate memory for coercing input array to double");
      return(NhlFATAL);
    }
  }
  else {
    type_xout = NCL_double;
  }
/*
 * Allocate space for tmp_pres.
 */
  if(type_pres != NCL_double) {
    tmp_pres = (double *)calloc(nlevlatlon,sizeof(double));
    if(tmp_pres == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: Unable to allocate memory for coercing input array to double");
      return(NhlFATAL);
    }
  }
/*
 * Allocate space for tmp_cor
 */
  if(type_cor != NCL_double) {
      tmp_cor = (double *)calloc(nlatlon,sizeof(double));
      if(tmp_cor == NULL) {
        NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: Unable to allocate memory for coercing input array to double");
        return(NhlFATAL);
      }
    }


/*
 * Allocate space for tmp_delta.
 */
  tmp_delta = coerce_input_double(delta,type_delta,1,0,NULL,NULL);
  if(tmp_delta == NULL) {
    NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: Unable to allocate memory for coercing input array to double");
    return(NhlFATAL);
  }

/*
 * Calculate size of output array, which is same as x.
 */
  size_output = size_leftmost * nlevlatlon;

/* 
 * Allocate space for output array.
 */
  if(type_xout != NCL_double) {
    xout = (void *)calloc(size_output, sizeof(float));
    tmp_xout = (double *)calloc(nlevlatlon,sizeof(double));
    if(xout == NULL || tmp_xout == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: Unable to allocate memory for temporary output array");
      return(NhlFATAL);
    }
  }
  else {
    xout = (void *)calloc(size_output, sizeof(double));
    if(xout == NULL) {
      NhlPError(NhlFATAL,NhlEUNKNOWN,"wrf_monotonic: Unable to allocate memory for output array");
      return(NhlFATAL);
    }
  }

/*
 * Loop across leftmost dimensions and call the Fortran routine for each
 * subsection of the input arrays.
 */
  index_x = 0;
  index_cor = 0;
  for(i = 0; i < size_leftmost; i++) {
/*
 * Coerce subsection of x (tmp_x) to double if necessary.
 */
    if(type_x != NCL_double) {
      coerce_subset_input_double(x,tmp_x,index_x,type_x,nlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_x = &((double*)x)[index_x];
    }

/*
 * Coerce subsection of pres (tmp_pres) to double if necessary.
 */
    if(type_pres != NCL_double) {
      coerce_subset_input_double(pres,tmp_pres,index_x,type_pres,nlevlatlon,0,NULL,NULL);
    }
    else {
      tmp_pres = &((double*)pres)[index_x];
    }

/*
 * Coerce subsection of cor (tmp_cor) to double if necessary.
 */
	if(type_pres != NCL_double) {
	  coerce_subset_input_double(cor,tmp_cor,index_cor,type_cor,nlatlon,0,NULL,NULL);
	}
	else {
	  tmp_cor = &((double*)cor)[index_cor];
	}

/*
 * Point temporary output array to void output array if appropriate.
 */
    if(type_xout == NCL_double) tmp_xout = &((double*)xout)[index_x];

/*
 * Call the Fortran routine.
 */
    NGCALLF(wrf_monotonic,WRF_MONOTONIC)(tmp_xout, tmp_x, tmp_pres, 
                                         tmp_cor, idir, tmp_delta, &inlon,
                                         &inlat, &inlev, icorsw);
/*
 * Coerce output back to float if necessary.
 */
    if(type_xout == NCL_float) {
      coerce_output_float_only(xout,tmp_xout,nlevlatlon,index_x);
    }
    index_x += nlevlatlon;
    index_cor += nlatlon;
  }

/*
 * Free unneeded memory.
 */
  if(type_x     != NCL_double) NclFree(tmp_x);
  if(type_pres  != NCL_double) NclFree(tmp_pres);
  if(type_cor   != NCL_double) NclFree(tmp_cor);
  if(type_delta != NCL_double) NclFree(tmp_delta);
  if(type_xout  != NCL_double) NclFree(tmp_xout);

/*
 * Return value back to NCL script.
 */
  ret = NclReturnValue(xout,ndims_x,dsizes_x,NULL,type_xout,0);
  return(ret);
}
